wx_BGI_Graphics
Classic BGI-compatible graphics API with modern OpenGL extension API
Loading...
Searching...
No Matches
Visual Aids

The Visual Aids system provides non-DDS overlays for camera/UCS workflows. These overlays are designed for orientation, scale awareness, and interactive selection feedback without changing the DDS scene data.

Key behavior:

  • Visual Aids are drawn fresh each frame and are not stored in DDS.
  • They are not serialized to DDJ/DDY.
  • They are not selectable as DDS objects.
  • They are disabled by default to keep legacy behavior unchanged.
  • They are rendered automatically during wxbgi_render_dds() in GLFW mode.
  • In wxWidgets mode they are rendered via wxbgi_wx_render_overlays_for_camera() in the WxBgiCanvas::PostBlit() hook, after 3-D solid geometry, ensuring visual aids are always in front of all scene content.

Primary API header: src/wx_bgi_overlay.h


Visual Aid Types

1) Reference Grid (global)

Draws grid lines in the UCS XY plane and projects them through every camera viewport. The grid is visible simultaneously in all panels when enabled. BGI viewport clipping applies so only the portion inside each panel is drawn.

  • Enable/disable and query:
    • wxbgi_overlay_grid_enable()
    • wxbgi_overlay_grid_disable()
    • wxbgi_overlay_grid_is_enabled() -> 1 / 0
  • Configuration:
    • wxbgi_overlay_grid_set_spacing(worldUnits) – distance between lines (> 0)
    • wxbgi_overlay_grid_set_extent(halfExtentInLines) – lines each side of origin (>= 1)
    • wxbgi_overlay_grid_set_colors(xAxisColor, yAxisColor, gridColor)
    • wxbgi_overlay_grid_set_ucs(ucsName)NULL/"" = active UCS

Defaults:

Field Default
Spacing 25 world units
Half extent 4 lines each side
X axis color RED
Y axis color GREEN
Grid line color DARKGRAY
UCS binding active UCS

2) UCS Axes (global)

Draws labeled X (RED), Y (GREEN), Z (BLUE) axis arms in every camera viewport for orientation. Both the world UCS origin and the active UCS origin can be shown simultaneously.

  • Enable/disable and query:
    • wxbgi_overlay_ucs_axes_enable()
    • wxbgi_overlay_ucs_axes_disable()
    • wxbgi_overlay_ucs_axes_is_enabled() -> 1 / 0
  • Configuration:
    • wxbgi_overlay_ucs_axes_show_world(show) – show world-origin axes
    • wxbgi_overlay_ucs_axes_show_active(show) – show active-UCS axes
    • wxbgi_overlay_ucs_axes_set_length(worldUnits) – arm length (> 0)

Defaults:

Field Default
World axes shown
Active UCS axes shown
Axis length 80 world units

3) Concentric Circles + Crosshair (per-camera)

Draws concentric world-space circles centered on the camera pan/target point. Because radii are in world units, the visible pixel spacing changes with zoom, providing immediate scale feedback.

  • 2D cameras: crosshair rotates with the camera rotation angle.
  • 3D cameras: crosshair is fixed (upward on screen).
  • Enable/disable and query:
    • wxbgi_overlay_concentric_enable(camName)NULL/"" = active camera
    • wxbgi_overlay_concentric_disable(camName)
    • wxbgi_overlay_concentric_is_enabled(camName) -> 1 / 0
  • Configuration:
    • wxbgi_overlay_concentric_set_count(camName, count) – clamped to 1..8
    • wxbgi_overlay_concentric_set_radii(camName, innerRadius, outerRadius)

Defaults:

Field Default
Circle count 3
Inner radius 25 world units
Outer radius 100 world units

4) Selection Cursor Square (per-camera)

Draws a small square that tracks the mouse inside a camera's viewport. The square is drawn in the OpenGL pass (after the BGI pixel buffer) so it is always visible on top of scene content.

  • Border alternates every 2 seconds between a light and dark shade of the chosen color scheme.
  • Color schemes: 0 = blue (default), 1 = green, 2 = red.
  • Selection is triggered by a left-click anywhere inside the camera viewport.
  • Enable/disable and query:
    • wxbgi_overlay_cursor_enable(camName) – also activates click-to-select
    • wxbgi_overlay_cursor_disable(camName)
    • wxbgi_overlay_cursor_is_enabled(camName) -> 1 / 0
  • Configuration:
    • wxbgi_overlay_cursor_set_size(camName, sizePx) – clamped to 2..16
    • wxbgi_overlay_cursor_set_color(camName, colorScheme)

Default size: 8 px


Selection System

The selection system lets the user pick DDS drawing objects by left-clicking inside any camera viewport that has the Selection Cursor Square overlay enabled. Selected objects flash in an alternate color each frame.

Click Behavior

Action Result
Left-click on an object Clear selection; select the clicked object
Left-click on an already-selected object Deselect it (selection becomes empty)
Left-click on empty space Clear selection
CTRL+click on an object Add to existing selection
CTRL+click on already-selected object Remove it from selection
CTRL+click on empty space Selection unchanged

Depth Picking

When several objects overlap on screen the one closest to the camera eye is picked. Objects within a 0.5 world-unit depth band of each other are resolved by screen-space distance to the click point. All DDS drawing-object types are supported:

  • 2D primitives: line, circle, arc, rectangle, ellipse, text
  • 3D world/UCS draw functions: world line, world circle, world arc, world rectangle, world ellipse
  • 3D solids: sphere, cylinder, cone, torus (Sutherland-Hodgman projected outline)
  • Parametric surfaces: sphere, cylinder, torus, saddle, Mobius (formula-aware probe points)

Flash Visual Feedback

Selected objects are drawn each frame with a color override that alternates between an orange and a darker orange tone (scheme 0), or purple/dark-purple (scheme 1).

  • 3D solids: solidColorOverride in BgiState overrides tri.faceColor / tri.edgeColor in renderTriangleBatch.
  • All other primitives: colorOverride in renderObject replaces gState.currentColor / gState.fillColor.

Flash rate: 1 Hz (1-second on/off toggle based on glfwGetTime()).

Selection API

int wxbgi_selection_count(void);
const char *wxbgi_selection_get_id(int index); // 0-based; valid until next mutation
void wxbgi_selection_clear(void); // deselect all (no DDS change)
void wxbgi_selection_delete_selected(void); // remove from DDS + clear selection
void wxbgi_selection_set_flash_scheme(int scheme); // 0=orange (default), 1=purple
void wxbgi_selection_set_pick_radius(int pixels); // default 16, clamped 2..64

Iterating selected objects:

for (int i = 0; i < wxbgi_selection_count(); ++i) {
const char *id = wxbgi_selection_get_id(i);
printf("selected: %s\n", id);
}

Minimal Setup Example

#include "wx_bgi.h"
#include "wx_bgi_3d.h"
#include "wx_bgi_dds.h"
#include "wx_bgi_overlay.h"
initwindow(1280, 720, "Visual Aids", 0, 0, 1, 1);
/* Global aids */
wxbgi_overlay_grid_enable();
wxbgi_overlay_grid_set_spacing(25.0f);
wxbgi_overlay_grid_set_extent(4);
wxbgi_overlay_ucs_axes_enable();
wxbgi_overlay_ucs_axes_set_length(110.0f);
/* Per-camera aids */
wxbgi_overlay_concentric_enable("cam2d");
wxbgi_overlay_concentric_set_count("cam2d", 3);
wxbgi_overlay_concentric_set_radii("cam2d", 30.0f, 90.0f);
wxbgi_overlay_cursor_enable("cam2d"); /* also activates click-to-select */
wxbgi_overlay_cursor_set_size("cam2d", 12);
wxbgi_overlay_cursor_set_color("cam2d", 0); /* blue */
/* In your render loop */
/* Read selection after each frame */
for (int i = 0; i < wxbgi_selection_count(); ++i)
printf("selected: %s\n", wxbgi_selection_get_id(i));
BGI_API int BGI_CALL initwindow(int width, int height, const char *title, int left, int top, int dbflag, int closeflag)
Creates and initializes a graphics window and context.
Public classic BGI-compatible C API exported by the library.
Camera viewport and 3-D world coordinate extension API.
Public C API for the Drawing Description Data Structure (DDS).
BGI_API void BGI_CALL wxbgi_render_dds(const char *camName)
Renders all visible DDS drawing objects through the named camera into the BGI pixel buffer.

For a full interactive example, see examples/cpp/wxbgi_camera_demo.cpp.


Visual Aids – Code Map

This section describes where every piece of the Visual Aids system lives in the source tree, from data structs through the drawing layer to the public C API.


1. Data – structs in <tt>src/bgi_types.h</tt>

Global overlay state (fields of <tt>BgiState</tt>)

// ---- Global overlays -------------------------------------------------------
struct OverlayGridState
{
bool enabled {false};
float spacing {25.f}; // world units between adjacent lines
int halfExtent {4}; // grid spans +/-(halfExtentxspacing)
int xAxisColor {RED};
int yAxisColor {GREEN};
int gridColor {DARKGRAY};
std::string ucsName; // empty = BgiState::activeUcs
} overlayGrid;
struct OverlayUcsAxesState
{
bool enabled {false};
bool showWorld {true}; // draw world UCS at (0,0,0)
bool showActive {true}; // draw active UCS at its origin
float axisLength {80.f}; // world units origin->tip
} overlayUcsAxes;
// ---- Selection state -------------------------------------------------------
std::vector<std::string> selectedObjectIds; // IDs of currently selected objects
int selectionFlashScheme {0}; // 0 = orange, 1 = purple
int selectionPickRadiusPx{16}; // screen-pixel pick threshold [2..64]
// ---- Solid color override (used by selection flash) -----------------------
int solidColorOverride{-1}; // if >= 0, overrides tri.faceColor/edgeColor
constexpr int GREEN
Definition bgi_types.h:168
constexpr int DARKGRAY
Definition bgi_types.h:174
constexpr int RED
Definition bgi_types.h:170

Per-camera overlay state (anonymous structs inside <tt>Camera3D</tt>)

// Inside Camera3D (src/bgi_types.h) -- not serialised to DDS/DDJ
struct {
bool enabled {false};
int count {3}; // circles: 1..8
float innerRadius {25.f}; // world-unit radius of innermost circle
float outerRadius {100.f}; // world-unit radius of outermost circle
} concentricOverlay;
struct {
bool enabled {false};
int sizePx {8}; // square side length, pixels: 2..16
int colorScheme {0}; // 0 = blue, 1 = green, 2 = red
} selCursorOverlay;

Ownership: Both overlay structs are value members of Camera3D, which is a value member of DdsCamera. They live and die with the camera. They are not serialized – the scene file does not store overlay state.


2. Drawing layer – <tt>src/bgi_overlay.cpp</tt>

Pure drawing routines; no public API symbols. All functions operate under gMutex (the mutex is held by the caller – do not re-acquire inside).

Internal drawing functions (anonymous namespace)

Function What it draws
drawGridOverlay(cam) One grid-line pass for a camera. Iterates rows and columns; each line passes through cameraWorldToScreen and drawLineInternal.
drawUcsAxesOverlay(cam) Six axis arms (+/-X, +/-Y, +/-Z) for world UCS and/or active UCS. Text labels placed at the positive tip via bgi::drawText.
drawConcentricOverlay(camName, cam) Up to 8 concentric circles and a crosshair. Radius stepping: innerRadius + i * (outerRadius - innerRadius) / (count - 1). Each circle drawn with circle() in projected pixel coordinates.

Selection cursor GL pass

void drawSelectionCursorsGL();

Called after the 3-D solid geometry GL pass, so the square always renders on top of solid objects. Uses immediate-mode GL_LINE_LOOP with a pixel-space orthographic projection (glOrtho(0, w, h, 0, -1, 1)) so screen-pixel coordinates are used directly. Does not acquire gMutex (caller holds it).

The blink animation uses std::chrono::steady_clock (not glfwGetTime), so it works correctly in both GLFW-standalone and wxWidgets-embedded modes.

Pick handler

void overlayPerformPick(int screenX, int screenY, bool multiSelect);

Called from mouseButtonCallback in bgi_api.cpp. Logic:

  1. For each camera that has selCursorOverlay.enabled:
    • Check if click coordinates lie inside the camera's screen viewport.
    • For every DDS drawing object, call screenDistToObject() to get the screen-pixel distance and camera depth.
    • Collect all objects within selectionPickRadiusPx pixels.
    • Sort by depth (closest first); break ties by screen distance within 0.5 world-unit depth band.
    • Take the nearest candidate as nearestId.
  2. Update selectedObjectIds according to the toggle rules:
wasSelected = nearestId in selectedObjectIds
if NOT multiSelect:
selectedObjectIds.clear()
if NOT wasSelected:
selectedObjectIds.push_back(nearestId) // select
else if multiSelect:
selectedObjectIds.erase(nearestId) // CTRL+click toggle off
// plain click on already-selected: cleared above -> deselected
  1. Break – first matching camera viewport wins.

Distance probing – <tt>screenDistToObject()</tt>

Returns the minimum screen-pixel distance from a screen point to any part of a DDS object, and sets an output depth (camera-space -Z, positive = in front). Per-type logic:

DDS Object Type Probe method
Line, WorldLine, UcsLine Closest point on the projected line segment
Circle, WorldCircle, UcsCircle Closest point on projected circle perimeter
Arc, WorldArc, UcsArc Sweep 8 arc sample points
Rectangle, WorldRect, UcsRect 4 corners
Ellipse, WorldEllipse, UcsEllipse 8 perimeter points
Text, WorldText Closest point on projected text bounding box
Sphere3D, Cylinder3D, Cone3D, Torus3D Projected outline vertices of the triangle mesh
ParamSurface Formula-aware poles/rim/corner probe points

3. Rendering pass integration – <tt>src/bgi_dds_render.cpp</tt>, <tt>src/bgi_draw.cpp</tt>, <tt>src/bgi_modern_api.cpp</tt>

Visual aids are composited after 3-D solid geometry so they always appear in front. The rendering pipeline uses two texture passes to achieve this:

for each camera in wxbgi_render_dds():
render DDS scene objects into pixel buffer
queue 3-D solid GL geometry (pendingGl)
store camName in gState.pendingOverlayCam
GLFW / flushToScreen() path:
+-- renderPageAsTexture() <- pixel buffer as opaque RGBA texture
+-- drain pendingGl queue <- 3-D solid GL geometry (depth-tested)
+-- [clear pixel buffer; drawOverlaysForCamera()] <- grid, UCS axes, circles
+-- renderPageAsTextureAlpha() <- overlay-only buffer; background = alpha 0
+-- drawSelectionCursorsGL() <- selection cursor (OpenGL pass, on top)
+-- glfwSwapBuffers()
wx / WxBgiCanvas::Render() path (per panel):
+-- PreBlit(w, h) <- cleardevice() + wxbgi_render_dds(camName)
+-- wxbgi_wx_render_page_gl_vp() <- opaque page texture + 3-D solid GL
+-- PostBlit(pageW, pageH, vpW, vpH)
calls wxbgi_wx_render_overlays_for_camera(camName, ...):
+-- clear pixel buffer to background
+-- drawOverlaysForCamera() <- grid, UCS axes, circles
+-- renderPageAsTextureAlpha() <- alpha-blit overlays on top of solids
+-- drawSelectionCursorsGL() <- cursor on top of everything
+-- SwapBuffers()

**renderPageAsTextureAlpha()** (in src/bgi_gl.cpp):

  • Same as renderPageAsTexture() but does not clear the framebuffer.
  • Background-colored pixels in the CPU buffer get alpha = 0; all other pixels get alpha = 255.
  • Enables GL_BLEND(SRC_ALPHA, ONE_MINUS_SRC_ALPHA) so only overlay strokes are composited on top of the existing framebuffer (3-D solids remain visible).

**wxbgi_wx_render_overlays_for_camera()** (in src/bgi_modern_api.cpp, declared in src/wx_bgi_ext.h):

const char *camName,
int pageW, int pageH,
int vpW, int vpH);
#define BGI_CALL
Definition bgi_types.h:20
#define BGI_API
Definition bgi_types.h:14
BGI_API void BGI_CALL wxbgi_wx_render_overlays_for_camera(const char *camName, int pageW, int pageH, int vpW, int vpH)
Composite visual-aids overlays (grid, UCS axes, concentric circles, selection cursor) for a named cam...

Clears the page buffer, draws overlays for the named camera, calls renderPageAsTextureAlpha, then calls drawSelectionCursorsGL. Call from WxBgiCanvas::PostBlit only – the GL context must already be current and the 3-D solid pass must already be complete.

Flash rendering for selected objects:

// In renderObject(), bgi_dds_render.cpp
const bool isSelected = std::find(gState.selectedObjectIds.begin(),
gState.selectedObjectIds.end(), obj.id)
!= gState.selectedObjectIds.end();
int colorOverride = -1;
if (isSelected) {
using clock = std::chrono::steady_clock;
static const auto t0 = clock::now();
const int flashPhase =
(int)std::chrono::duration_cast<std::chrono::seconds>(
clock::now() - t0).count() & 1;
colorOverride = (gState.selectionFlashScheme == 0)
? (flashPhase ? 252 : 214) // orange / dark-orange
: (flashPhase ? 253 : 93); // purple / dark-purple
}
// 2D/world primitives: apply colorOverride to gState.currentColor / fillColor
// 3D solids: set gState.solidColorOverride, call renderSolid3D, clear

4. Public C API – <tt>src/wx_bgi_overlay.h</tt> / <tt>src/bgi_overlay_api.cpp</tt>

All functions are BGI_API / BGI_CALL (C linkage, dllexport on Windows). Each acquires bgi::gMutex, reads/writes gState or a named camera's overlay struct, then releases the mutex.

Reference Grid

Function Description
wxbgi_overlay_grid_enable() overlayGrid.enabled = true
wxbgi_overlay_grid_disable() overlayGrid.enabled = false
wxbgi_overlay_grid_is_enabled() Returns overlayGrid.enabled
wxbgi_overlay_grid_set_spacing(f) Sets overlayGrid.spacing (> 0 only)
wxbgi_overlay_grid_set_extent(n) Sets overlayGrid.halfExtent (>= 1)
wxbgi_overlay_grid_set_colors(x,y,g) Sets X/Y/grid colors
wxbgi_overlay_grid_set_ucs(name) Sets overlayGrid.ucsName

UCS Axes

Function Description
wxbgi_overlay_ucs_axes_enable() overlayUcsAxes.enabled = true
wxbgi_overlay_ucs_axes_disable() overlayUcsAxes.enabled = false
wxbgi_overlay_ucs_axes_is_enabled() Returns overlayUcsAxes.enabled
wxbgi_overlay_ucs_axes_show_world(n) Sets overlayUcsAxes.showWorld
wxbgi_overlay_ucs_axes_show_active(n) Sets overlayUcsAxes.showActive
wxbgi_overlay_ucs_axes_set_length(f) Sets overlayUcsAxes.axisLength (> 0)

Concentric Circles (per-camera, <tt>camName</tt> = <tt>NULL</tt>/<tt>""</tt> -> active camera)

Function Description
wxbgi_overlay_concentric_enable(cam) concentricOverlay.enabled = true
wxbgi_overlay_concentric_disable(cam) concentricOverlay.enabled = false
wxbgi_overlay_concentric_is_enabled(cam) Returns concentricOverlay.enabled
wxbgi_overlay_concentric_set_count(cam, n) count clamped to 1..8
wxbgi_overlay_concentric_set_radii(cam, r1, r2) Sets innerRadius/outerRadius

Selection Cursor (per-camera)

Function Description
wxbgi_overlay_cursor_enable(cam) selCursorOverlay.enabled = true (also activates click-to-select)
wxbgi_overlay_cursor_disable(cam) selCursorOverlay.enabled = false
wxbgi_overlay_cursor_is_enabled(cam) Returns selCursorOverlay.enabled
wxbgi_overlay_cursor_set_size(cam, px) sizePx clamped to 2..16
wxbgi_overlay_cursor_set_color(cam, s) colorScheme: 0=blue, 1=green, 2=red

Selection

Function Description
wxbgi_selection_count() Number of currently selected objects
wxbgi_selection_get_id(i) ID of the i-th selected object (0-based)
wxbgi_selection_clear() Deselect all (no DDS change)
wxbgi_selection_delete_selected() Remove selected objects from DDS + clear
wxbgi_selection_set_flash_scheme(s) 0 = orange (default), 1 = purple
wxbgi_selection_set_pick_radius(px) Pick radius clamped to 2..64 (default 16)

wxWidgets Overlay Helper (<tt>src/wx_bgi_ext.h</tt> / <tt>src/bgi_modern_api.cpp</tt>)

Called from WxBgiCanvas::PostBlit only – composites overlays in front of 3-D solid GL geometry within an already-current GL context.

Function Description
wxbgi_wx_render_overlays_for_camera(camName, pageW, pageH, vpW, vpH) Clears CPU buffer → draws overlays for camName → alpha-blits buffer onto the GL framebuffer → draws selection cursor

wx_bgi_overlay.h (public declarations -- include in user code)
|
v
bgi_overlay_api.cpp (public C API implementation: lock gMutex, mutate gState)
|
+---> bgi_overlay.h/cpp (internal: drawOverlaysForCamera, drawSelectionCursorsGL,
| overlayPerformPick, screenDistToObject)
| |
| +---> bgi_camera.h/cpp (cameraWorldToScreen, cameraEffectiveViewport)
| +---> bgi_draw.h/cpp (drawLineInternal, drawCircleInternal, stampPixel)
| +---> bgi_font.h/cpp (drawText for axis labels)
| +---> bgi_ucs.h/cpp (ucsLocalToWorldMatrix for grid UCS transform)
|
+---> bgi_types.h (OverlayGridState, OverlayUcsAxesState, Camera3D overlay structs,
BgiState::selectedObjectIds / selectionFlashScheme / selectionPickRadiusPx
BgiState::pendingOverlayCam)
bgi_gl.h/cpp (renderPageAsTexture, renderPageAsTextureAlpha)
bgi_dds_render.cpp (sets pendingOverlayCam; renderObject flash logic)
bgi_api.cpp (mouseButtonCallback -> overlayPerformPick)
bgi_draw.cpp (flushToScreen -> renderPageAsTextureAlpha + drawSelectionCursorsGL
after 3-D solid pass)
bgi_modern_api.cpp (wxbgi_wx_render_overlays_for_camera -- wx PostBlit helper)
wx_bgi_canvas.cpp (WxBgiCanvas::Render calls PostBlit; PostBlit calls
wxbgi_wx_render_overlays_for_camera)
wx_bgi_ext.h (wxbgi_wx_render_overlays_for_camera public declaration)

See also