Tcl/Tk 9 upgrade and additions

Rbc 0.5.0 targets Tcl/Tk 9 on Windows and Linux. This page summarizes the visible additions first, followed by the API, numerical and resource-management changes behind them. Tcl/Tk 8.x compatibility is not a goal of this fork. Build instructions and dependency details are in the documentation overview.

User-visible additions

This fork adds plotting, interaction and numerical features alongside the Tcl/Tk 9 API migration.

Stacked tracks with shared axes

::rbc::trackgraph places line and strip elements in named, weighted rows inside one native widget. Tracks share the horizontal plot bounds and X axes, with independent Y axes, clipping and picking. Existing RBC vector notifications update each track, and SVG, PDF and PostScript exports use the same layout. Line, polygon, arc, text, bitmap and image markers can select a row with -track and -mapy, with clipped drawing, picking and exports. See ::rbc::TRACKGRAPH for the track API, ::rbc::MARKER for track annotations, and demos/trackgraph1.tcl for a working example. track order rearranges existing rows; track sashpos redistributes adjacent heights. Enable -trackresize yes for mouse dragging of the separators, with Escape cancellation, without requiring graphtoolbar. The optional toolbar now supports -type trackgraph: rectangle zoom and pan affect shared X axes and the starting row’s Y axes, wheel zoom follows the pointed row, and coordinate/closest-point annotations stay clipped to their track. Navigation history follows axis names across reordering. Both toolbar and context-menu controls work with the global legend and individual axis scale toggles. Each track can also show a local legend with track legend name configure -hide no. Its entries, bindings and clipping belong to the row; global and local legends can coexist. Graphtoolbar active-legend controls work with both. -invertxy yes arranges tracks left to right with shared vertical X axes and local horizontal Y axes. Weights and -minheight divide the available width; separator dragging becomes horizontal. Marker and legend clipping, picking, toolbar navigation and all exports follow the rotated layout. The merged demos/trackgraph1.tcl demonstrates both legend types, line/strip elements, markers and orientation switching; the example also selects independent grids from its Time and Samples X axes.

Each track can select its own grid axes and style with track grid, for example .g track grid current configure -mapx samples -mapy currentY -hide no. Unconfigured tracks inherit the graph-wide grid; the first successful local change copies the current settings and makes that track independent. Grid queries preserve inheritance. Local grids follow inversion, stay clipped to their tracks, and are included by all exporters.

Optional -tracklayout scroll preserves each track’s requested -height and provides a Tk-compatible track view for navigating the list. The graph’s -trackscrollcommand updates a scrollbar. Local data scrolling is independent: track yview moves the track’s owned Y axes, or one selected with -mapy, using existing axis scroll limits and callbacks. Both orientations, viewport clipping, separator resizing and exports are supported. Fit-to-window remains the default.

Track XOR markers, dividers and decimation

::rbc::TRACKGRAPH line, polygon and arc markers now accept -xor yes, using native XOR within the clipped redraw pass in either renderer. Graphtoolbar zoom outlines can use it too. Optional -trackseparatorcolor and -trackseparatorwidth graph options draw dividers at the separator centres, following rows or inverted columns; the default empty colour keeps them hidden. Line/strip -decimate auto uses the shared X span with the existing eligibility rules and full-detail exports. The fast strip mapping path now attaches its reduced segments to the drawing pen, fixing dense strips disappearing with decimation enabled in both stripcharts and trackgraphs. Regression coverage checks visible extrema, vector updates and picking in both orientations.

Window-marker visibility and track support

Window markers now unmap their live child when hidden, emptied, moved completely outside the plot, or associated with a hidden element, and remap it when visible again. ::rbc::TRACKGRAPH widgets accept window markers with -track and -mapy in either orientation. They follow track placement and resizing, preserving ordinary graph behaviour: partially overlapping children keep their full size and may extend beyond the track. There is no internal window clipping container. See ::rbc::MARKER.

Configurable logarithmic bar baseline

Bar elements honor a positive graph -baseline on logarithmic Y axes, including automatic limits, inverted graphs and exports. Non-positive settings retain the historical logarithmic baseline of 1.0. Stack totals remain additive from zero. See ::rbc::BARELEMENT for details.

Static MinGW-w64 threading runtime

--enable-winpthreads-static embeds the genuine libwinpthread.a archive in the RBC DLL and checks a linked probe for unwanted winpthreads imports. It is independent of --enable-cairo-static; combine both options to embed Cairo dependencies and the threading runtime. The option is off by default and requires a MinGW-w64 toolchain and target objdump. Windows system libraries remain dynamic.

Literal parentheses in vector names

Real and complex vectors accept names such as v(out) directly, with references such as v(out)(1:20) selecting a range. Exact existing names take precedence over range interpretation. Creation retains numeric size/range suffixes; -literal true permits literal names such as v(1). Parenthesized names default to no Tcl array mapping; -variable can select a separate array name without parentheses. Mathematical functions keep expression precedence; braces inside an expression disambiguate a vector name such as {sin(x)}. Public C constructors and lookup APIs support the same names without changing the stubs ABI. See ::rbc::vector for examples and the naming rules.

Tcl-only vector loading

package require rbc::vector loads vectors from the same shared library without initializing Tk or loading argparse and graphtoolbar. Full package require rbc can follow without replacing vectors or client subscriptions; the reverse loading order also works. C consumers can use Rbc_VectorInitStubs with the updated stub library. make test-vector runs the package-loading and public C API tests without a graphical display. Binary dependencies remain required. See ::rbc::vector for details.

Optional toolbar package

package require rbc loads the native widgets without loading argparse or the toolbar. Applications using ::rbc::graphtoolbar now need package require rbc::graphtoolbar, before any namespace import ::rbc::*. The toolbar package requires the matching Rbc version and loads argparse. An installed argparse is preferred; if it is missing, the bundled Tcl-only argparse 0.65 is used, so graph demos need no separate installation. Errors from an installed argparse are still reported. The private fallback is not advertised by the package index and is never loaded by native RBC or the vector core. Repeated requires preserve existing widgets. Toolbar demos, tests, and documentation generation load this optional package explicitly.

Backward-compatible graph helpers

The historical graph.tcl helpers are available again after package require rbc, without requiring argparse. Existing calls such as Rbc_ZoomStack .g and Rbc_Crosshairs .g work on native graphs. These helpers are retained for backward compatibility; new applications should prefer rbc::graphtoolbar. Using both interaction systems on the same graph is not recommended. Pending callbacks and helper state are cleaned up on widget destruction. See GRAPHINST for the complete legacy command list and reserved binding/marker names.

X and Y error bands for lines and strips

Cartesian line and strip elements support -errorband x|y, filling between symmetric -xerror or -yerror bounds, or the corresponding absolute low/high endpoints. The default none disables bands. Independent -errorbandpattern, -errorbandforeground, -errorbandbackground, -errorbandtile, and -errorbandopacity options reuse ordinary area-fill styles and backend limitations. Solid opacity is supported by Cairo and SVG; native rendering and PostScript ignore it. Linear and step bands preserve all source bounds despite symbol thinning or trace decimation and split at invalid data. See ::rbc::LINEELEMENT for details and examples.

Symbols on reduced traces

Line, stripchart, and polar elements accept -symbolpoints all|reduced. The default all preserves original-sample symbols. With positive -reduce, reduced restricts symbols, error bars and value labels to original samples retained by trace simplification; generated smoothing vertices never acquire symbols. Exact closest-point searches still include omitted samples. See ::rbc::LINEELEMENT for details.

A positive -maxsymbols further thins normal symbols, error bars and value labels together on screen and in SVG/PostScript exports. Active symbols and labels bypass this density limit.

MSVC builds on Windows

The win/makefile.vc build supports MSVC and Nmake with Tcl/Tk 9, including optional Cairo via CAIRO=1 and CAIRODIR. It provides build, test, install and shell targets independently of the MSYS2 build. Matching MSVC-built Tcl/Tk source trees and libraries are required; Cairo runtime DLLs and their dependencies must be available when loading RBC. See win/README.md for commands.

Polar/Smith grid configuration

Polar/Smith tick lists, label anchors, label formatter commands, and -smithgrid are now grid component options. Use graphPath grid configure and graphPath grid cget. They are no longer accepted by the graph constructor or graph-level configure/cget. The graph retains -representation and -aspect.

::rbc::polar .p -representation smith
.p grid configure -smithgrid admittance -smithrealmajorticks {0 0.5 1 2}

Option-database patterns likewise include the grid component, for example option add *Polar.grid.AngleMajorTicks {0 90 180 270}. Polar grids are now visible by default, including Smith charts. Grid -hide yes hides both grid lines and their coordinate labels on screen and in exports. The toolbar keeps its -smithgrid constructor convenience option and reads subsequent changes from the grid component. See ::rbc::GRID for full option tables and formatter details.

Optional Cairo rendering

Configure with --enable-cairo to build the antialiased renderer. Cairo-enabled builds default to -renderer cairo; builds without Cairo default to -renderer native. Applications can select either available renderer explicitly or through Tk’s option database.

::rbc::graph .g -renderer cairo -antialias default
.g element create signal -data {0 0 1 2 2 1} -symbol none
.g configure -renderer native

Cairo covers line and strip traces, symbols, error bars, bars, grid and axis lines, ticks, legend symbols, and supported marker geometry, bitmaps and photos. Text glyphs remain native, as do XOR crosshairs, window markers, Tk relief drawing and unsupported image types. Snapshots use the selected renderer; PostScript and Windows metafile output retain their existing exporters.

Cairo improves edge quality but is not a universal speed improvement. Native rendering remains useful for dense geometry; image-heavy workloads can favour Cairo. Windows marker passes share a drawing target, and compatible marker strokes reuse their Cairo context while retaining separate drawing operations.

The automated test runner defaults to native through the option database. Dedicated renderer tests select Cairo explicitly. Manual tests and demos use the compiled default unless they request another renderer. Benchmark scripts have their own explicit -renderer and -antialias settings and CSV comparison tools.

Display decimation and streaming updates

Line and strip elements support -decimate none|auto; none remains the default. Eligible monotonic waveforms retain first/minimum/maximum/last representatives in each physical data-X pixel, preserving narrow extrema rather than averaging them away. Source data and PostScript resolution remain unchanged.

With auto, the activation threshold is 4 source points per plot pixel for Cairo and 8 for native. Renderer changes remap the data. The persistent cache supports axis changes, ranged Y updates and monotonic tail appends, while unsupported data combinations use the full or fallback mapping paths.

Graph interaction and toolbar

graphtoolbar provides zooming, panning, closest-point annotations, legend controls and configurable pointer appearance. Windows touchpad scrolling is supported alongside mouse-wheel zoom. Consecutive axis-scroll actions retain their selected axis when rescaling moves it away from the pointer.

Crosshair annotations refresh beneath a stationary pointer when streaming data or axis mapping changes. Current-coordinate annotations stay beside the pointer; closest-point annotations repeat the search. Annotation boxes are recalculated after zooming, including with inverted axes.

Graph widgets emit <<RbcGraphChanged>> after relevant display changes. The toolbar combines refresh requests into an idle callback and respects temporary suspension during zooming, panning and menus. Windows crosshair motion was corrected to avoid flicker. Symbols crossing the plot boundary are clipped to the visible area instead of disappearing while still partially visible.

Graph widgets also provide <<RbcAxisChanged>> for successful axis configuration or changed effective limits, and <<RbcAxisLimitsChanged>> for numeric limit changes alone. Both queue one list of affected axis names in %d after display, including initial limits. Applications can use these events to synchronize plots or update controls without reacting to unrelated redraws. The broader event includes log/linear switching, tick configuration and styling; synchronization handlers must account for queued feedback.

Custom closest-point annotations

The toolbar’s -closestcommand accepts a Tcl command prefix receiving element x y info and returning annotation text. Configuring it adds Custom to the available formats; -coordclosestmark custom selects it. Empty results suppress the annotation. Ordinary coordinates follow the element’s mapped axes; Polar coordinates are Cartesian components. Smith charts supply normalized admittance for an admittance grid, or normalized impedance for impedance and combined grids. The information dictionary retains the closest-search fields, including the source index.

Built-in closest annotations display the element’s nonempty -label, falling back to its name. Toolbar control mode now uses a Crosshairs menu button instead of two comboboxes, with the same mode and format choices as context control mode. Polar formats include polar (radians) and polardegrees (degrees). Format changes refresh the annotation without requiring another pointer movement.

Complex vectors

The vector subsystem now supports both real and complex numeric vectors. Complex vectors are created explicitly with -type complex, and Tcl-level complex values use the form {real imag}:

::rbc::vector create z -type complex
z set {{1 2} {3 -4} {5 0}}

Complex vectors are supported by most vector instance operations and by the vector expression engine, including arithmetic, complex exponentiation, trigonometric and logarithmic functions, projections such as real, imag, abs, and arg, and complex statistical reductions.

Operations that require an ordering of values remain real-only. In particular, sort and normalize are not supported for complex vectors.

Traditional graph, barchart, and stripchart X/Y element data remain real-valued. Polar elements can attach a complex vector directly through -cdata, and the spline subsystem provides complex-vector parametric natural and quadratic interpolation.

Applications can still explicitly derive real projections such as real, imag, abs, or arg when a traditional Cartesian graph or scalar spline is required.

The public C API was extended with typed vector access, including Rbc_VectorType, Rbc_Complex, Rbc_CreateVectorWithType(), Rbc_VectorComplexData(), Rbc_ResetComplexVector(), and Rbc_VectorChanged(). The original real-vector API remains available for compatibility.

Polar and Smith charts

This fork adds the polar graph widget for plotting Cartesian complex-plane data with either a conventional Polar grid or a Smith-chart representation.

Polar elements accept real lists or vectors through -xdata and -ydata, paired lists through -data, or first-class complex vectors through -cdata. With the default -datacoordinates cartesian, X and Y are real and imaginary components. With -datacoordinates polar, X supplies angles in radians and Y supplies radii; conversion to Cartesian coordinates occurs internally. Source lists and vectors remain unchanged. Polar input coordinates cannot be combined with -cdata or error-bar data options.

Complex data can be interpreted as:

  • reflection coefficient with -cdataformat gamma;

  • physical impedance with -cdataformat impedance;

  • physical admittance with -cdataformat admittance.

The -z0 option specifies the positive reference impedance used for impedance/admittance conversion.

Smith charts support impedance, admittance, or combined contour grids, configurable major/minor normalized contour values, and Tcl callbacks for formatting real and imaginary contour labels.

Polar grids provide independently configurable angular major/minor spokes, angular label callbacks, radial label formatting through the mapped X axis, and configurable label anchoring.

Polar and Smith grid geometry follows the axes selected by grid -mapx/-mapy.

Unless -aspect is explicitly configured, Polar widgets automatically maintain equal physical scale for X and Y data units. This keeps circles circular even when the mapped numerical X/Y ranges have different spans.

Polar grid rendering remains meaningful when the origin is outside the current viewport. Radial circles which intersect the viewport are retained, angular spokes are clipped to the visible Cartesian area, and angular labels move from the normal circular layout to the visible ends of their spokes when no complete centered circle is available.

Radial labels remain tied to the positive zero-degree ray and are displayed whenever their corresponding {radius,0} point is visible, even when the Polar origin itself is outside the viewport.

Polar autoscaling also supplies a neutral fallback range when a grid-mapped axis has no contributing visible element data, allowing the Polar grid to remain usable when the widget is empty or all plotted elements are hidden.

Smith reactive labels likewise adapt to zoomed views. When the complete unit circle is no longer visible, major reactance and susceptance labels are moved to the first visible boundary of their corresponding contour instead of disappearing with their normal unit-circle label position.

Natural/cubic and quadratic smoothing on Polar elements is parametric, allowing vertical paths, non-monotonic-X paths and loops. Catmull-Rom smoothing remains parametric as well.

Missing, non-finite, logarithmically invalid, or singular impedance/admittance points split a trace into continuous runs. Smoothing is performed independently on those runs and never bridges a gap.

element closest on a Polar widget additionally reports radius and angle. The angle field now uses radians in [0, 2*pi), including for Cartesian and complex-vector input; this deliberately replaces the previous degree result. The origin has angle zero, and returned X/Y coordinates remain Cartesian. Polar grid angular labels and their formatting callback continue to use degrees. In Smith representation, closest results also report Gamma and normalized and physical impedance and admittance quantities.

Polar/Smith grid rendering, labels, custom formatter callbacks and smoothed element traces are supported by PostScript output as well as on-screen drawing.

Area closure and transparency

Line and Polar elements support -areaclose baseline|chord|origin. The default baseline retains the bottom-of-plot closure; chord joins the endpoints directly, and origin closes through Cartesian {0 0}. These choices control the fill polygon without adding segments to the visible trace. They apply to solid, stippled and tiled areas, and the same closure geometry is used in PostScript.

Bar elements and bar pens support -fillopacity (default 1.0). Cairo and SVG support partial opacity for solid and stippled bar fills and legend swatches. Zero disables filling on every backend; native drawing and PostScript render nonzero values as opaque. Borders, error bars, and labels remain unaffected.

-areaopacity controls solid fills (-areapattern solid) in Cairo and SVG, from 0.0 (invisible) to 1.0 (opaque, the default). It does not change lines, symbols, stipples or image tiles. Photo tiles retain their own per-pixel alpha, including partial transparency.

Native rendering accepts but ignores -areaopacity, and native image tiles use binary transparency masks. PostScript also ignores the option and retains opaque solid fills; standard PostScript cannot express general alpha blending. These area options are available on line and Polar elements, not bars or strip elements.

Plot-only element hiding

Line, stripchart, and bar elements now support the -hideplot option.

Unlike -hide, which removes an element from the graph display list, -hideplot yes suppresses plotting while leaving the element in the display list. Its legend entry therefore remains available, making it possible for applications to hide and restore plotted data without rebuilding or reordering the legend.

A plot-hidden element is also excluded from automatic axis limits, closest-element searches, element hit testing, and PostScript element output. Plot-hidden bars are additionally excluded from bar grouping and stacking calculations.

-hide and -hideplot are independent, so changing the normal visibility of an element does not alter its -hideplot state.

Circles, ellipses and arc markers

The new arc marker supports full circles/ellipses, open curves, filled chords and pie sectors. -coords {x1 y1 x2 y2} specifies opposite corners of the ellipse bounding rectangle. -start sets the starting angle and -extent the signed sweep in degrees; their defaults are 0 and 360. Positive sweeps run counterclockwise from the display’s positive X direction. The box is mapped before constructing the ellipse, preserving its displayed shape on logarithmic or inverted axes. Equal displayed width and height produce a circle.

-style arc draws the curve only, chord connects the endpoints directly, and pieslice connects them through the centre. The default is pieslice. Full sweeps have no radial seam. Fill and outline colors, foreground/background color pairs, stipples, line width, dashes, caps, joins and XOR follow polygon-marker conventions. Common marker options, clipping, picking and bindings are supported. See ::rbc::MARKER for the complete interface.

.g marker create arc -coords {2 2 6 6} -fill lightblue -outline navy
.g marker create arc -coords {2 2 6 6} -start 30 -extent 120 -style pieslice -fill orange
.g marker create arc -coords {2 2 6 6} -start 30 -extent 120 -style chord -fill lightblue
.g marker create arc -coords {2 2 6 6} -start 30 -extent 120 -style arc -outline red -linewidth 2

The private renderer interface now provides Rbc_RenderArc() and Rbc_RenderFillArc() with a shared mapped ellipse description. Cairo draws curved paths, SVG emits elliptical arc commands, and PostScript emits transformed arc/arcn paths. These backends clip the original curve without adding boundary edges, preserve continuous dashes through clipped portions, and keep stroke widths and stipple patterns independent of the ellipse aspect ratio. SVG and PostScript arc export do not require Cairo.

Native rendering, including XOR, and hit testing retain flattened geometry with a 0.15-pixel chord-error target and an 8192-segment limit. At extreme zoom, picking can differ from the curved path; native dashes can restart after leaving and re-entering the plot.

tests/benchmark/markers.tcl includes separate ellipse-fill, ellipse-outline, open-arc, chord-fill and pieslice-fill workloads in all profiles. They report creation, already-mapped redraw and axis-remap timings for native and Cairo renderers. See tests/benchmark/README.md for case names and commands.

Bar alignment and error-bar positioning

Bar elements now support -baralign center|left|right and -baroffset value. These options control how the full bar or group interval is positioned relative to each data X coordinate. Offsets are expressed in X-axis data units. The defaults, center and 0.0, preserve existing placement.

In aligned mode, alignment applies to the full group width before subdivision into individual bars. Different alignments and offsets within a group are supported in aligned, overlap and stacked modes, allowing gaps, additional overlap or staggered stacks. Group membership still uses the original X coordinates and axis pair.

Bar elements also support -errorbarposition data|bar:

  • data, the default, preserves historical placement: errors use the original data coordinates plus -baroffset in X.

  • bar positions errors at the final bar centre in X and applies the accumulated stacking displacement in Y. Alignment, offsets and group-slot placement are included.

The same translation applies to symmetric errors and explicit low/high bounds, without modifying their source lists or vectors. Translation occurs in data coordinates before axis mapping, and automatic axis limits include the translated error endpoints.

.g element configure first second -baralign left -baroffset 0.25 -errorbarposition bar

Arrowheads for line markers

Line markers now support arrowheads through the -arrow and -arrowshape options.

-arrow accepts none, first, last, or both, allowing an arrowhead to be drawn at either or both ends of a line marker. -arrowshape {a b c} controls the arrowhead geometry using screen distances.

.g marker create line -name direction -coords {0 0 5 4 10 2} -outline red -linewidth 2 -arrow last

.g marker configure direction -arrow both -arrowshape {8 10 3}

Arrowheads use the line marker’s -outline color, and the line shaft is shortened to the arrowhead neck so that it does not extend through the filled arrowhead.

The drawing implementation was also corrected for X11/Linux. Arrowheads are concave polygons, so they are now passed to the native polygon renderer with the correct non-convex geometry and without the duplicate closing vertex. This keeps arrow rendering consistent across supported platforms.

Bar geometry from element closest

The element closest operation now provides additional geometry information when the selected element is a bar.

In addition to the normal name, index, x, y, and dist entries, the result array contains:

  • left

  • top

  • right

  • bottom

These values are the physical widget coordinates of the final mapped bar rectangle that is actually drawn. They therefore reflect the current bar mode: stacked bars return the selected stacked segment, while aligned and overlapping bars return the adjusted rectangle belonging to the selected element.

The values remain physical widget coordinates when -invertxy is enabled: left and right always describe physical X coordinates, while top and bottom always describe physical Y coordinates.

This makes element closest directly usable for bar highlighting, overlays, annotations, and interactive interfaces without requiring applications to reconstruct bar geometry themselves.

Custom element value labels

Value labels accept -valueoffset {dx dy} on elements and pens. Signed integer pixel offsets move the label anchor right/down, independently of axis inversion and text rotation. The default {0 0} preserves existing placement in both renderers and PostScript.

Elements and named pens now accept -valuecommand, a Tcl command prefix receiving the graph pathname, element name, source index, X and Y. Its result replaces the -showvalues label on screen and in PostScript. Empty results hide individual labels; clearing the option restores -valueformat.

Vector polyline simplification

rbc::vector::simplify x y destX destY tolerance (also rbc::vector simplify) reduces a polyline into two existing real vectors using Douglas-Peucker simplification. Tolerance is a Euclidean distance in data coordinates, measured to finite replacement segments. Endpoints and point order are retained; in-place output is supported without replacing vector identities or graph bindings. This headless operation is independent of graph rendering decimation. See ::rbc::vector simplify and the interactive demos/simplify1.tcl example.

Vector FFT and inverse FFT

Real and complex vectors provide fft and ifft instance operations. Results are written into an existing complex vector or two existing real vectors. Vector types and C identities remain fixed; compatible input/output overlap is supported. The full spectrum uses the conventional negative forward sign and a 1/N-normalized inverse. Power-of-two lengths are required, with explicit -length auto|size zero padding. No external FFT dependency or Tk initialization is needed. See ::rbc::VECINST for syntax, frequency-bin ordering, normalization and sampling requirements.

FFT frequency bins and windows

Existing real vectors provide fftfreq length ?-delta interval? ?-onesided boolean? and window type length ?-periodic boolean?. Frequency axes match full FFT ordering or its nonnegative half. Window types are rectangular, Hann, Hamming, Blackman and Bartlett; periodic form is the default, with symmetric form available explicitly. Generate windows at the original sample count before padding, and frequency bins at the actual transform length. Neither operation creates vectors or changes types. See ::rbc::VECINST for formulas, examples and amplitude/energy normalization.

Implementation modernization

The migration replaces obsolete Tcl/Tk interfaces, widens collection sizes and indexes, and improves configuration rollback, numerical boundaries and resource lifetime management.

Tcl 9 command and object API

  • Converted command implementations to the Tcl 9 object-based command interface.

  • Updated command callbacks to use Tcl_Size argument counts where appropriate.

  • Migrated command creation to Tcl_CreateObjCommand2() for Tcl 9 large-argument support.

  • Replaced remaining old string-based command and result handling with Tcl_Obj-based APIs.

  • Removed obsolete command parsing infrastructure inherited from the original BLT/Rbc implementation.

  • Reworked operation dispatch tables and argument validation around object-based parsing.

  • Improved const-correctness throughout Tcl-facing APIs and removed casts that previously discarded const.

  • Removed several local copies or emulations of Tcl internal parsing structures and functions.

64-bit and Tcl_Size support

A large part of the original code assumed that collection sizes, vector lengths and indexes fit in an int. These assumptions were systematically removed.

  • Converted vector lengths, indexes and capacities to Tcl_Size.

  • Converted spline point counts and interpolation indexes to Tcl_Size.

  • Converted graph element point indexes and data-index mappings to Tcl_Size.

  • Converted error-bar index mappings and element-to-data mappings to Tcl_Size.

  • Updated sorting, searching, closest-point and line-reduction code for large indexes.

  • Updated chain/list traversal code where sizes or indexes could exceed the old 32-bit assumptions.

  • Fixed vector binary-reading and vector mathematical operations for Tcl 9 size semantics.

  • Added overflow checks before converting between Tcl_Size, size_t, integers and allocation sizes.

  • Added allocation-overflow guards for dynamically sized arrays throughout graph, spline and vector code.

This removes a large class of silent truncation and overflow problems that were possible with large datasets.

Modern Tk option handling

The graph subsystem previously relied heavily on the legacy Tk configuration API. Most of it has been replaced with the modern Tk option-table interface.

The following subsystems were migrated to Tk_OptionSpec/Tk_OptionTable based configuration:

  • graph widgets

  • axes

  • legends

  • grid

  • crosshairs

  • pens

  • markers

  • line elements

  • stripchart elements

  • bar elements

  • PostScript options

Legacy configuration tables and their associated processing functions were removed after the new implementations were activated.

Custom option types were added or rewritten for complex Rbc values such as:

  • vectors

  • axes

  • pens

  • tags

  • element styles

  • states

  • coordinates

  • padding and margins

  • format strings

  • graph-specific enumerations

Configuration processing was also made transactional where needed. Invalid option changes now preserve the previous valid object state instead of leaving partially modified graph objects.

The EPS canvas item remains on Tk_ConfigSpec/Tk_ConfigureWidget() because the public Tk 9 Tk_ItemType canvas-item interface still exposes the legacy configuration mechanism.

Component option-database lookup

Graph components such as elements, markers, pens, axes, the legend, grid, crosshairs, and PostScript settings are not ordinary Tk child widgets, but they participate in Tk’s option database using component resource names and classes.

The previous implementation created and destroyed a temporary Tk child window whenever a pseudo-component was initialized so that Tk_InitOptions() could perform the appropriate name/class lookup. With large numbers of objects, particularly markers or elements, this made component creation dominated by Tk window-management overhead.

Each graph now owns a single anonymous Tk option-proxy window which is reused for pseudo-component option lookup. Before initializing a component, the proxy is temporarily given the component’s resource name and class and is then passed to Tk’s normal option-table machinery.

This preserves the existing option-database hierarchy and precedence, including both class-wide resources such as:

option add *Graph.TextMarker.Foreground darkgreen

and resources for particular named components such as:

option add *Graph.annotation.foreground purple

without constructing a real Tk child for every component.

The proxy is anonymous and is not entered into Tk’s pathname hierarchy. It exists only for the lifetime of its graph and is destroyed with the graph. Explicit component options continue to override option-database defaults normally.

Removing the temporary-window creation path substantially reduces the cost of creating large numbers of graph objects while retaining the established option-database behavior.

Graph object and resource lifetime

Configuration modernization exposed a number of ownership and rollback problems that were corrected at the same time.

  • Reworked creation, configuration and destruction paths for graph components.

  • Improved rollback after failed configurations.

  • Corrected ownership of dynamically allocated option values.

  • Fixed temporary Tk image creation and destruction.

  • Corrected resource cleanup for legends, markers, pens and elements.

  • Hardened vector mathematical operations against ownership and aliasing problems.

  • Removed dead initialization and cleanup paths inherited from the old configuration system.

  • Removed substantial amounts of obsolete and unreachable compatibility code.

Axis and coordinate-system modernization

The axis implementation received extensive numerical and behavioral cleanup.

  • Hardened coordinate mapping against integer overflow and extreme coordinate ranges.

  • Widened intermediate geometry calculations before converting to Tk/X11 integer coordinates.

  • Added safe handling for rotated-axis dimensions.

  • Added validation for non-finite configuration values.

  • Hardened generated tick calculations against invalid or degenerate ranges.

  • Added guards against ill-defined tick spacing and numerical overflow.

  • Corrected axis scrolling and -scrollcommand behavior.

  • Corrected tick-label command callback handling.

  • Improved option validation and rollback for axis configuration.

  • Hardened user-controlled formatting options and removed unsafe formatting operations.

Logarithmic axes were also substantially corrected:

  • Logarithmic axes now use well-defined positive-domain semantics.

  • Invalid/non-positive points are excluded instead of being mapped through invalid logarithms.

  • Line elements are split at invalid log-domain points instead of drawing across discontinuities.

  • Bar charts correctly handle logarithmic axis mapping.

  • Stacked bars were corrected for logarithmic scales.

  • Extreme logarithmic ranges are handled more safely.

  • Smoothing no longer bridges invalid log-domain gaps.

Line, bar and element processing

The internal representation and processing of graph elements were extensively updated.

  • Modernized element option handling.

  • Reworked vector association using object-based APIs.

  • Updated element state, tag, pen, style and axis handling.

  • Fixed error-bar mapping and data-index tracking.

  • Corrected closest-point and closest-segment calculations.

  • Corrected element binding/tag behavior.

  • Fixed line-reduction routines and their index handling.

  • Hardened construction of mapped point arrays and drawing traces.

  • Replaced internal uses of narrow X11 rectangle structures where larger internal coordinates were required.

  • Added explicit handling of NaN and infinite values in plotted data.

  • Corrected bar handling of non-finite data.

  • Corrected several element formatting and -valueformat paths.

  • Fixed step-generation behavior.

  • Corrected line -trace behavior.

  • Added optional screen-density decimation for large line and strip elements through -decimate none|auto.

  • Automatic line and strip decimation preserves the first, minimum, maximum, and last source representatives associated with each physical data-X pixel rather than averaging dense waveform data.

  • Added density-based selection so sparse data continues through the normal mapping path while dense eligible traces can avoid mapping and drawing millions of redundant screen points.

  • Added a persistent data-domain decimation cache for finite, monotonic-X line and strip data. The cache survives ordinary axis remapping and supports increasing, decreasing, and equal X coordinates as well as -invertxy and descending axes.

  • Added conservative fallback paths for discontinuities, logarithmically invalid data, non-monotonic paths, symbols, value labels, error data, multiple styles, and other combinations that require full source geometry.

  • Added incremental maintenance of persistent line and strip decimation summaries for ranged Y-vector updates and monotonic tail growth.

  • Vector append and index ++end now provide exact ranged tail notifications, allowing persistent display caches and cached real-vector extrema to be extended without rescanning the unchanged source prefix.

  • Pure tail growth can preserve the decimation cache even when vector capacity growth relocates its backing storage. Shrinkage, changes inside the cached X prefix, non-monotonic growth, arbitrary storage replacement, and unknown/full changes retain conservative invalidation semantics.

  • Accelerated exact ELEMENT closest -interpolate no searches for decimated monotonic-X lines while preserving exact source-point results.

  • Screen-density decimation affects window rendering only. PostScript output continues to use full source resolution.

Spline subsystem

The spline subsystem was subsequently extended with direct complex-vector parametric interpolation.

  • Added spline parametric natural source result samples.

  • Added spline parametric quadratic source result samples.

  • Parametric source and result vectors are first-class complex Rbc vectors.

  • Parametric splines use cumulative two-dimensional chord length and do not require either Cartesian coordinate to be monotonic.

  • Vertical paths, reversing paths and loop-like trajectories are supported.

  • Consecutive duplicate source points are rejected because they create a zero-length parameter interval.

  • Parametric generation is transactional and supports source/result aliasing.

  • The original scalar real-vector spline interface remains unchanged.

Vector subsystem

Vector handling was updated substantially beyond the minimum required for compilation with Tcl 9.

  • Added Tcl 9 sized vector indexing.

  • Converted sorting indexes to Tcl_Size.

  • Removed global state from vector sorting operations.

  • Updated vector arithmetic and mathematical operations.

  • Corrected vector duplication and overwrite behavior.

  • Corrected binary-read operations.

  • Hardened allocation and capacity calculations.

  • Improved parsing and const-correctness.

  • Removed legacy parser dependencies.

  • Improved ownership of temporary data used by mathematical operations.

  • Added stricter handling of invalid and non-finite input.

  • Added first-class complex vectors, complex expression arithmetic and mathematical functions, typed public C API access, and precise ranged in-place update notification while retaining compatibility with the existing real-vector interface.

  • Added cached real-vector range maintenance so repeated clients do not rescan unchanged vectors merely to obtain minimum and maximum values.

  • Added ranged vector-change notifications. Mapped Tcl-array writes and ordinary vector index writes report the affected source interval when an existing real-vector range is modified in place.

  • Added Rbc_VectorChangedRange(Rbc_Vector *, Tcl_Size first, Tcl_Size last) to the public C/stubs API so C producers can report precise in-place source modifications without changing vector storage or ownership.

  • Ranged real-vector updates maintain cached extrema incrementally when possible and conservatively fall back to a complete range scan when a modified interval invalidates an existing global extremum.

  • Retained Rbc_VectorChanged() as the full/unknown-range notification interface for callers that cannot provide a precise changed interval.

Geometry, text and drawing

Many drawing routines inherited assumptions from old X11 APIs where coordinates and dimensions were commonly stored in narrow integer types.

The modernization:

  • widened internal geometry calculations;

  • added checked conversion at native drawing boundaries;

  • hardened rotated text and bounding-box calculations;

  • updated text-layout processing;

  • hardened shadows, halos and graph-layout coordinate arithmetic;

  • corrected clipping and projection calculations;

  • updated polygon, rectangle and region processing;

  • added guards around fixed-size polygon and symbol allocations;

  • corrected point-in-region and projection helpers;

  • fixed drawing-boundary issues discovered on both Windows and Linux.

The intent is to keep high-precision or wide internal calculations for as long as possible and narrow them only when calling APIs that inherently require integer coordinates.

Image and bitmap handling

The image-processing code was significantly updated for current Tk behavior.

  • Updated photo-image access and processing.

  • Corrected temporary image lifetime management.

  • Updated bitmap rotation and scaling code.

  • Unified bitmap-region operations across platforms.

  • Corrected Unix image handling.

  • Corrected Windows bitmap representation and mask generation.

  • Hardened bitmap allocation and size calculations.

  • Isolated native bitmap-handle access behind Rbc Windows helper functions.

Named-font changes

Graph widgets now register Tk’s world-change callback. Reconfiguring a named font refreshes cached text-marker geometry, title and axis layout, legends and value-label drawing resources, including external legends and named/active pens. Redraws are scheduled through the normal idle mechanism. Explicit pixel sizes and configured axis limits are preserved. See Named fonts and display scaling.

PDF output

Graph, barchart, stripchart and polar widgets support pdf cget, pdf configure and pdf output. The exporter writes single-page PDF 1.4 directly through the shared rendering abstraction, without Cairo or an external converter. Paths, arcs, symbols, clipping, transparency, images and window snapshots are supported. TrueType and ordinary OpenType/CFF fonts are embedded with Unicode maps for searching/copying, including mathematical superscripts/subscripts and fallback characters. Font lookup follows Tk 9 font attributes using GDI on Windows and Fontconfig on X11. PDF -embedfonts defaults to true; disabling it selects standard Helvetica/Times/Courier fonts and Windows-1252 text, with an error for unsupported characters. SVG offers the same option, defaulting to false. Enabling it embeds native fonts and fallback faces as CSS webfonts while preserving Unicode text; viewers must support CSS webfonts and data URLs. Unsupported or restricted fonts and missing glyphs report an error. Output is a binary file or Tcl bytearray. See ::rbc::PDF.

SVG output

Graph widgets now provide svg cget, svg configure, and svg output, with independent width, height and decoration settings. SVG export uses vector geometry and editable text and does not require Cairo. Solid area opacity and photo-marker alpha are preserved. Photos are embedded as PNG; bitmap markers and symbols use vector shapes. Stipple fills use vector patterns, and photo area tiles embed repeating PNG images with alpha. Non-photo Tk images are rasterized with recovered transparency; mapped window markers embed native pixel snapshots. Capture failures report an error before opening a file. See ::rbc::SVG for commands, options and capture limitations.

PostScript output

The PostScript subsystem was modernized and hardened alongside the graph code.

PostScript drawing now passes through the common renderer interface: strokes, fills, symbols, text, images, bitmap masks, window snapshots, relief borders, plot backgrounds and clipping. This includes normal/active elements, markers, legends, axis labels, and polar/Smith labels. The existing PostScript backend, prolog, font/color mapping and public command are retained; export does not require Cairo. Page setup, orientation, EPS metadata and file output remain in the PostScript subsystem. Screen text rendering is unchanged.

This is an internal refactoring, not a new export format. PostScript still ignores area opacity; image-tiled line areas export only their configured background. Existing bitmap masks, drawing order and window-snapshot fallback behavior are preserved.

  • Updated PostScript option handling.

  • Updated image generation and bitmap handling.

  • Corrected PostScript writer behavior.

  • Replaced unsafe formatting paths.

  • Hardened formatting and buffer handling.

  • Updated coordinate and size handling for the modernized graph structures.

Windows drawing backend

The Windows drawing implementation received a substantial cleanup.

Obsolete compatibility code and dead drawing implementations were removed, and the remaining native drawing code was reorganized behind an Rbc-owned Windows abstraction.

Rbc code outside the Windows backend no longer accesses Tk’s private Windows drawable structures directly.

Operations such as:

  • acquiring and releasing a drawable HDC;

  • extracting an HBITMAP from a Tk pixmap;

  • wrapping an HBITMAP as a pixmap;

  • wrapping a native HDC;

  • mapping an HWND to the required drawable representation;

  • setting Windows GDI raster operations

are now isolated behind Rbc_Win* helper functions.

This reduced the former widespread dependency on:

  • TkWinDrawable

  • TkWinBitmap

  • TkWinDC

  • TkWinDCState

  • TkWinGetDrawableDC()

  • TkWinReleaseDrawableDC()

  • TWD_*

to a single Windows implementation unit, rbcWinDraw.c.

Windows rendering performance

The Windows graph renderer was also optimized to reduce repeated GDI setup for large drawing workloads.

Several older drawing paths performed native device-context, pen, brush, or bitmap setup separately for every individual primitive. This was particularly expensive for large scatter plots, bar charts, error bars, and collections of graph markers.

The Windows backend now batches or shares native drawing state where the original drawing semantics permit it:

  • independent line and error-bar segments are grouped into PolyPolyline() calls;

  • polygon-based symbols use batched native polygon and polyline drawing;

  • square and circle symbol rendering uses dedicated Windows fast paths;

  • raised bars share an acquired drawable HDC and precomputed 3-D border colors instead of repeatedly entering Tk’s per-rectangle drawing path;

  • opaque stippled bars use a reusable monochrome pattern brush and PatBlt() instead of creating temporary bitmap state for every rectangle;

  • compatible consecutive line-marker and polygon-outline markers are combined into segment batches while preserving marker display-list order;

  • solid fill-only polygon markers share their destination HDC and brush across a compatible run;

  • compatible bitmap markers share both source and destination device-context setup;

  • compatible text-marker runs reuse an acquired destination HDC while continuing to use Tk’s font renderer.

The fast paths are deliberately conservative. XOR drawing, dashed or otherwise incompatible line styles, marker arrowheads, masked or rotated bitmap cases, mixed polygon fill/outline cases, and other operations whose semantics depend on individual drawing order continue through the normal drawing paths.

Batching never reorders visible graph objects. A pending batch is flushed whenever an incompatible object or another visible marker class is encountered, preserving the graph’s existing stacking and overlap semantics.

These changes affect the Windows native renderer only. The X11 drawing paths and PostScript output retain their existing behavior.

Remaining private Tk dependencies

Complete elimination of private Tk interfaces is currently not practical without either changes in Tk itself or a significant drawing-backend redesign.

The remaining dependencies are deliberately isolated:

  • rbcText.c uses Tk’s internal TkDrawAngledChars() functionality for rotated text. Tk does not currently provide equivalent functionality through its public API.

  • rbcWinDraw.c uses tkWinInt.h to bridge between Tk drawables and native Windows GDI objects.

No other graph, element, image or general Rbc code needs to know about Tk’s Windows-private drawable structures.

Compiler diagnostics caused by known attribute mismatches inside the private Tk headers are suppressed only around those specific includes; warnings remain enabled for Rbc’s own source code.

C safety and hardening

The Tcl/Tk 9 work also included a wider C safety audit.

  • Replaced unsafe sprintf() usage with bounded or object-based formatting.

  • Added validation for user-controlled numeric formatting strings.

  • Added multiplication and allocation-overflow checks.

  • Added range checks before narrowing floating-point or wide integer values.

  • Added explicit handling of NaN and infinity.

  • Removed undefined or questionable integer-overflow paths.

  • Improved const correctness.

  • Removed mutations through pointers to constant data.

  • Fixed uninitialized index/data-mapping cases.

  • Removed obsolete K&R-era and compatibility code where encountered.

  • Removed dead #ifdef notdef and otherwise unreachable implementations where they no longer served a purpose.

Bindings and tags

The graph binding infrastructure was corrected and expanded during the migration.

  • Fixed axis bindings.

  • Fixed element bindings.

  • Fixed marker bindings.

  • Fixed legend binding activation.

  • Corrected graph tag generation and lookup.

  • Updated bind-tag handling after the option-system migration.

  • Added regression tests for the affected binding paths.

Regression testing

The modernization was accompanied by a substantial expansion of the test suite.

New regression coverage was added for areas including:

  • option configuration and rollback;

  • axes and axis bindings;

  • markers;

  • element options and bindings;

  • legends;

  • vectors and vector arithmetic;

  • splines;

  • non-finite values;

  • logarithmic axes;

  • bar charts;

  • PostScript output;

  • bitmap and image handling;

  • formatting options;

  • error bars;

  • closest-point calculations;

  • coordinate and integer-boundary cases.

Manual graph tests and demos were also repeatedly exercised during the migration.

The resulting code has been tested on both Windows and Linux with Tcl/Tk 9.x.

Remaining compatibility interfaces

The remaining use of legacy or private Tk interfaces is intentional:

  • the EPS canvas item uses the configuration API still required by Tk’s public custom-canvas-item interface;

  • rotated text uses Tk’s unsupported angled-text API;

  • the Windows drawing adapter uses Tk’s private drawable representation.

These remaining interfaces can only be removed cleanly if equivalent public Tk APIs become available or if the relevant rendering code is replaced with an independent backend.


Copyright (c) George Yashin