/// <summary>Updates the FlatWorldUI so it builds the mesh for this element.</summary> private void UpdateRenderer(LayoutBox box, float width, float height) { // - Set w/h to width and height: int w = (int)width; int h = (int)height; // Resize the renderer (which will emit a changed image event): Renderer.SetDimensions(w, h); // Temporarily set the positioning of 'box' such that it's at the origin: int _pos = box.PositionMode; BoxStyle _position = box.Position; // Clear: box.Position = new BoxStyle(0f, float.MaxValue, float.MaxValue, 0f); box.PositionMode = PositionMode.Fixed; // Put the RenderData in the render only queue of *Renderer* and ask it to layout now: RenderableData _next = RenderData.Next; UpdateMode _mode = RenderData.NextUpdateMode; RenderableData _ancestor = RenderData.Ancestor; // Clear: RenderData.Next = null; RenderData.NextUpdateMode = UpdateMode.Render; RenderData.Ancestor = null; // Queue: Renderer.Renderer.StylesToUpdate = RenderData; Renderer.Renderer.HighestUpdateMode = UpdateMode.Render; // Draw now! Renderer.Renderer.Update(); // Restore (box): box.Position = _position; box.PositionMode = _pos; // Restore (queue): RenderData.Next = _next; RenderData.NextUpdateMode = _mode; RenderData.Ancestor = _ancestor; }
/// <summary>Renders and performs a layout of the round corners.</summary> public void Layout(Color colour, LayoutBox box, Renderman renderer, int i) { // Get the co-ord of the top edge: float top = box.Y; float left = box.X; // Get border widths: BoxStyle border = box.Border; // And the dimensions of the lines: // Note: boxwidth doesn't include the left/right widths to prevent overlapping. float boxWidth = box.PaddedWidth; float boxHeight = box.PaddedHeight + border.Top + border.Bottom; switch (i) { case 0: // Top: // Move over by the top-left corner: if (TopLeft != null) { TopLeft.Colour = colour; // Render the top left corner: TopLeft.RenderInverse(box, renderer, left, top); } break; case 1: // Right: if (TopRight != null) { TopRight.Colour = colour; // Render the top right corners inverse now: TopRight.RenderInverse(box, renderer, left + boxWidth + border.Left + border.Right, top); } break; case 2: // Bottom: if (BottomRight != null) { BottomRight.Colour = colour; // Render the bottom right corners inverse now: BottomRight.RenderInverse(box, renderer, left + boxWidth + border.Left + border.Right, top + boxHeight); } break; case 3: // Left: if (BottomLeft != null) { BottomLeft.Colour = colour; // Render the bottom left corners inverse now: BottomLeft.RenderInverse(box, renderer, left, top + boxHeight); } break; } }
/// <summary>Recomputes the inner arc. The inner arc is special because it depends on border width. /// The two borders that this corner connects may be different widths, so it may have to transition from one thickness to another.</summary> private void RecomputeInnerArc(BoxStyle widths) { // How big is the outer arc? int size = OuterArc.Length; if (InnerArc == null || InnerArc.Length != size) { // Create the set now: InnerArc = new Vector2[size]; } // We're next going to be doing some polars here. Everything is clockwise. // We're essentially going to rotate around the virtual center of a circle, changing our radius as we go. // The current angle in radians: float currentAngle; // The virtual center of the circle, relative to the corner: float centerX; float centerY; // The source border width: float sourceWidth; // The target border width: float targetWidth; // Get radius as a float: float cornerRadius = CornerRadius; // The starting radius: float radius = cornerRadius; switch (Position) { case RoundCornerPosition.TopLeft: // The center is up on Y and up on X: centerX = radius; centerY = radius; // The angle starts at.. currentAngle = Mathf.PI; // Get the widths: sourceWidth = widths.Left; targetWidth = widths.Top; break; case RoundCornerPosition.TopRight: // The center is up on Y and down on X: centerX = -radius; centerY = radius; // The angle starts at.. currentAngle = Mathf.PI * 1.5f; // Get the widths: sourceWidth = widths.Top; targetWidth = widths.Right; break; case RoundCornerPosition.BottomRight: // The center is down on Y and down on X: centerX = -radius; centerY = -radius; // The angle starts at.. currentAngle = 0f; // Get the widths: sourceWidth = widths.Right; targetWidth = widths.Bottom; break; default: case RoundCornerPosition.BottomLeft: // The center is down on Y and up on X: centerX = radius; centerY = -radius; // The angle starts at.. currentAngle = Mathf.PI * 0.5f; // Get the widths: sourceWidth = widths.Bottom; targetWidth = widths.Left; break; } // Remove source width from radius: radius -= sourceWidth; // What's the maximum number of iterations? float maximumValue = (float)(size - 1); // Figure out delta angle (we'll be travelling through PI/2 degrees): float deltaAngle = (Mathf.PI * 0.5f) / maximumValue; // Figure out delta radius based on source/target: float deltaRadius = (sourceWidth - targetWidth) / maximumValue; // Next, for each point.. for (int i = 0; i < size; i++) { // Get the cos/sin of the current angle: float cosAngle = Mathf.Cos(currentAngle); float sinAngle = Mathf.Sin(currentAngle); // And also the inner arc value: InnerArc[i] = new Vector2(centerX + (cosAngle * radius), centerY + (sinAngle * radius)); // Move the angle along: currentAngle += deltaAngle; // Move the radius along: radius += deltaRadius; } }
internal override void Layout(LayoutBox box, Renderman renderer) { if (Corners != null) { Corners.PreLayout(); } ComputedStyle computed = RenderData.computedStyle; // Find the zIndex: // NB: At same depth as BGColour - right at the back. float zIndex = (computed.ZIndex - 0.006f); // Get the co-ord of the top edge: float top = box.Y; float left = box.X; // Get the border widths: BoxStyle width = box.Border; // Move top by the widths: top += width.Top; left += width.Left; // And the dimensions of the lines: float boxWidth = box.PaddedWidth; float boxHeight = box.PaddedHeight; // Get the other dimensions: float topY = top - width.Top; float right = left + boxWidth; float rightX = right + width.Right; float bottom = top + boxHeight; float bottomY = bottom + width.Bottom; float leftX = left - width.Left; int segment = renderer.Segment; Transformation transform = renderer.Transform; BoxRegion screenRegion = new BoxRegion(); // Get the default colour - that's the same as the text colour: Color colour = Color.black; // Is the border multicoloured? bool multiColour = false; // Does this border have a colour? if (BaseColour == null) { // Grab the text colour if there is one: if (RenderData.Text != null) { // It's the same as the font colour: colour = RenderData.Text.BaseColour * renderer.ColorOverlay; } else { // Nope - We need to set alpha: colour.a = renderer.ColorOverlay.a; } } else if (BaseColour.Count == 1) { colour = BaseColour[0].GetColour(RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay; } else { multiColour = true; } // Handle border-radius: if (Corners != null) { for (int i = 0; i < 4; i++) { if (multiColour) { colour = BaseColour[i].GetColour(RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay; } Corners.Layout(colour, box, renderer, i); } } // Get clipper: BoxRegion clip = renderer.ClippingBoundary; float origLeftX = leftX; float origTopY = topY; float origBottomY = bottomY; float origRightX = rightX; // top and topY: if (top < clip.Y) { top = clip.Y; } else if (top > clip.MaxY) { top = clip.MaxY; } if (topY < clip.Y) { topY = clip.Y; } // bottom and bottomY: if (bottom > clip.MaxY) { bottom = clip.MaxY; } else if (bottom < clip.Y) { bottom = clip.Y; } if (bottomY > clip.MaxY) { bottomY = clip.MaxY; } // right and rightX: if (right < clip.X) { right = clip.X; } else if (right > clip.MaxX) { right = clip.MaxX; } // rightX vs clip.MaxX if (rightX > clip.MaxX) { rightX = clip.MaxX; } // left and leftX: if (left < clip.X) { left = clip.X; } else if (left > clip.MaxX) { left = clip.MaxX; } if (leftX < clip.X) { leftX = clip.X; } float cornerPointA; float cornerPointB; for (int i = 0; i < 4; i++) { // Does this border have multiple colours? if (multiColour) { colour = BaseColour[i].GetColour(RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay; } // Add to region: switch (i) { case 0: // Top. screenRegion.SetPoints(leftX, topY, rightX, top); break; case 1: // Right. // We only draw the right border if 'segment' includes 'end' if ((segment & LineBoxSegment.End) == 0) { goto NextLine; } screenRegion.SetPoints(right, topY, rightX, bottomY); break; case 2: // Bottom. screenRegion.SetPoints(leftX, bottom, rightX, bottomY); break; case 3: // Left. // Similarly, we only draw left if segment includes 'start': if ((segment & LineBoxSegment.Start) == 0) { goto NextLine; } screenRegion.SetPoints(leftX, topY, left, bottomY); break; } if (screenRegion.Overlaps(clip)) { // It's visible. // Ensure we have a batch (doesn't change graphics or font textures, thus both null): renderer.SetupBatch(this, null, null); // And get our block ready: MeshBlock block = Add(renderer); // Set the UV to that of the solid block colour pixel: block.SetSolidColourUV(); // Set the border colour: block.SetColour(colour); // Apply verts: switch (i) { case 0: // Top: if (Corners == null) { block.VertexTopLeft = renderer.PixelToWorldUnit(leftX, topY, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(rightX, topY, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(left, top, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(right, top, zIndex); } else { // Top left/right corners: cornerPointA = origLeftX + Corners.TopLeftRadius; cornerPointB = origRightX - Corners.TopRightRadius; if (cornerPointA < clip.X) { cornerPointA = clip.X; } if (cornerPointB > clip.MaxX) { cornerPointB = clip.MaxX; } // Note that we use leftX/rightX for all of them. // That's because the corner has a 'straight' edge. block.VertexTopLeft = renderer.PixelToWorldUnit(cornerPointA, topY, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(cornerPointB, topY, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(cornerPointA, top, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(cornerPointB, top, zIndex); } break; case 1: // Right: if (Corners == null) { block.VertexTopLeft = renderer.PixelToWorldUnit(right, top, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(rightX, topY, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(right, bottom, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(rightX, bottomY, zIndex); } else { // Top right/ bottom right corners: cornerPointA = origTopY + Corners.TopRightRadius; cornerPointB = origBottomY - Corners.BottomRightRadius; if (cornerPointA < clip.Y) { cornerPointA = clip.Y; } if (cornerPointB > clip.MaxY) { cornerPointB = clip.MaxY; } // Note that we use topY/bottomY for all of them. block.VertexTopLeft = renderer.PixelToWorldUnit(right, cornerPointA, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(rightX, cornerPointA, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(right, cornerPointB, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(rightX, cornerPointB, zIndex); } break; case 2: // Bottom: if (Corners == null) { block.VertexTopLeft = renderer.PixelToWorldUnit(left, bottom, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(right, bottom, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(leftX, bottomY, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(rightX, bottomY, zIndex); } else { // Bottom left/ bottom right corners: // Note that we use leftX/rightX for all of them. cornerPointA = origLeftX + Corners.BottomLeftRadius; cornerPointB = origRightX - Corners.BottomRightRadius; if (cornerPointA < clip.X) { cornerPointA = clip.X; } if (cornerPointB > clip.MaxX) { cornerPointB = clip.MaxX; } block.VertexTopLeft = renderer.PixelToWorldUnit(cornerPointA, bottom, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(cornerPointB, bottom, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(cornerPointA, bottomY, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(cornerPointB, bottomY, zIndex); } break; case 3: // Left: if (Corners == null) { block.VertexTopLeft = renderer.PixelToWorldUnit(leftX, topY, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(left, top, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(leftX, bottomY, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(left, bottom, zIndex); } else { // Top right/ bottom right corners: cornerPointA = origTopY + Corners.TopLeftRadius; cornerPointB = origBottomY + width.Bottom - Corners.BottomLeftRadius; if (cornerPointA < clip.Y) { cornerPointA = clip.Y; } if (cornerPointB > clip.MaxY) { cornerPointB = clip.MaxY; } block.VertexTopLeft = renderer.PixelToWorldUnit(leftX, cornerPointA, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(left, cornerPointA, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(leftX, cornerPointB, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(left, cornerPointB, zIndex); } break; } // Done! block.Done(transform); } NextLine: continue; } }