Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Spec] Shapes #8

Closed
Tracked by #22
jsuarezruiz opened this issue May 18, 2020 · 1 comment
Closed
Tracked by #22

[Spec] Shapes #8

jsuarezruiz opened this issue May 18, 2020 · 1 comment
Labels
t/enhancement ☀️ New feature or request

Comments

@jsuarezruiz
Copy link
Contributor

Shapes

A Shape is a type of View that enables you to draw a shape to the screen.

But, hey, why Shapes?

Currently there are Views such as BoxView or Frame that allow us to create elements such as rectangles or ellipses but, there are many more complex shapes. Having a set of Shapes (lines, polygons, etc.) allows a greater number of possibilities when creating attractive and rich user interfaces.

shapes

Notice that in the design there are:

  • Dash Line
  • Circle (Ellipse)
  • Bubble (Path)
  • Nested Circles (Path)

API

Next, the Shapes API definition.

NOTE: This API definition is based on WPF Shapes API: https://docs.microsoft.com/en-us/dotnet/framework/wpf/graphics-multimedia/shapes-and-basic-drawing-in-wpf-overview

Shape

To render a Shape, must set the Fill property of the Shape to the Color you want. A Shape can also have a Stroke, which is a line that is drawn around the shape's perimeter. A Stroke also requires a Color that defines its appearance, and should have a non-zero value for StrokeThickness. StrokeThickness is a property that defines the perimeter's thickness around the shape edge.

public class Shape : View
{
    public static readonly BindableProperty FillProperty =
        BindableProperty.Create(nameof(Fill), typeof(Color), typeof(Shape), null);

    public static readonly BindableProperty StrokeProperty =
        BindableProperty.Create(nameof(Stroke), typeof(Color), typeof(Shape), null);

    public static readonly BindableProperty StrokeThicknessProperty =
        BindableProperty.Create(nameof(StrokeThickness), typeof(double), typeof(Shape), 1.0);

    public static readonly BindableProperty StrokeDashArrayProperty =
        BindableProperty.Create(nameof(StrokeDashArray), typeof(DoubleCollection), typeof(Shape), null,
            defaultValueCreator: bindable => new DoubleCollection());

    public static readonly BindableProperty StrokeDashOffsetProperty =
        BindableProperty.Create(nameof(StrokeDashOffset), typeof(double), typeof(Shape), 0.0);

    public static readonly BindableProperty AspectProperty =
        BindableProperty.Create(nameof(Aspect), typeof(Stretch), typeof(Shape), Stretch.None);

    public Color Fill
    {
        set { SetValue(FillProperty, value); }
        get { return (Color)GetValue(FillProperty); }
    }

    public Color Stroke
    {
        set { SetValue(StrokeProperty, value); }
        get { return (Color)GetValue(StrokeProperty); }
    }

    public double StrokeThickness
    {
        set { SetValue(StrokeThicknessProperty, value); }
        get { return (double)GetValue(StrokeThicknessProperty); }
    }

    public DoubleCollection StrokeDashArray
    {
        set { SetValue(StrokeDashArrayProperty, value); }
        get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); }
    }

    public double StrokeDashOffset
    {
        set { SetValue(StrokeDashOffsetProperty, value); }
        get { return (double)GetValue(StrokeDashOffsetProperty); }
    }

    public Stretch Aspect
    {
        set { SetValue(AspectProperty, value); }
        get { return (Stretch)GetValue(AspectProperty); }
    }
}

Ellipse

An Ellipse is a shape with a curved perimeter. To create a basic Ellipse, specify a WidthRequest, HeightRequest, and a Color for the Fill.

public sealed class Ellipse : Shape
{

}

Example:

<Ellipse Fill="SteelBlue" HeightRequest="200" WidthRequest="200" />

Ellipse

Rectangle

A Rectangle is a four-sided shape with its opposite sides being equal. To create a basic Rectangle, specify a WidthRequest, a HeightRequest, and a Fill.
You can round the corners of a Rectangle. To create rounded corners, specify a value for the RadiusX and RadiusY properties. These properties specify the x-axis and y-axis of an ellipse that defines the curve of the corners.

public sealed class Rectangle : Shape
{
    public static readonly BindableProperty RadiusXProperty =
        BindableProperty.Create(nameof(RadiusX), typeof(double), typeof(Rectangle), 0.0);

    public static readonly BindableProperty RadiusYProperty =
        BindableProperty.Create(nameof(RadiusY), typeof(double), typeof(Rectangle), 0.0);

    public double RadiusX
    {
        set { SetValue(RadiusXProperty, value); }
        get { return (double)GetValue(RadiusXProperty); }
    }

    public double RadiusY
    {
        set { SetValue(RadiusYProperty, value); }
        get { return (double)GetValue(RadiusYProperty); }
    }
}

Example:

<Rectangle Fill="Blue" Width="200" Height="100" Stroke="Black" StrokeThickness="3" RadiusX="50" RadiusY="10" />

Rectangle

Polygon

A Polygon is a shape with a boundary defined by an arbitrary number of points. The boundary is created by connecting a line from one point to the next, with the last point connected to the first point. The Points property defines the collection of points that make up the boundary.

public sealed class Polygon : Shape
{
    public static readonly BindableProperty PointsProperty =
        BindableProperty.Create(nameof(Points), typeof(PointCollection), typeof(Polygon), null, defaultValueCreator: bindable => new PointCollection());

    public static readonly BindableProperty FillRuleProperty =
        BindableProperty.Create(nameof(FillRule), typeof(FillRule), typeof(Polygon), FillRule.EvenOdd);

    public PointCollection Points
    {
        set { SetValue(PointsProperty, value); }
        get { return (PointCollection)GetValue(PointsProperty); }
    }

    public FillRule FillRule
    {
        set { SetValue(FillRuleProperty, value); }
        get { return (FillRule)GetValue(FillRuleProperty); }
    }
}

The FillRule property specifies a "rule" which the composite shape uses to determine whether a given point is part of the geometry. There are two possible values for FillRule: EvenOdd and Nonzero.

Example:

<Polygon Fill="LightBlue" Points="10,200,60,140,130,140,180,200" />

Polygon

Line

A Line is simply a line drawn between two points in coordinate space. For a Line, make sure to specify values for the Stroke and StrokeThickness properties, because otherwise the Line won't render.

public sealed class Line : Shape
{
    public static readonly BindableProperty X1Property =
        BindableProperty.Create(nameof(X1), typeof(double), typeof(Line), 0.0);

    public static readonly BindableProperty Y1Property =
        BindableProperty.Create(nameof(Y1), typeof(double), typeof(Line), 0.0);

    public static readonly BindableProperty X2Property =
        BindableProperty.Create(nameof(X2), typeof(double), typeof(Line), 0.0);

    public static readonly BindableProperty Y2Property =
        BindableProperty.Create(nameof(Y2), typeof(double), typeof(Line), 0.0);

    public double X1
    {
        set { SetValue(X1Property, value); }
        get { return (double)GetValue(X1Property); }
    }

    public double Y1
    {
        set { SetValue(Y1Property, value); }
        get { return (double)GetValue(Y1Property); }
    }

    public double X2
    {
        set { SetValue(X2Property, value); }
        get { return (double)GetValue(X2Property); }
    }

    public double Y2
    {
        set { SetValue(Y2Property, value); }
        get { return (double)GetValue(Y2Property); }
    }
}

Example:

<Line Stroke="Red" X2="400"/>

Polyline

A Polyline is similar to a Polygon in that the boundary of the shape is defined by a set of points, except the last point in a Polyline is not connected to the first point.

public sealed class Polyline : Shape
{
    public static readonly BindableProperty PointsProperty =
        BindableProperty.Create(nameof(Points), typeof(PointCollection), typeof(Polyline), null, defaultValueCreator: bindable => new PointCollection());

    public static readonly BindableProperty FillRuleProperty =
        BindableProperty.Create(nameof(FillRule), typeof(FillRule), typeof(Polyline), FillRule.EvenOdd);

    public PointCollection Points
    {
        set { SetValue(PointsProperty, value); }
        get { return (PointCollection)GetValue(PointsProperty); }
    }

    public FillRule FillRule
    {
        set { SetValue(FillRuleProperty, value); }
        get { return (FillRule)GetValue(FillRuleProperty); }
    }
}

Example:

<Polyline Stroke="Black" StrokeThickness="4" Points="10,200,60,140,130,140,180,200" />

polyline

Path

A Path is the most versatile Shape because you can use it to define an arbitrary geometry. But with this versatility comes complexity. Define the geometry of a path with the Data property.

public sealed class Path : Shape
{
    public static readonly BindableProperty DataProperty =
            BindableProperty.Create(nameof(Data), typeof(Geometry), typeof(Path), null);

    public Geometry Data
    {
        set { SetValue(DataProperty, value); }
        get { return (Geometry)GetValue(DataProperty); }
    }
}

Geometry

Where Geometry provides a base class for objects that define geometric shapes.

public class Geometry : BindableObject
{

}

There will be:

  • LineGeometry
  • EllipseGeometry
  • RectangleGeometry
  • PathGeometry

LineGeometry

Represents the geometry of a line.

public class LineGeometry : Geometry
{
    public static readonly BindableProperty StartPointProperty =
        BindableProperty.Create(nameof(StartPoint), typeof(Point), typeof(LineGeometry), new Point());

    public static readonly BindableProperty EndPointProperty =
        BindableProperty.Create(nameof(EndPoint), typeof(Point), typeof(LineGeometry), new Point());

    public Point StartPoint
    {
        set { SetValue(StartPointProperty, value); }
        get { return (Point)GetValue(StartPointProperty); }
    }

    public Point EndPoint
    {
        set { SetValue(StartPointProperty, value); }
        get { return (Point)GetValue(StartPointProperty); }
    }
}

EllipseGeometry

Represents the geometry of a circle or ellipse.

public class EllipseGeometry : Geometry
{
    public static readonly BindableProperty CenterProperty =
        BindableProperty.Create(nameof(Center), typeof(Point), typeof(EllipseGeometry), new Point());

    public static readonly BindableProperty RadiusXProperty =
        BindableProperty.Create(nameof(RadiusX), typeof(double), typeof(EllipseGeometry), 0.0);

    public static readonly BindableProperty RadiusYProperty =
        BindableProperty.Create(nameof(RadiusY), typeof(double), typeof(EllipseGeometry), 0.0);

    public Point Center
    {
        set { SetValue(CenterProperty, value); }
        get { return (Point)GetValue(CenterProperty); }
    }

    public double RadiusX
    {
        set { SetValue(RadiusXProperty, value); }
        get { return (double)GetValue(RadiusXProperty); }
    }

    public double RadiusY
    {
        set { SetValue(RadiusYProperty, value); }
        get { return (double)GetValue(RadiusYProperty); }
    }
}

RectangleGeometry

Describes a two-dimensional rectangle.

public class RectangleGeometry : Geometry
{
    public static readonly BindableProperty RectangleProperty =
        BindableProperty.Create(nameof(Rectangle), typeof(Rectangle), typeof(RectangleGeometry), new Rectangle());

    public Rectangle Rectangle
    {
        set { SetValue(RectangleProperty, value); }
        get { return (Rectangle)GetValue(RectangleProperty); }
    }
}

PathGeometry

Represents a complex shape that may be composed of arcs, curves, ellipses, lines, and rectangles.

public sealed class PathGeometry : Geometry
{
    public PathGeometry()
    {
        Figures = new PathFigureCollection();
    }

    public static readonly BindableProperty FiguresProperty =
        BindableProperty.Create(nameof(Figures), typeof(PathFigureCollection), typeof(PathGeometry), null);

    public static readonly BindableProperty FillRuleProperty =
        BindableProperty.Create(nameof(FillRule), typeof(FillRule), typeof(PathGeometry), FillRule.EvenOdd);

    [TypeConverter(typeof(PathFigureCollectionConverter))]
    public PathFigureCollection Figures
    {
        set { SetValue(FiguresProperty, value); }
        get { return (PathFigureCollection)GetValue(FiguresProperty); }
    }

    public FillRule FillRule
    {
        set { SetValue(FillRuleProperty, value); }
        get { return (FillRule)GetValue(FillRuleProperty); }
    }
}

Example:

<Path Stroke="DarkGoldenRod" StrokeThickness="3" Data="M 100,200 C 100,25 400,350 400,175 H 280" />

path

Clip Views

All views (inherited from IView) have the Clip property of type Geometry.

clip-image

(In the previous sample, the header background image is clipped to achieve the diagonal effect).

The following example shows an Image without a defined clip region.

<Image 
     Source="waterlilies.jpg" 
     WidthRequest="200"
     HeightRequest="150" />

image

In the next example, an identical Image is created, except that it has a defined clip region.

<Image 
     Source="waterlilies.jpg" 
     WidthRequest="200" 
     HeightRequest="150">
     <Image.Clip>
     <EllipseGeometry
          RadiusX="100"
          RadiusY="75"
          Center="100, 75"/>
     </Image.Clip>
</Image>

clip-image

Applying Styles

We can customize the appearance of Shapes using styles. We can create the styles using XAML or CSS.

Using XAML:

<Style x:Key="MyRectangle" Target="Rectangle">
     <Setter Property="Fill" Value="Red"  />     
     <Setter Property="Stroke" Value="Black"  />
     <Setter Property="StrokeThickness" Value="2"  />
     <Setter Property="StrokeDashArray" Value="2"  />
</Style>

Using CSS:

#MyRectangle {
  fill: red;
  stroke: black;
  stroke-width: 2px;
  stroke-dasharray: 2;
}

Difficulty : Medium

@jsuarezruiz jsuarezruiz added the t/enhancement ☀️ New feature or request label May 18, 2020
@jsuarezruiz
Copy link
Contributor Author

Moved to Xamarin.Forms.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
t/enhancement ☀️ New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant