Lines and Curves#

Common Style of Lines and Curves#

Here is a list of common styles 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 besides the common styles for Lines and Curves.

Here are 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:

Code Result

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.

Code Result

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.

Code Result

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, driven by 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.

Code Result

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 smooth mathematical curve defined by a set of control points, used to create curves and shapes that can be scaled indefinitely. BezierCurve doesn’t have any other style except the common styles for Lines and Curves.

Here is a BezierCurve with style:

Code Result

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 uses two widgets to get the position of the curve endpoints. 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.

Code Result

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)

Curve Anchors#

Curve Anchors and Line Anchors allow for decorations to be placed on a curve or line, such that when the shape is moved, the decoration will stay attached to it at the same parametric position. The anchor has 2 properties for its alignment and position (0-1), and an anchor_fn to supply a callback function which draws the decoration that will be attached to the curve.

Here is an example of an Anchor on a FreeBezierCurve. The decoration can be dragged along the curve with the left mouse button.

Code Result

from functools import partial
import asyncio

params = [None, None, None, None]

def moved(x, y, b, m):
    x1 = params[0].screen_position_x + params[0].computed_width / 2
    x2 = params[1].screen_position_x + params[1].computed_width / 2
    anchor_position = (x - x1) / (x2 - x1)
    anchor_position = max(min(anchor_position, 1), 0)
    params[2].anchor_position = anchor_position
    params[3].text = f"{params[2].anchor_position:.1f}"

def bound(curve=None):
    with ui.ZStack(content_clipping=1):
        params[3] = ui.Label(f"{params[2].anchor_position:.1f}", mouse_moved_fn=moved)

with ui.ZStack():
    with ui.Placer(draggable=1):
        r1 = ui.Rectangle(width=10, height=10, style={"background_color": ui.color.blue})
    with ui.Placer(draggable=1, offset_x=100, offset_y=100):
        r2 = ui.Rectangle(width=10, height=10, style={"background_color": ui.color.green})
    with ui.Frame(separate_window=True):
        curve = ui.FreeBezierCurve(r1, r2, anchor_position=0.25)
    curve.set_anchor_fn(partial(bound, curve))

params[0] = r1
params[1] = r2
params[2] = curve