Graphics¶
Unicon graphics¶
Unicon has graphics built in, well optionally built in, if the operating system supports X11, Win32 and/or OpenGL.
In the case of Unicon, graphics doesn’t just mean colours drawn on a canvas. Unicon includes an entire Graphical User Interface engine as well. Graphics come in 2D and 3D forms.
That means you can draw, and you can work with widgets and event driven programming without changing language or tool.
Much of the callback management normally associated with event driven programming is handled by Unicon for many of the graphic operations. The design of the graphics features of Unicon allow for both procedural and event programming in a seamless way. Using graphics does not dictate an event driven only style of programming in Unicon.
Colours¶
Unicon uses a pretty nifty colour naming system, as English phrases.
There are base colour names, with attributes for lightness, saturation and transparency levels.
colour := "medium strong bluish green"
Is parsed down to an RGBA (Red Green Blue Alpha) colour value. If the phrase is not understood, Unicon passes the name down to the operating system for another round of lookup. For X11 that means all the Xorg colour names are also valid.
Jafar Al-Gharaibeh was nice enough to extract the pertinent bits from
unicon/src/rwindow.r
static colrname colortable[] = { /* known colors */
/* color ish-form hue lgt sat */
{ "black", "blackish", 0, 0, 0 },
{ "blue", "bluish", 240, 50, 100 },
{ "brown", "brownish", 30, 25, 100 },
{ "cyan", "cyanish", 180, 50, 100 },
{ "gray", "grayish", 0, 50, 0 },
{ "green", "greenish", 120, 50, 100 },
{ "grey", "greyish", 0, 50, 0 },
{ "magenta", "magentaish", 300, 50, 100 },
{ "orange", "orangish", 15, 50, 100 },
{ "pink", "pinkish", 345, 75, 100 },
{ "purple", "purplish", 270, 50, 100 },
{ "red", "reddish", 0, 50, 100 },
{ "violet", "violetish", 270, 75, 100 },
{ "white", "whitish", 0, 100, 0 },
{ "yellow", "yellowish", 60, 50, 100 },
};
static colrmod lighttable[] = { /* lightness modifiers */
{ "dark", 0 },
{ "deep", 0 }, /* = very dark (see code) */
{ "light", 100 },
{ "medium", 50 },
{ "pale", 100 }, /* = very light (see code) */
};
static colrmod sattable[] = { /* saturation levels */
{ "moderate", 50 },
{ "strong", 75 },
{ "vivid", 100 },
{ "weak", 25 },
};
static colrmod transptable[] = { /* transparency levels */
{ "dull", 75 }, /* alias for subtranslucent */
{ "opaque", 100 },
{ "subtranslucent", 75 },
{ "subtransparent", 25 },
{ "translucent", 50 },
{ "transparent", 5 },
};
Along with all those combinations, there are the system names, for things like Teal, and Cornflower, and Dark Khaki, when using X11.
https://en.wikipedia.org/wiki/X11_color_names
With a full list (many hundreds of named colours) in the source code at
https://cgit.freedesktop.org/xorg/xserver/tree/os/oscolor.c
If you look closely, there are more than double the 50 Shades of Grey.
Other specific graphic subsystems will have their own list of available names.
Unicon colour scheme¶
I’ve asked about an official Unicon project colour scheme, and I’m not sure if
there has been an actual choice made. I’ve even asked to put unicon
in
the list of known colours, for potential branding purposes (and cool code
samples). That may never happen though, but it’s worth asking.
From the project home page, from September 2016, the current guess is a form of orange (although the logo on that page uses a professionally laid out gradient, by Serendel Macphereson).
Best (bad)[1] guess (vivid orange) shown below.
#
# colour-sample.icn, a best (bad) guess at a Unicon project colour.
#
procedure main()
&window := open("colour", "g", "size=70,45", "canvas=hidden")
colour := "vivid orange"
Fg(colour)
FillRectangle(5, 5, 60, 35)
WSync()
WriteImage("../images/colour-sample.png")
close(&window)
end
prompt$ unicon -s colour-sample.icn -x
[1] | Do not mistake this author for a graphic designer. There are few design skills in these fingers and little artistic flair hiding behind the eyes. |
Drawing¶
Points, lines, rectangles, circles, spheres, torus and more, are all part and parcel of Unicon graphics. As is text. The write statement can write to a graphical window as readily as it can write to standard output and to files.
Events¶
Event driven programming was never easier or more adaptable than with Unicon.
Attributes¶
Unicon windows use a set of attributes to control look, feel and certain features of graphic handling. These attributes can be set during open and with the function WAttrib.
The src/runtime/rwindow.r
source file lists the following supported
attributes.
stringint attribs[] = {
{ 0, NUMATTRIBS},
{"ascent", A_ASCENT},
{"bg", A_BG},
{"buffer", A_BUFFERMODE},
{"canvas", A_CANVAS},
{"ceol", A_CEOL},
{"cliph", A_CLIPH},
{"clipw", A_CLIPW},
{"clipx", A_CLIPX},
{"clipy", A_CLIPY},
{"col", A_COL},
{"columns", A_COLUMNS},
{"cursor", A_CURSOR},
{"depth", A_DEPTH},
{"descent", A_DESCENT},
{"dim", A_DIM},
{"display", A_DISPLAY},
{"displayheight", A_DISPLAYHEIGHT},
{"displaywidth", A_DISPLAYWIDTH},
{"drawop", A_DRAWOP},
{"dx", A_DX},
{"dy", A_DY},
{"echo", A_ECHO},
{"eye", A_EYE},
{"eyedir", A_EYEDIR},
{"eyepos", A_EYEPOS},
{"eyeup", A_EYEUP},
{"fg", A_FG},
{"fheight", A_FHEIGHT},
{"fillstyle", A_FILLSTYLE},
{"font", A_FONT},
{"fovangle", A_FOV},
{"fwidth", A_FWIDTH},
{"gamma", A_GAMMA},
{"geometry", A_GEOMETRY},
{"glrenderer", A_GLRENDERER},
{"glvendor", A_GLVENDOR},
{"glversion", A_GLVERSION},
{"height", A_HEIGHT},
{"iconic", A_ICONIC},
{"iconimage", A_ICONIMAGE},
{"iconlabel", A_ICONLABEL},
{"iconpos", A_ICONPOS},
{"image", A_IMAGE},
{"inputmask", A_INPUTMASK},
{"label", A_LABEL},
{"leading", A_LEADING},
{"light", A_LIGHT},
{"light0", A_LIGHT0},
{"light1", A_LIGHT1},
{"light2", A_LIGHT2},
{"light3", A_LIGHT3},
{"light4", A_LIGHT4},
{"light5", A_LIGHT5},
{"light6", A_LIGHT6},
{"light7", A_LIGHT7},
{"lines", A_LINES},
{"linestyle", A_LINESTYLE},
{"linewidth", A_LINEWIDTH},
{"meshmode", A_MESHMODE},
{"normode", A_NORMODE},
{"pattern", A_PATTERN},
{"pick", A_PICK},
{"pointer", A_POINTER},
{"pointercol", A_POINTERCOL},
{"pointerrow", A_POINTERROW},
{"pointerx", A_POINTERX},
{"pointery", A_POINTERY},
{"pos", A_POS},
{"posx", A_POSX},
{"posy", A_POSY},
{"resize", A_RESIZE},
{"reverse", A_REVERSE},
{"rgbmode", A_RGBMODE},
{"rings", A_RINGS},
{"row", A_ROW},
{"rows", A_ROWS},
{"selection", A_SELECTION},
{"size", A_SIZE},
{"slices", A_SLICES},
{"texcoord", A_TEXCOORD},
{"texmode", A_TEXMODE},
{"texture", A_TEXTURE},
{"titlebar", A_TITLEBAR},
{"visual", A_VISUAL},
{"width", A_WIDTH},
{"windowlabel", A_WINDOWLABEL},
{"x", A_X},
{"y", A_Y},
};
Vidgets¶
For lack of a better term for Graphical User Interface (GUI)
elements, Unicon includes a rich set of ready to go widgets. Some few built
in, many as part of the IPL as vidgets
and with the Unicon
gui classes.
#
# vidget-button.icn, button demo
#
link evmux
link button
link enqueue
procedure main()
&window := open("vidget", "g", "size=70,45", "canvas=hidden")
b := button(&window, "Hello", hello, -3, 10, 10, 50, 25)
WriteImage("../images/vidget-button.png")
Enqueue(&window, &lpress, 11, 14, "", 2)
Enqueue(&window, &lrelease, 11, 14, "", 3)
evhandle(&window)
end
procedure hello()
write("Hello, button")
end
The sample simulates a mouse click, invoking the hello
procedure.
prompt$ unicon -s vidget-button.icn -x
Hello, button
On names¶
Icon was developed long before modern graphical desktops were mainstream.
There is still no ubiquitous term for graphic elements, but widget
seems
to be well understood. When Clint Jeffery was working with Ralph Griswold on
the early graphics features of Icon, even the term widget was not in common
use. The Icon (now Unicon) team went with a portmanteau of Visual Gadget,
vidget
. Things change, but history stays the same.
Unicon classes allow for a modernized approach to GUI development.
Unicon GUI¶
Todo
samples of Robert Parlett’s GUI classes
Plot coordinate pairs¶
A nice example of Unicon graphics can be found at Rosetta Code under the Plot coordinate pairs
task. Duplicated here from
a copy taken in August of 2016 with some slight modifications to captured
image name, and not waiting for a mouse click to end the run.
#
# From http://rosettacode.org/wiki/Plot_coordinate_pairs#Icon_and_Unicon
#
link printf,numbers
procedure main()
x := [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]
y := [2.7, 2.8, 31.4, 38.1, 58.0, 76.2, 100.5, 130.0, 149.3, 180.0]
Plot(x,y,600,400)
end
$define POINTR 2 # Point Radius
$define POINTC "red" # Point Colour
$define GRIDC "grey" # grid colour
$define AXISC "black" # axis/label colour
$define BORDER 60 # per side border
$define TICKS 5. # grid ticks per axis
$define AXISFH 20 # font height for axis labels
procedure Plot(x,y,cw,ch)
/cw := 700 # default dimensions
/ch := 400
uw := cw-BORDER*2 # usable dimensions
uh := ch-BORDER*2
wparms := ["Plot","g", "canvas=hidden",
sprintf("size=%d,%d",cw,ch),
"bg=white"] # base window parms
dx := sprintf("dx=%d",BORDER) # grid origin
dy := sprintf("dy=%d",BORDER)
&window := open!wparms | stop("Unable to open window")
X := scale(x,uw) # scale data to usable space
Y := scale(y,uh,"invert")
WAttrib(dx,dy) # set origin=grid & draw grid
every x := (X.tickfrom to X.tickto by X.tick) * X.tickscale do {
if x = 0 then Fg(AXISC) else Fg(GRIDC)
DrawLine(x,Y.tickfrom*Y.tickscale,x,Y.tickto*Y.tickscale)
}
every y := (Y.tickfrom to Y.tickto by Y.tick) * Y.tickscale do {
if y = uh then Fg(AXISC) else Fg(GRIDC)
DrawLine(X.tickfrom*X.tickscale,y,X.tickto*X.tickscale,y)
}
Fg(POINTC) # draw data points ....
every i := 1 to *X.scaled do
FillCircle(X.scaled[i],Y.scaled[i],POINTR)
Fg(AXISC) # label grid
WAttrib(dx,"dy=0") # label X axis
Font(sprintf("Helvetica,%d",AXISFH))
ytxt := ch-BORDER+1+(WAttrib("ascent") - WAttrib("descent"))/2
every x := X.tickscale * (xv := X.tickfrom to X.tickto by X.tick) do
DrawString(x - TextWidth(xv)/2, ytxt + integer(AXISFH*1.5),xv)
WAttrib("dx=0",dy) # label Y axis
every y := Y.tickscale * (yv := Y.tickfrom to Y.tickto by Y.tick) do
DrawString(BORDER/2 - TextWidth(yv)/2, ytxt - BORDER - y,yv)
WriteImage("../images/PlotPairs.png") # save image
WAttrib("dx=0","dy=0") # close off nicely
Font("Helvetica,10")
#DrawString(10,ch-5,"click to exit")
#until Event() == &lpress # wait for left mouse button
close(&window)
end
record scaledata(low,high,range,pix,raw,scaled,tick,tickfrom,tickto,tickscale)
procedure scale(data,pix,opts[])
P := scaledata( pmin := min!data, pmax := max!data,
prange := real(pmax-pmin), pix,
data,q :=[])
/ticks := TICKS
P.tick := ceil(prange/(10^(k:=floor(log(prange,10))))*(10^k)/ticks)
P.tickfrom := P.tick*floor(pmin/P.tick)
P.tickto := P.tick*ceil(pmax/P.tick)
P.tickscale := real(pix)/(P.tickto-P.tickfrom)
every put(q,integer((!data-P.tickfrom)*P.tickscale))
if !opts == "invert" then # invert is for y
every q[i := 1 to *q] := pix - q[i]
return P
end
A nice feature of Unicon is the WriteImage graphics function. It saves
a canvas image in GIF, JPG, BMP or PNG format (depending on given filename
extension, including system specific types like XBM, XPM). That handy
function is used to generate PlotPairs.png
(and most of the other images)
during builds of this documentation.
prompt$ unicon -s plotpairs.icn -x
Index | Previous: Objects | Next: Database