コード例 #1
0
        private void DrawArrow(D2dGraphics g, D2dBrush brush, PointF p, float dx, float dy)
        {
            const float cos  = 0.866f;
            const float sin  = 0.500f;
            PointF      end1 = new PointF(
                (float)(p.X + (dx * cos + dy * -sin)),
                (float)(p.Y + (dx * sin + dy * cos)));
            PointF end2 = new PointF(
                (float)(p.X + (dx * cos + dy * sin)),
                (float)(p.Y + (dx * -sin + dy * cos)));

            g.DrawLine(p, end1, brush, m_theme.StrokeWidth);
            g.DrawLine(p, end2, brush, m_theme.StrokeWidth);
        }
コード例 #2
0
ファイル: D2dUtil.cs プロジェクト: blue3k/ATFClone
        /// <summary>
        /// Draws grid lines corresponding to a vertical scale</summary>
        /// <param name="g">The Direct2D graphics object</param>
        /// <param name="transform">Graph (world) to window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="majorSpacing">Scale's major spacing</param>
        /// <param name="lineBrush">Grid line brush</param>
        public static void DrawVerticalScaleGrid(
            this D2dGraphics g,
            Matrix transform,
            RectangleF graphRect,
            int majorSpacing,
            D2dBrush lineBrush)
        {
            double     xScale     = transform.Elements[0];
            RectangleF clientRect = Transform(transform, graphRect);

            double min        = Math.Min(graphRect.Left, graphRect.Right);
            double max        = Math.Max(graphRect.Left, graphRect.Right);
            double tickAnchor = CalculateTickAnchor(min, max);
            double step       = CalculateStep(min, max, Math.Abs(clientRect.Right - clientRect.Left), majorSpacing, 0.0);

            if (step > 0)
            {
                double offset = tickAnchor - min;
                offset = offset - MathUtil.Remainder(offset, step) + step;
                for (double x = tickAnchor - offset; x <= max; x += step)
                {
                    double cx = (x - graphRect.Left) * xScale + clientRect.Left;
                    g.DrawLine((float)cx, clientRect.Top, (float)cx, clientRect.Bottom, lineBrush);
                }
            }
        }
コード例 #3
0
ファイル: D2dSnapManipulator.cs プロジェクト: zparr/ATF
        /// <summary>
        /// Draws the snap-to indicator to give the user a visual cue that snapping is occurring</summary>
        /// <param name="sender">The TimelineControl whose Paint event is being raised</param>
        /// <param name="e">The paint event args</param>
        /// <remarks>Draws a vertical line at the snapping location</remarks>
        private void owner_DrawingD2d(object sender, EventArgs e)
        {
            if (m_snapInfo.Count == 0)
            {
                return;
            }

            D2dGraphics g             = m_owner.D2dGraphics;
            Rectangle   clipRectangle = m_owner.VisibleClientRectangle;

            try
            {
                g.PushAxisAlignedClip(clipRectangle);
                Matrix worldToView = m_owner.Transform;

                foreach (SnapOffsetInfo info in m_snapInfo)
                {
                    float viewXCoord = GdiUtil.Transform(worldToView, info.SnapToPoint);
                    g.DrawLine(viewXCoord, clipRectangle.Top, viewXCoord, clipRectangle.Bottom, s_color, 3.0f, null);
                }
            }
            finally
            {
                g.PopAxisAlignedClip();
            }
        }
コード例 #4
0
        private void DrawArrow(Vec2F p, Vec2F d, D2dBrush brush, D2dGraphics g)
        {
            d.Normalize();
            // draw arrowhead
            const double cos  = -0.866;
            const double sin  = -0.500;
            PointF       head = new PointF(p.X, p.Y);
            PointF       end1 = new PointF(
                (float)(p.X + (d.X * cos + d.Y * -sin) * m_arrowSize),
                (float)(p.Y + (d.X * sin + d.Y * cos) * m_arrowSize));
            PointF end2 = new PointF(
                (float)(p.X + (d.X * cos + d.Y * sin) * m_arrowSize),
                (float)(p.Y + (d.X * -sin + d.Y * cos) * m_arrowSize));


            g.DrawLine(head, end1, brush, m_theme.StrokeWidth);
            g.DrawLine(head, end2, brush, m_theme.StrokeWidth);
        }
コード例 #5
0
ファイル: D2dUtil.cs プロジェクト: blue3k/ATFClone
        /// <summary>
        /// Draws a tree-control style expander, which looks like a square with
        /// a dash (expanded) or a cross (unexpanded).</summary>
        /// <param name="x">X coordinate of expander top right corner</param>
        /// <param name="y">Y coordinate of expander top right corner</param>
        /// <param name="pen">Pen, should be 1 pixel wide</param>
        /// <param name="size">Size of pin, in pixels</param>
        /// <param name="pinned">Whether or not expander should appear "expanded"</param>
        /// <param name="g">Graphics object</param>
        public static void DrawRightPin(int x, int y, int size, D2dBrush pen, bool pinned, D2dGraphics g)
        {
            //g.DrawRectangle(pen, x - size, y, size, size);

            int rectWidth  = size / 4;
            int rectHeight = 2 * size / 3;
            int center     = size / 2;

            if (pinned)
            {
                g.DrawLine(x + center - size, y + rectHeight, x + center - size, y + size, pen); //lower center-vertical line
                g.DrawLine(x - size, y + rectHeight, x, y + rectHeight, pen);                    // middle-horizontal line
                g.DrawRectangle(new RectangleF(x + rectWidth - size, y, 2 * rectWidth, rectHeight), pen);
                //g.DrawLine(pen, x + 3 * rectWidth - 1, y, x + 3 * rectWidth - 1, y + rectHeight); // a vertial line next to the right side of the rect
                g.DrawLine(x + 3 * rectWidth - 1 - size, y,
                           x + 3 * rectWidth - 1 - size, y + rectHeight, pen);     // a vertial line next to the right side of the rect
            }
            else
            {
                g.DrawLine(x, y + center, x - size + rectHeight, y + center, pen);                                                                                //left center-horizontal line
                g.DrawLine(x - size + rectHeight, y, x - size + rectHeight, y + size, pen);                                                                       // middle-vertical line
                g.DrawRectangle(new RectangleF(x - size, y + (size - rectWidth) / 2 - 1, rectHeight, 2 * rectWidth), pen);
                g.DrawLine(x - size + rectHeight, y + (size - rectWidth) / 2 + 2 * rectWidth - 2, x - size, y + (size - rectWidth) / 2 + 2 * rectWidth - 2, pen); // a horizontal line next to the bottom side of the rect
            }
        }
コード例 #6
0
        /// <summary>
        /// Draws the scale manipulator and calculates the bounding rectangles on the left and right
        /// handles</summary>
        /// <param name="g">The graphics object to draw with</param>
        /// <param name="leftHandle">The left handle's bounding rectangle for pick tests, in view
        /// coordinates</param>
        /// <param name="rightHandle">The right handle's bounding rectangle for pick tests, in view
        /// coordinates</param>
        protected virtual void DrawManipulator(D2dGraphics g, out RectangleF leftHandle, out RectangleF rightHandle)
        {
            const float penWidth = 3;

            Matrix worldToView = Owner.Transform;
            float  viewMin     = GdiUtil.Transform(worldToView, WorldMin);
            float  viewMax     = GdiUtil.Transform(worldToView, WorldMax);

            leftHandle  = new RectangleF(viewMin - penWidth * 0.5f, 0.0f, penWidth, HandleHeight);
            rightHandle = new RectangleF(viewMax - penWidth * 0.5f, 0.0f, penWidth, HandleHeight);

            if (IsScaling &&
                ScaleHelper.Mode == ScaleMode.TimePeriod)
            {
                // Draw lines in red at current ghost position.
                viewMin = GdiUtil.Transform(worldToView, ScaleHelper.WorldGhostMin);
                viewMax = GdiUtil.Transform(worldToView, ScaleHelper.WorldGhostMax);
                g.DrawLine(viewMin, 0.0f, viewMax, 0.0f, Color.Red, penWidth, null);
                g.DrawLine(viewMin, 0.0f, viewMin, HandleHeight, Color.Red, penWidth, null);
                g.DrawLine(viewMax, 0.0f, viewMax, HandleHeight, Color.Red, penWidth, null);
            }
            else
            {
                // Draw using original positions and the usual color.
                g.DrawLine(viewMin, 0.0f, viewMax, 0.0f, Color, penWidth, null);
                g.DrawLine(viewMin, 0.0f, viewMin, HandleHeight, Color, penWidth, null);
                g.DrawLine(viewMax, 0.0f, viewMax, HandleHeight, Color, penWidth, null);
            }
        }
コード例 #7
0
        /// <summary>
        /// Draws a partially defined graph edge</summary>
        /// <param name="fromNode">Source node, or null</param>
        /// <param name="fromRoute">Source route, or null</param>
        /// <param name="toNode">Destination node, or null</param>
        /// <param name="toRoute">Destination route, or null</param>
        /// <param name="label">Edge label</param>
        /// <param name="endPoint">Endpoint to substitute for source or destination (in client coords), if either is null</param>
        /// <param name="g">Graphics object</param>
        public override void Draw(
            TNode fromNode,
            NumberedRoute fromRoute,
            TNode toNode,
            NumberedRoute toRoute,
            string label,
            Point endPoint,
            D2dGraphics g)
        {
            var inverse = g.Transform;

            inverse.Invert();
            PointF end = Matrix3x2F.TransformPoint(inverse, endPoint);

            TNode   node     = (fromNode != null) ? fromNode : toNode;
            CircleF boundary = GetBoundary(node);
            Vec2F   proj     = new Vec2F();

            if (CircleF.Project(new Vec2F(end), boundary, ref proj))
            {
                PointF start = new PointF(proj.X, proj.Y);
                g.DrawLine(start, end, m_theme.OutlineBrush);

                if (fromNode == null)
                {
                    PointF temp = end;
                    end   = start;
                    start = temp;
                }
                Vec2F endTangent    = new Vec2F(end.X - start.X, end.Y - start.Y);
                Vec2F arrowPosition = new Vec2F(end);
                DrawArrow(arrowPosition, endTangent, m_theme.OutlineBrush, g);

                if (!string.IsNullOrEmpty(label))
                {
                    PointF     textPoint = new PointF((end.X + start.X) * 0.5f, (end.Y + start.Y) * 0.5f);
                    RectangleF textBox   = new RectangleF(textPoint.X - 512, textPoint.Y, 1024, m_theme.TextFormat.FontHeight);
                    //g.DrawString(label, m_theme.Font, m_theme.TextBrush, textBox, m_theme.CenterStringFormat);
                    g.DrawText(label, m_theme.TextFormat, textBox, m_theme.TextBrush);
                }
            }
        }
コード例 #8
0
        /// <summary>
        /// Draws the scrubber manipulator and calculates the bounding rectangle on the handle</summary>
        /// <param name="g">The graphics object to draw with</param>
        /// <param name="handleRect">The handle's bounding rectangle for pick tests, in view
        /// coordinates</param>
        protected virtual void DrawManipulator(D2dGraphics g, out RectangleF handleRect)
        {
            Matrix worldToView = Owner.Transform;

            float     viewX         = Sce.Atf.GdiUtil.Transform(worldToView, Position);
            Rectangle clipRectangle = Owner.VisibleClientRectangle;

            // allow only the arrow portion to be selected
            handleRect = new RectangleF(
                viewX - 5,
                clipRectangle.Top,
                10,
                7);

            g.DrawLine(viewX, clipRectangle.Top, viewX, clipRectangle.Bottom, s_color, 1.0f, null);

            Color handle_color = m_isMoving ? Color.Tomato : s_color;
            float pos_x        = viewX;
            float pos_y        = clipRectangle.Top + 5;

            s_arrow[0] = new PointF(pos_x - 4, pos_y - 5);
            s_arrow[1] = new PointF(pos_x - 4, pos_y);
            s_arrow[2] = new PointF(pos_x - 5, pos_y + 1);
            s_arrow[3] = new PointF(pos_x - 5, pos_y + 2);
            s_arrow[4] = new PointF(pos_x, pos_y + 7);
            s_arrow[5] = new PointF(pos_x + 5, pos_y + 2);
            s_arrow[6] = new PointF(pos_x + 5, pos_y + 1);
            s_arrow[7] = new PointF(pos_x + 4, pos_y);
            s_arrow[8] = new PointF(pos_x + 4, pos_y - 5);
            //g.FillPolygon(s_arrow, handle_color);   // Fill arrow
            // Draw arrow border with same gray Photoshop uses
            //g.DrawLines(s_arrow, Color.FromArgb(116, 114, 106), 3.0f);

            g.DrawLines(s_arrow, handle_color, 2.0f);

            string label = Position.ToString(CultureInfo.CurrentCulture);

            g.DrawText(label, Owner.Renderer.TextFormat, new PointF(pos_x + 6, clipRectangle.Top), SystemColors.WindowText);
        }
コード例 #9
0
        /// <summary>
        /// Draws horizontal grid using Direct2D</summary>
        /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="step">Grid step in canvas coordinates</param>
        /// <param name="color">Grid line color</param>
        /// <param name="g">Graphics Direct2D drawing surface</param>
        public static void DrawHorizontalGrid(
            Matrix transform,
            RectangleF graphRect,
            double step,
            Color color,
            D2dGraphics g)
        {
            double     yScale     = transform.Elements[3];
            RectangleF clientRect = GdiUtil.Transform(transform, graphRect);

            double screenStep = Math.Abs(yScale * step);

            int a = ComputeOpacity(screenStep);

            color = Color.FromArgb(a, color);
            double start = graphRect.Top - MathUtil.Remainder(graphRect.Top, step) + step;

            for (double y = start; y < graphRect.Bottom; y += step)
            {
                double cy = (y - graphRect.Top) * yScale + clientRect.Top;
                g.DrawLine(clientRect.Left, (float)cy, clientRect.Right, (float)cy, color);
            }
        }
コード例 #10
0
        /// <summary>
        /// Draws vertical grid lines using Direct2D</summary>
        /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="step">Grid step in canvas coordinates</param>
        /// <param name="color">Grid line color</param>
        /// <param name="g">Graphics Direct2D drawing surface</param>
        public static void DrawVerticalGrid(
            Matrix transform,
            RectangleF graphRect,
            double step,
            Color color,
            D2dGraphics g)
        {
            double     xScale     = transform.Elements[0];
            RectangleF clientRect = GdiUtil.Transform(transform, graphRect);

            double screenStep = Math.Abs(xScale * step);
            int    a          = ComputeOpacity(screenStep);

            color = Color.FromArgb(a, color);

            double start = graphRect.Left - MathUtil.Remainder(graphRect.Left, step) + step;

            for (double x = start; x < graphRect.Right; x += step)
            {
                double cx = (x - graphRect.Left) * xScale + clientRect.Left;
                g.DrawLine((float)cx, clientRect.Top, (float)cx, clientRect.Bottom, color);
            }
        }
コード例 #11
0
        private void Draw(TEdge edge, D2dBrush brush, D2dGraphics g)
        {
            Vec2F   startPoint  = new Vec2F();
            Vec2F   endPoint    = new Vec2F();
            CircleF c           = new CircleF();
            bool    moreThan180 = false;
            Vec2F   endTangent;
            Vec2F   textPoint;
            int     route = edge.FromRoute.Index;

            if (GetEdgeGeometry(edge, route, ref startPoint, ref endPoint, ref c, ref moreThan180))
            {
                g.DrawLine(new PointF(startPoint.X, startPoint.Y),
                           new PointF(endPoint.X, endPoint.Y),
                           brush,
                           m_theme.StrokeWidth);

                endTangent = endPoint - startPoint;
                textPoint  = (endPoint + startPoint) * 0.5f;
            }
            else
            {
                // prepare to draw arc
                RectangleF rect = new RectangleF(c.Center.X - c.Radius, c.Center.Y - c.Radius, 2 * c.Radius, 2 * c.Radius);

                double       angle1 = Math.Atan2(startPoint.Y - c.Center.Y, startPoint.X - c.Center.X);
                double       angle2 = Math.Atan2(endPoint.Y - c.Center.Y, endPoint.X - c.Center.X);
                const double twoPi  = 2 * Math.PI;

                // swap so we always go clockwise
                if (angle1 > angle2)
                {
                    double temp = angle1;
                    angle1 = angle2;
                    angle2 = temp;
                }

                double startAngle = angle1;
                double sweepAngle = angle2 - angle1;

                if (moreThan180)
                {
                    if (sweepAngle < Math.PI)
                    {
                        sweepAngle = -(twoPi - sweepAngle);
                    }
                }
                else
                {
                    if (sweepAngle > Math.PI)
                    {
                        sweepAngle = -(twoPi - sweepAngle);
                    }
                }

                const double RadiansToDegrees = 360 / twoPi;
                startAngle *= RadiansToDegrees;
                sweepAngle *= RadiansToDegrees;

                g.DrawArc((D2dEllipse)rect,
                          brush,
                          (float)startAngle,
                          (float)sweepAngle,
                          m_theme.StrokeWidth);

                endTangent = endPoint - c.Center; endTangent = endTangent.Perp;
                textPoint  = (endPoint + startPoint) * 0.5f;
                CircleF.Project(textPoint, c, ref textPoint);
                if (moreThan180)
                {
                    textPoint -= 2 * (textPoint - c.Center);
                }
            }

            DrawArrow(endPoint, endTangent, brush, g);

            string label = edge.Label;

            if (!string.IsNullOrEmpty(label))
            {
                RectangleF textBox = new RectangleF(textPoint.X - 512, textPoint.Y, 1024, m_theme.TextFormat.FontHeight);
                g.DrawText(label, m_theme.TextFormat, textBox, m_theme.TextBrush);
            }
        }
コード例 #12
0
ファイル: D2dUtil.cs プロジェクト: blue3k/ATFClone
        /// <summary>
        /// Draws a horizontal chart scale</summary>
        /// <param name="g">The Direct2D graphics object</param>
        /// <param name="transform">Graph (world) to window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="top">Whether or not the scale should be aligned along the top of the rectangle</param>
        /// <param name="majorSpacing">Spacing, in pixels, between major tick marks</param>
        /// <param name="minimumGraphStep">Minimum spacing, in graph (world) space, between ticks.
        /// For example, 1.0 would limit ticks to being drawn on whole integers.</param>
        /// <param name="lineBrush">Scale line pen</param>
        /// <param name="textFormat">Text format</param>
        /// <param name="textBrush">Text brush</param>
        public static void DrawHorizontalScale(
            this D2dGraphics g,
            Matrix transform,
            RectangleF graphRect,
            bool top,
            int majorSpacing,
            float minimumGraphStep,
            D2dBrush lineBrush,
            D2dTextFormat textFormat,
            D2dBrush textBrush)
        {
            double     xScale     = transform.Elements[0];
            RectangleF clientRect = Transform(transform, graphRect);

            double tickEnd, majorTickStart, minorTickStart, textStart;

            if (top)
            {
                tickEnd        = clientRect.Top + 1;
                majorTickStart = tickEnd + 12;
                minorTickStart = tickEnd + 6;
                textStart      = tickEnd + 8;
            }
            else
            {
                tickEnd        = clientRect.Bottom - 1;
                majorTickStart = tickEnd - 12;
                minorTickStart = tickEnd - 6;
                textStart      = tickEnd - 19;
            }

            double min = Math.Min(graphRect.Left, graphRect.Right);
            double max = Math.Max(graphRect.Left, graphRect.Right);

            double tickAnchor     = CalculateTickAnchor(min, max);
            double majorGraphStep = CalculateStep(
                min, max, Math.Abs(clientRect.Right - clientRect.Left), majorSpacing, minimumGraphStep);
            int    numMinorTicks = CalculateNumMinorTicks(majorGraphStep, minimumGraphStep, 5);
            double cMinorStep    = (majorGraphStep / numMinorTicks) * xScale;

            if (majorGraphStep > 0)
            {
                double offset = tickAnchor - min;
                offset = offset - MathUtil.Remainder(offset, majorGraphStep);

                // draw leading minor ticks
                double cmx;
                cmx = ((tickAnchor - (offset + majorGraphStep)) - min) * xScale + clientRect.Left + cMinorStep;
                for (int i = 0; i < numMinorTicks - 1 && cmx < clientRect.Right; i++)
                {
                    // cull minor ticks outside of the view
                    if (cmx > clientRect.Left)
                    {
                        g.DrawLine((float)cmx, (float)minorTickStart, (float)cmx, (float)tickEnd, lineBrush);
                    }
                    cmx += cMinorStep;
                }

                for (double x = tickAnchor - offset; x < max; x += majorGraphStep)
                {
                    double cx = (x - min) * xScale + clientRect.Left;
                    g.DrawLine((float)cx, (float)majorTickStart, (float)cx, (float)tickEnd, lineBrush);
                    string xString  = String.Format("{0:G8}", Math.Round(x, 6));
                    SizeF  textSize = g.MeasureText(xString, textFormat);
                    var    textRect = new RectangleF(new PointF((float)cx + 1, (float)textStart), textSize);
                    g.DrawText(xString, textFormat, textRect, textBrush);

                    // draw minor ticks
                    cmx = cx + cMinorStep;
                    for (int i = 0; i < numMinorTicks - 1 && cmx < clientRect.Right; i++)
                    {
                        g.DrawLine((float)cmx, (float)minorTickStart, (float)cmx, (float)tickEnd, lineBrush);
                        cmx += cMinorStep;
                    }
                }
            }
        }
コード例 #13
0
        private void Draw(TNode state, D2dGraphics g, bool outline)
        {
            RectangleF bounds = state.Bounds;

            if (state.Type != StateType.Normal)
            {
                DrawPseudostate(state, g, outline);
            }
            else
            {
                float scaleX     = g.Transform.M11; // assume no rotation.
                float radInPixel = scaleX * CornerRadius;

                IComplexState <TNode, TEdge> complexState = state as IComplexState <TNode, TEdge>;

                StateIndicators indicators = state.Indicators;
                if ((indicators & StateIndicators.Active) != 0)
                {
                    if (radInPixel > MinRadiusInPixel)
                    {
                        D2dEllipse ellipse = new D2dEllipse();
                        ellipse.RadiusX = CornerRadius;
                        ellipse.RadiusY = CornerRadius;
                        ellipse.Center  = bounds.Location;
                        g.FillEllipse(ellipse, Color.SpringGreen);
                    }
                }


                if (radInPixel > MinRadiusInPixel)
                {
                    m_stateRect.Rect = bounds;
                    D2dLinearGradientBrush gradbrush = m_theme.FillGradientBrush;
                    gradbrush.StartPoint = bounds.Location;
                    gradbrush.EndPoint   = new PointF(bounds.Right, bounds.Bottom);

                    g.FillRoundedRectangle(m_stateRect, gradbrush);
                    if (outline)
                    {
                        g.DrawRoundedRectangle(m_stateRect, m_theme.OutlineBrush);
                    }
                }
                else
                {
                    g.FillRectangle(bounds, m_theme.FillBrush);
                    if (outline)
                    {
                        g.DrawRectangle(bounds, m_theme.OutlineBrush);
                    }
                }
                g.DrawLine(bounds.Left, bounds.Top + m_fontHeight + Margin,
                           bounds.Right, bounds.Top + m_fontHeight + Margin, m_theme.OutlineBrush);

                if ((scaleX * m_fontHeight) > MinFontHeightInPixel)
                {
                    g.DrawText(complexState.TitleText, m_theme.TextFormat,
                               new PointF(bounds.X + CornerRadius, bounds.Y + Margin), m_theme.TextBrush);
                }



                //RectangleF textBounds = new RectangleF(
                //    (float)(bounds.Left + 4),
                //    (float)(bounds.Top + m_fontHeight + 2),
                //    (float)(bounds.Width - 5),
                //    (float)(bounds.Height - m_fontHeight - 4));

                //g.DrawString(complexState.Text, m_theme.Font, m_theme.TextBrush, textBounds, s_stateTextFormat);

                //IList<int> partitionWidths = complexState.PartitionSizes;
                //if (partitionWidths.Count > 0)
                //{
                //    // draw AND-state dividers
                //    int lastDivider = bounds.Left;
                //    foreach (int width in partitionWidths)
                //    {
                //        g.DrawLine(
                //            m_dividerPen,
                //            lastDivider, bounds.Y + m_fontHeight + Margin,
                //            lastDivider, bounds.Y + bounds.Height);

                //        lastDivider += width;
                //    }
                //}
            }
        }