# Lines and Curves

## Common Style of Lines and Curves
Here is a list of common style you can customize on all the Lines and Curves:
> color (color): the color of the line or curve
> border_width (float): the thickness of the line or curve

## Line
Line is the simplest shape that represents a straight line. It has two points, color and thickness. You can use Line to draw line shapes. Line doesn't have any other style except the common style for Lines and Curves.

Here is some of the properties you can customize on Line:
> alignment (enum): the Alignment defines where the line is in parent defined space. It is always scaled to fit.

Here is a list of the supported Alignment value for the line:
```execute 200
from omni.ui import color as cl
style ={
    "Rectangle::table": {"background_color": cl.transparent, "border_color": cl(0.8), "border_width": 0.25},
    "Line::demo": {"color": cl("#007777"), "border_width": 3},
    "ScrollingFrame": {"background_color": cl.transparent},
}
alignments = {
    "ui.Alignment.LEFT": ui.Alignment.LEFT,
    "ui.Alignment.RIGHT": ui.Alignment.RIGHT,
    "ui.Alignment.H_CENTER": ui.Alignment.H_CENTER,
    "ui.Alignment.TOP": ui.Alignment.TOP,
    "ui.Alignment.BOTTOM": ui.Alignment.BOTTOM,
    "ui.Alignment.V_CENTER": ui.Alignment.V_CENTER,
}
with ui.ScrollingFrame(
    height=100,
    vertical_scrollbar_policy=ui.ScrollBarPolicy.SCROLLBAR_ALWAYS_OFF,
    style=style,
):
    with ui.HStack(height=100):
        for key, value in alignments.items():
            with ui.ZStack():
                ui.Rectangle(name="table")
                with ui.VStack(style={"VStack": {"margin": 10}}, spacing=10):
                    ui.Line(name="demo", alignment=value)
                    ui.Label(key, style={"color": cl.white, "font_size": 12}, alignment=ui.Alignment.CENTER)

```

By default, the line is scaled to fit.
```execute 200
from omni.ui import color as cl
style = {"Line::default": {"color": cl.red, "border_width": 1}}
with ui.Frame(height=50, style=style):
    ui.Line(name="default")
```

Users can define the color and border_width to make customized lines.
```execute 200
from omni.ui import color as cl
with ui.Frame(height=50):
    with ui.ZStack(width=200):
        ui.Rectangle(style={"background_color": cl(0.4)})
        ui.Line(alignment=ui.Alignment.H_CENTER, style={"border_width":5, "color": cl("#880088")})
```

## FreeLine
FreeLine is a line whose length will be determined by other widgets. The supported style list is the same as Line.

Here is an example of a FreeLine with style following two draggable circles. Notice the control widgets are not the start and end points of the line. By default, the alignment of the line is `ui.Alighment.V_CENTER`, and the line direction won't be changed by the control widgets.

```execute 200
from omni.ui import color as cl
with ui.Frame(height=200):
    with ui.ZStack():
        # Four draggable rectangles that represent the control points
        with ui.Placer(draggable=True, offset_x=0, offset_y=0):
            control1 = ui.Circle(width=10, height=10)
        with ui.Placer(draggable=True, offset_x=150, offset_y=200):
            control2 = ui.Circle(width=10, height=10)

        # The rectangle that fits to the control points
        ui.FreeLine(control1, control2, style={"color":cl.yellow})
```

## BezierCurve
BezierCurve is a shape drawn with multiple lines which has a bent or turns in it. They are used to model smooth curves that can be scaled indefinitely. BezierCurve doesn't have any other style except the common style for Lines and Curves.

Here is a BezierCurve with style:
```execute 200
from omni.ui import color as cl
style = {"BezierCurve": {"color": cl.red, "border_width": 2}}
ui.Spacer(height=2)
with ui.Frame(height=50, style=style):
    ui.BezierCurve()
ui.Spacer(height=2)
```

## FreeBezierCurve
FreeBezierCurve is using two widgets to get the position of the curve ends. This is super useful to build graph connections. The supported style list is the same as BezierCurve.

Here is an example of a FreeBezierCurve which is controlled by 4 control points.

```execute 200
from omni.ui import color as cl
with ui.ZStack(height=400):
    # The Bezier tangents
    tangents = [(50, 50), (-50, -50)]

    # Four draggable rectangles that represent the control points
    placer1 = ui.Placer(draggable=True, offset_x=0, offset_y=0)
    with placer1:
        rect1 = ui.Rectangle(width=20, height=20)
    placer2 = ui.Placer(draggable=True, offset_x=50, offset_y=50)
    with placer2:
        rect2 = ui.Rectangle(width=20, height=20)
    placer3 = ui.Placer(draggable=True, offset_x=100, offset_y=100)
    with placer3:
        rect3 = ui.Rectangle(width=20, height=20)
    placer4 = ui.Placer(draggable=True, offset_x=150, offset_y=150)
    with placer4:
        rect4 = ui.Rectangle(width=20, height=20)

    # The bezier curve
    curve = ui.FreeBezierCurve(rect1, rect4, style={"color": cl.red, "border_width": 5})
    curve.start_tangent_width = ui.Pixel(tangents[0][0])
    curve.start_tangent_height = ui.Pixel(tangents[0][1])
    curve.end_tangent_width = ui.Pixel(tangents[1][0])
    curve.end_tangent_height = ui.Pixel(tangents[1][1])

    # The logic of moving the control points
    def left_moved(_):
        x = placer1.offset_x
        y = placer1.offset_y
        tangent = tangents[0]
        placer2.offset_x = x + tangent[0]
        placer2.offset_y = y + tangent[1]

    def right_moved(_):
        x = placer4.offset_x
        y = placer4.offset_y
        tangent = tangents[1]
        placer3.offset_x = x + tangent[0]
        placer3.offset_y = y + tangent[1]

    def left_tangent_moved(_):
        x1 = placer1.offset_x
        y1 = placer1.offset_y
        x2 = placer2.offset_x
        y2 = placer2.offset_y
        tangent = (x2 - x1, y2 - y1)
        tangents[0] = tangent
        curve.start_tangent_width = ui.Pixel(tangent[0])
        curve.start_tangent_height = ui.Pixel(tangent[1])

    def right_tangent_moved(_):
        x1 = placer4.offset_x
        y1 = placer4.offset_y
        x2 = placer3.offset_x
        y2 = placer3.offset_y
        tangent = (x2 - x1, y2 - y1)
        tangents[1] = tangent
        curve.end_tangent_width = ui.Pixel(tangent[0])
        curve.end_tangent_height = ui.Pixel(tangent[1])

    # Callback for moving the control points
    placer1.set_offset_x_changed_fn(left_moved)
    placer1.set_offset_y_changed_fn(left_moved)
    placer2.set_offset_x_changed_fn(left_tangent_moved)
    placer2.set_offset_y_changed_fn(left_tangent_moved)
    placer3.set_offset_x_changed_fn(right_tangent_moved)
    placer3.set_offset_y_changed_fn(right_tangent_moved)
    placer4.set_offset_x_changed_fn(right_moved)
    placer4.set_offset_y_changed_fn(right_moved)
```