ArcLib — Arc Shape Library

Curved segments of an ellipse defined by start and end angles. Arcs are ideal for gauges, progress rings, speedometers, pie chart segments, clock faces, and decorative curved elements. The arc is drawn clockwise from start angle to end angle within the bounding ellipse. 86 functions.

CategoryCountDescription
Error Handling4arc_error, errormsg$, strerror$, clearerror
Creation & Destruction4arc# (3 overloads), arc_free
Arc Angles5startangle, endangle (get/set), angles# (set both)
Fill3fill$ (get), fill# (set), fillnone#
Stroke11stroke$ / stroke# / strokenone#, thickness, dash, cap, join (get/set)
Position & Size14x, y, width, height (get/set), bounds#, move#, size#
Alignment & Margins12align (get/set), margin#, margins#, marginleft/top/right/bottom (get/set)
Visibility & State8visible, enabled, opacity, hittest (get/set)
Tag, Rotation & Parent8tag, rotation (get/set), parent# (get/set), bringtofront#, sendtoback#, invalidate#
Events199 event types × set/get + clearcallbacks#

Cross-Platform Support

PlatformStatusNotes
Windows✅ Full SupportWin32/Win64
macOS✅ Full SupportIntel/ARM (Apple Silicon)
Linux✅ Full SupportGTK-based
Android✅ Full SupportHardware-accelerated
iOS✅ Full SupportHardware-accelerated

Error Handling

FunctionSignatureDescription
arc_error()arc_error@Last error code (0 = no error)
arc_errormsg$()arc_errormsg$@Last error message as string
arc_strerror$(code)arc_strerror$@nDescription for a given error code
arc_clearerror()arc_clearerror@Clear the error state

Angle System

Angles are measured in degrees, starting from the right (3 o'clock position) and increasing clockwise:

DegreesPositionClock Analogy
Right3 o’clock
90°Bottom6 o’clock
180°Left9 o’clock
270°Top12 o’clock
360°Full circle (back to right)Complete revolution

Common Arc Ranges

StartEndSpanShape
90°90°Quarter circle (bottom-right)
180°180°Half circle (bottom)
180°360°180°Half circle (top) — great for gauges
270°270°Three-quarter circle
360°360°Full circle
270°360°90°Quarter circle (top-right)
135°405°270°270° gauge arc (bottom-open)
ⓘ Note: The arc is drawn clockwise from start angle to end angle. The end angle can exceed 360° (e.g., start=270, end=630 draws a full circle starting from the top).
⚠ Warning: If start angle equals end angle, no arc is visible. The end angle must be greater than the start angle for the arc to render.

Numeric Values Reference

Control Alignment arc_align#

ValueDescription
0None (absolute positioning)
1Top
2Left
3Right
4Bottom
9Client (fill parent)

Stroke Dash Style arc_strokedash#

ValueStyle
0Solid
1Dash
2Dot
3DashDot
4DashDotDot

Stroke Cap Style arc_strokecap#

ValueStyle
0Flat
1Round

Stroke Join Style arc_strokejoin#

ValueStyle
0Miter
1Round
2Bevel

Creation & Destruction

FunctionSignatureDescription
arc#(parent#)arc#@#Create with default size
arc#(parent#, w, h)arc#@#nnCreate with specified size
arc#(parent#, x, y, w, h)arc#@#nnnnCreate with position and size
arc_free(arc#)arc_free@#Destroy arc shape
╯ plan9basic
' Quarter circle arc
let a# = arc#(frm#, 50, 50, 200, 200)
arc_startangle#(a#, 0)
arc_endangle#(a#, 90)
arc_fill#(a#, "#3498db")
arc_stroke#(a#, "#2c3e50")
arc_strokethickness#(a#, 2)
ⓘ Note: Use equal width and height for circular arcs. Different width/height creates elliptical arcs.

Arc Angle Properties

FunctionSignatureDescription
arc_startangle(arc#)arc_startangle@#Get start angle in degrees
arc_startangle#(arc#, angle)arc_startangle#@#nSet start angle in degrees
arc_endangle(arc#)arc_endangle@#Get end angle in degrees
arc_endangle#(arc#, angle)arc_endangle#@#nSet end angle in degrees
arc_angles#(arc#, start, end)arc_angles#@#nnSet both angles at once
╯ plan9basic
' Semi-circle gauge (top half)
arc_angles#(a#, 180, 360)

' Full ring
arc_angles#(a#, 0, 360)

' 270-degree gauge (bottom-open)
arc_angles#(a#, 135, 405)

' Animated: set end angle dynamically
arc_endangle#(a#, 180 + progress * 1.8)

Fill Properties

FunctionSignatureDescription
arc_fill$(arc#)arc_fill$@#Get fill color as hex string
arc_fill#(arc#, color$)arc_fill#@#$Set fill color ("#RRGGBB" or "#AARRGGBB")
arc_fillnone#(arc#)arc_fillnone#@#Remove fill (transparent)
PatternFillStrokeUse Case
Filled wedgearc_fill#arc_strokenone#Pie chart segments, colored gauges
Bordered wedgearc_fill#arc_stroke#Progress indicators with borders
Stroke-only arcarc_fillnone#arc_stroke#Progress rings, loading spinners, gauge outlines
ⓘ Note: For progress rings and loading spinners, use arc_fillnone# with a thick stroke and round caps. This creates the modern circular progress indicator look.

Stroke Properties

FunctionSignatureDescription
arc_stroke$(arc#)arc_stroke$@#Get stroke color
arc_stroke#(arc#, color$)arc_stroke#@#$Set stroke color
arc_strokenone#(arc#)arc_strokenone#@#Remove stroke (no border)
arc_strokethickness(arc#)arc_strokethickness@#Get stroke thickness
arc_strokethickness#(arc#, n)arc_strokethickness#@#nSet stroke thickness
arc_strokedash(arc#)arc_strokedash@#Get dash style (0–4)
arc_strokedash#(arc#, n)arc_strokedash#@#nSet dash style
arc_strokecap(arc#)arc_strokecap@#Get cap style (0=Flat, 1=Round)
arc_strokecap#(arc#, n)arc_strokecap#@#nSet cap style
arc_strokejoin(arc#)arc_strokejoin@#Get join style (0=Miter, 1=Round, 2=Bevel)
arc_strokejoin#(arc#, n)arc_strokejoin#@#nSet join style
ⓘ Note: Round stroke caps (arc_strokecap#(arc#, 1)) look significantly better on thick stroke-only arcs. They round off the ends of the arc curve instead of cutting them flat.

Position & Size

FunctionSignatureDescription
arc_x(arc#)arc_x@#Get X position
arc_x#(arc#, x)arc_x#@#nSet X position
arc_y(arc#)arc_y@#Get Y position
arc_y#(arc#, y)arc_y#@#nSet Y position
arc_width(arc#)arc_width@#Get width
arc_width#(arc#, w)arc_width#@#nSet width
arc_height(arc#)arc_height@#Get height
arc_height#(arc#, h)arc_height#@#nSet height
arc_bounds#(arc#, x, y, w, h)arc_bounds#@#nnnnSet position and size
arc_move#(arc#, x, y)arc_move#@#nnSet position only
arc_size#(arc#, w, h)arc_size#@#nnSet size only

Alignment & Margins

FunctionSignatureDescription
arc_align(arc#)arc_align@#Get alignment
arc_align#(arc#, n)arc_align#@#nSet alignment
arc_margin#(arc#, n)arc_margin#@#nSet uniform margin on all four sides
arc_margins#(arc#, l, t, r, b)arc_margins#@#nnnnSet individual margins
arc_marginleft(arc#)arc_marginleft@#Get left margin
arc_marginleft#(arc#, n)arc_marginleft#@#nSet left margin
arc_margintop(arc#)arc_margintop@#Get top margin
arc_margintop#(arc#, n)arc_margintop#@#nSet top margin
arc_marginright(arc#)arc_marginright@#Get right margin
arc_marginright#(arc#, n)arc_marginright#@#nSet right margin
arc_marginbottom(arc#)arc_marginbottom@#Get bottom margin
arc_marginbottom#(arc#, n)arc_marginbottom#@#nSet bottom margin

Visibility & State

FunctionSignatureDescription
arc_visible(arc#)arc_visible@#Get visibility (0/1)
arc_visible#(arc#, n)arc_visible#@#nSet visibility
arc_enabled(arc#)arc_enabled@#Get enabled state (0/1)
arc_enabled#(arc#, n)arc_enabled#@#nSet enabled state
arc_opacity(arc#)arc_opacity@#Get opacity (0.0–1.0)
arc_opacity#(arc#, n)arc_opacity#@#nSet opacity
arc_hittest(arc#)arc_hittest@#Get hit-test state (0/1)
arc_hittest#(arc#, n)arc_hittest#@#nEnable/disable mouse hit testing
⚠ Warning: Set arc_hittest#(arc#, 1) before assigning mouse event handlers. Without hit testing, the shape will not receive mouse events.

Tag, Rotation & Parent

FunctionSignatureDescription
arc_tag(arc#)arc_tag@#Get user-defined integer tag
arc_tag#(arc#, n)arc_tag#@#nSet user-defined integer tag
arc_rotation(arc#)arc_rotation@#Get rotation angle in degrees
arc_rotation#(arc#, angle)arc_rotation#@#nSet rotation angle
arc_parent#(arc#)arc_parent#@#Get parent control pointer
arc_parent#(arc#, parent#)arc_parent#@##Move to a different parent
arc_bringtofront#(arc#)arc_bringtofront#@#Bring to front of Z-order
arc_sendtoback#(arc#)arc_sendtoback#@#Send to back of Z-order
arc_invalidate#(arc#)arc_invalidate#@#Force the shape to redraw
ⓘ Note: arc_rotation# rotates the entire shape around its center, which is different from changing arc_startangle#/arc_endangle#. Rotation shifts the visual orientation; angles control which segment of the ellipse is drawn.

Events

Each event has a setter (arc_onXXX#) and a getter (arc_onXXX$). Use arc_clearcallbacks#(arc#) to disconnect all callbacks at once.

Mouse Events

Event SetterGetterCallback Signature
arc_onclick#(arc#, func$)arc_onclick$(arc#)function name(sender#)
arc_ondblclick#(arc#, func$)arc_ondblclick$(arc#)function name(sender#)
arc_onmousedown#(arc#, func$)arc_onmousedown$(arc#)function name(sender#, button, x, y, shift$)
arc_onmouseup#(arc#, func$)arc_onmouseup$(arc#)function name(sender#, button, x, y, shift$)
arc_onmousemove#(arc#, func$)arc_onmousemove$(arc#)function name(sender#, x, y, shift$)
arc_onmouseenter#(arc#, func$)arc_onmouseenter$(arc#)function name(sender#)
arc_onmouseleave#(arc#, func$)arc_onmouseleave$(arc#)function name(sender#)
arc_onmousewheel#(arc#, func$)arc_onmousewheel$(arc#)function name(sender#, delta)

Other Events

Event SetterGetterCallback Signature
arc_onresize#(arc#, func$)arc_onresize$(arc#)function name(sender#)
arc_clearcallbacks#(arc#)Disconnect all event callbacks

Arc vs Pie

FeatureArc (ArcLib)Pie (PieLib)
ShapeCurved segment — follows the ellipse edgeWedge/sector — fills from center to edge
Typical stroke-only useProgress rings, loading spinners, gaugesNot typical (wedges are usually filled)
Typical filled useGauge needles, colored arc segmentsPie charts, segmented displays
Angle propertiesstartangle, endangle, angles#startangle, endangle, angles#
Visual differenceLooks like a curved line or partial ringLooks like a pizza slice
Function count8686
ⓘ Note: Both ArcLib and PieLib use the same angle system (0° = right, clockwise). The key difference is visual: arcs trace the edge of the ellipse, while pies fill the interior wedge from the center.

Complete Examples

Progress Gauge

╯ gauge.bas
let frm# = form#("Progress Gauge", 300, 300)
form_position#(frm#, 4)

' Background arc (full semi-circle, top half)
let bgArc# = arc#(frm#, 50, 75, 200, 200)
arc_startangle#(bgArc#, 180)
arc_endangle#(bgArc#, 360)
arc_fill#(bgArc#, "#ecf0f1")
arc_stroke#(bgArc#, "#bdc3c7")
arc_strokethickness#(bgArc#, 2)

' Progress arc (75% of 180 degrees = 135 degrees)
let progArc# = arc#(frm#, 50, 75, 200, 200)
arc_startangle#(progArc#, 180)
arc_endangle#(progArc#, 315)
arc_fill#(progArc#, "#3498db")
arc_stroke#(progArc#, "#2980b9")
arc_strokethickness#(progArc#, 2)

let lblPct# = label#(frm#, "75%")
label_bounds#(lblPct#, 100, 140, 100, 30)
label_textalign#(lblPct#, 0)
label_fontsize#(lblPct#, 24)

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Loading Spinner

╯ spinner.bas
let frm# = form#("Loading", 250, 250)
form_position#(frm#, 4)

' Spinning arc (stroke-only with round caps)
let spinner# = arc#(frm#, 50, 50, 150, 150)
arc_fillnone#(spinner#)
arc_stroke#(spinner#, "#e74c3c")
arc_strokethickness#(spinner#, 8)
arc_strokecap#(spinner#, 1)
arc_startangle#(spinner#, 0)
arc_endangle#(spinner#, 90)

let spinAngle = 0

let tmr# = timer#()
timer_interval#(tmr#, 30)
timer_ontimer#(tmr#, "OnSpin")
timer_start#(tmr#)

form_show(frm#)

function OnSpin(sender#)
    spinAngle = spinAngle + 8
    if spinAngle >= 360 then spinAngle = 0
    arc_startangle#(spinner#, spinAngle)
    arc_endangle#(spinner#, spinAngle + 90)
endfunction

while form_visible(frm#) = 1
    processmessages()
end while

Interactive Progress Ring

╯ progressring.bas
let frm# = form#("Progress Ring", 300, 350)
form_position#(frm#, 4)

let progress = 0

' Background ring
let bgRing# = arc#(frm#, 50, 30, 200, 200)
arc_fillnone#(bgRing#)
arc_stroke#(bgRing#, "#ecf0f1")
arc_strokethickness#(bgRing#, 12)
arc_angles#(bgRing#, 0, 360)

' Progress ring
let ring# = arc#(frm#, 50, 30, 200, 200)
arc_fillnone#(ring#)
arc_stroke#(ring#, "#2ecc71")
arc_strokethickness#(ring#, 12)
arc_strokecap#(ring#, 1)
arc_angles#(ring#, 270, 270)

let lblPct# = label#(frm#, "0%")
label_bounds#(lblPct#, 100, 110, 100, 30)
label_textalign#(lblPct#, 0)
label_fontsize#(lblPct#, 22)

let btnAdd# = button#(frm#, "+10%")
button_bounds#(btnAdd#, 50, 270, 90, 35)
button_onclick#(btnAdd#, "OnAdd")

let btnReset# = button#(frm#, "Reset")
button_bounds#(btnReset#, 160, 270, 90, 35)
button_onclick#(btnReset#, "OnReset")

form_show(frm#)

function OnAdd(sender#)
    progress = progress + 10
    if progress > 100 then progress = 100
    ' Map 0-100% to 270..630 (starting from top)
    arc_endangle#(ring#, 270 + progress * 3.6)
    label_text#(lblPct#, str$(progress) + "%")
endfunction

function OnReset(sender#)
    progress = 0
    arc_endangle#(ring#, 270)
    label_text#(lblPct#, "0%")
endfunction

while form_visible(frm#) = 1
    processmessages()
end while

Speedometer

╯ speedometer.bas
let frm# = form#("Speedometer", 320, 300)
form_position#(frm#, 4)

let speed = 0

' Background gauge arc (270 degrees, bottom-open)
let bgGauge# = arc#(frm#, 35, 30, 250, 250)
arc_fillnone#(bgGauge#)
arc_stroke#(bgGauge#, "#ddd")
arc_strokethickness#(bgGauge#, 16)
arc_angles#(bgGauge#, 135, 405)

' Speed arc
let speedArc# = arc#(frm#, 35, 30, 250, 250)
arc_fillnone#(speedArc#)
arc_stroke#(speedArc#, "#27ae60")
arc_strokethickness#(speedArc#, 16)
arc_strokecap#(speedArc#, 1)
arc_angles#(speedArc#, 135, 135)

let lblSpeed# = label#(frm#, "0")
label_bounds#(lblSpeed#, 110, 120, 100, 40)
label_textalign#(lblSpeed#, 0)
label_fontsize#(lblSpeed#, 32)

let lblUnit# = label#(frm#, "km/h")
label_bounds#(lblUnit#, 110, 160, 100, 20)
label_textalign#(lblUnit#, 0)

let btnUp# = button#(frm#, "Accelerate")
button_bounds#(btnUp#, 30, 260, 120, 30)
button_onclick#(btnUp#, "OnAccel")

let btnDown# = button#(frm#, "Brake")
button_bounds#(btnDown#, 170, 260, 120, 30)
button_onclick#(btnDown#, "OnBrake")

form_show(frm#)

function OnAccel(sender#) local speed, endA
    speed = speed + 20
    if speed > 200 then speed = 200
    ' Map 0-200 to 135..405 (270 degree range)
    let endA = 135 + speed / 200 * 270
    arc_endangle#(speedArc#, endA)
    label_text#(lblSpeed#, str$(speed))
    ' Color: green -> yellow -> red
    if speed < 80 then
        arc_stroke#(speedArc#, "#27ae60")
    else if speed < 140 then
        arc_stroke#(speedArc#, "#f39c12")
    else
        arc_stroke#(speedArc#, "#e74c3c")
    endif
endfunction

function OnBrake(sender#) local speed, endA
    speed = speed - 20
    if speed < 0 then speed = 0
    let endA = 135 + speed / 200 * 270
    arc_endangle#(speedArc#, endA)
    label_text#(lblSpeed#, str$(speed))
    if speed < 80 then
        arc_stroke#(speedArc#, "#27ae60")
    else if speed < 140 then
        arc_stroke#(speedArc#, "#f39c12")
    else
        arc_stroke#(speedArc#, "#e74c3c")
    endif
endfunction

while form_visible(frm#) = 1
    processmessages()
end while

Best Practices

PracticeWhy
Use arc_fillnone# + thick stroke for progress ringsCreates the modern circular progress indicator look
Use arc_strokecap#(arc#, 1) for round endsMuch better visual on thick stroke-only arcs
Use arc_angles# to set both angles at onceMore efficient than two separate calls
Start gauge arcs at 135° or 180°135°–405° = 270° bottom-open gauge; 180°–360° = top semi-circle
Set hittest = 1 for interactive arcsRequired for mouse events to fire
Use equal width/height for circular arcsDifferent dimensions create elliptical arcs
Layer background + foreground arcsStack a gray background arc under a colored progress arc
Map percentage to angle: start + pct * span / 100Standard formula for progress indicators

Quick Reference

FunctionSignatureDescription
arc_error / errormsg$ / strerror$ / clearerrorvariousError handling (4)
arc#(parent#[, w, h] | [, x, y, w, h])variousCreate (3 overloads)
arc_free(arc#)arc_free@#Destroy
arc_startangle / endangle / angles#variousArc angles (5)
arc_fill$ / fill# / fillnone#variousFill (3)
arc_stroke$ / stroke# / strokenone# / strokethickness / strokedash / strokecap / strokejoinvariousStroke (11)
arc_x/y/width/height (get/set) / bounds# / move# / size#variousPosition & size (14)
arc_align / margin# / margins# / margin[left/top/right/bottom]variousAlignment & margins (12)
arc_visible / enabled / opacity / hittestvariousVisibility & state (8)
arc_tag / rotation / parent# / bringtofront# / sendtoback# / invalidate#variousTag, rotation & parent (8)
arc_onclick/ondblclick/onmousedown/up/move/enter/leave/onmousewheel/onresizevariousEvents set+get (18)
arc_clearcallbacks#arc_clearcallbacks#@#Disconnect all events

86 functions. Part of the Plan9Basic GUI shape library system.