# Copyright (c) 2025-2026 Gigasoft, Inc. All rights reserved. === ProEssentials Real-Time (knowledge rev 4.1) Patterns === Real-time charting applies to Pego, Pesgo, Pepso, and Pe3do. Each has different data models and refresh needs. Complexity compounds when using Direct3D vs Direct2D. Always query pe_query.py for paths. --- ESSENTIAL SETUP (all real-time, all objects) --- PeConfigure.PrepareImages = true; // standard for all charts PeConfigure.CacheBmp = true; // standard for all charts These are standard initialization for any chart but critical for real-time -- they cache static elements (titles, axes, legend) so only the graph area redraws each tick. PeSpecial.AutoImageReset = false; // important for real-time AutoImageReset is an internal safety feature: when true, if any property changes, the chart flags itself dirty and auto-rebuilds on the next WM_PAINT. Setting false skips that check. Safe when you always call ReinitializeResetImage or ResetImage explicitly after changing properties (which real-time code always does). Manual axis scaling is strongly recommended for real-time. Auto- scaling requires the chart to search all data for min/max each update -- CPU intensive, especially with large datasets. --- REAL-TIME UPDATE STRATEGIES --- STRATEGY 1 -- APPENDDATA (strip chart, most common) Shifts existing data and adds new at the edge. Direction controlled by PeData.AppendToEnd (true=append right, false=append left). Works with: Pego, Pesgo, Pe3do. Call: PeData.Y.AppendData(newValues, amountPerSubset) Also: PeData.X.AppendData(...) for Pesgo/Pe3do numeric X data. PeData.Z.AppendData(...) for Pesgo contour or Pe3do data. MEMORY LAYOUT for the newValues array (non-jagged data): Data is organized as all points for subset 0, then subset 1, etc. For 4 subsets with 150 new points each: array length = 4 x 150. Layout: [s0p0..s0p149, s1p0..s1p149, s2p0..s2p149, s3p0..s3p149] For X data with DuplicateDataX, only one subset's worth is needed. Pego-specific: also append point labels to keep them in sync: PeString.PointLabels.AppendData(labelString); Pre-allocate labels at setup: PeString.PointLabels[Points-1] = ""; Pre-allocate Y data at setup: PeData.Y[lastSubset, Points-1] = 0; Pego scroll control with AppendData: PeUserInterface.Scrollbar.PointsToGraph = N; // visible window PeUserInterface.Scrollbar.PointsToGraphInit = Last; // show newest Note: PointsToGraph is not real-time-specific -- it controls how many points are visible, adding a scrollbar for the rest. Pe3do with AppendData: shifts points within ALL subsets simultaneously, producing a scrolling surface effect. See Strategy 4 for waterfall. Examples: 17(Pego), 116-117(Pesgo), 145-146(Pesgo), 410,413(Pe3do), 412(Pe3do bar) STRATEGY 2 -- FASTCOPYFROM (full data replace each tick) Replace ALL data every timer tick. Best for waveform displays where entire buffer changes simultaneously (oscilloscope-style). Call: PeData.Y.FastCopyFrom(array, totalSize) Set ReuseDataX = true in timer if X data is unchanged (see below). Manual axis scaling required -- no time for auto-ranging. Example 115: 4 subsets x 100K points, 400K values replaced per tick. Pepso real-time would typically use this strategy since the full polar dataset changes each update. STRATEGY 3 -- DIRECT INDEX WRITE WITH WRAP Write data directly by index, wrap counter at end of buffer. No shifting, no appending -- just overwrite in place. PeData.Y[0, counter] = newY; PeData.X[0, counter] = newX; counter++; if (counter >= Points) counter = 0; Good for circular displays where old data is overwritten. Example 118: 100-point buffer, line annotation tracks position. STRATEGY 4 -- APPENDSUBSET (Pe3do waterfall only) Shifts all subsets and adds a new complete subset -- each tick adds a new "row" (area plot) to a waterfall visualization. Call: Pe3do1.PeData.Y.AppendSubset(newYData, amountPerPoint) Also: X.AppendSubset(...), Z.AppendSubset(...) IMPORTANT: CircularBuffers do NOT work with AppendSubset. CircularBuffers only work with AppendData. Contrast with Pe3do AppendData (Strategy 1) which shifts points within all subsets -- scrolling surface vs waterfall. Example 411. STRATEGY 5 -- USEDATAATLOCATION (zero-copy, largest datasets) App manages its own memory; chart points to it, no copy. Call: PeData.Y.UseDataAtLocation(localArray, bufferSize) Update your local array directly, then trigger chart refresh. Can combine with CircularBuffers and AppendData -- PE appends into your local memory using its circular pointer. Example 146: 4 subsets x 2M points, zero-copy, faster than 145. --- REFRESH AND REPAINT RULES --- This is the most confusing area. The correct refresh call depends on the render engine and whether axis scales need updating. WHEN AXIS SCALES MUST UPDATE (auto-range or manual extents changed): PeFunction.ReinitializeResetImage() // all objects This internally calls PEreinitialize (recomputes scale ranges -- CPU intensive for large data) then PEresetimage (rebuilds image). Follow with Invalidate() or Refresh(). WHEN ONLY DATA CHANGES (scales are manually set and fixed): Direct2D / GdiPlus: PeFunction.ReinitializeResetImage() is still used, but if scales are manually set, the ranging step is minimal. Direct3D (Pesgo and Pe3do): PeFunction.Force3dxVerticeRebuild = true; // flag vertex rebuild Then Invalidate(); // no ReinitializeResetImage needed This is dramatically faster -- skips all scale computation. Pesgo with Direct3D: if ManualMinX/ManualMaxX are adjusted in the timer, also call ReinitializeResetImage() to update 2D axis labels (Example 145 pattern: Force3dxVerticeRebuild + Reinit). Pe3do with Direct3D: axis labels are part of the 3D scene, so Force3dxVerticeRebuild + Invalidate is sufficient even when manual axis extents change (Example 413 pattern). Pe3do LEGACY FUNCTION: PeFunction.PEreconstruct3dpolygons() is functionally identical to Force3dxVerticeRebuild = true. Both set the same internal flag. Prefer the property form. Older examples use the function form. ADDITIONAL DIRECT3D FLAGS (set in timer only when needed): PeFunction.Force3dxNewColors = true; Only if color-related properties change during real-time updates. At initialization, set once during setup -- not needed in timer unless colors actually change per tick. PeFunction.Force3dxAnnotVerticeRebuild = true; // Pe3do only Only if graph annotation data changes during real-time updates. Invalidate() vs Refresh(): Invalidate() queues a WM_PAINT -- asynchronous, preferred for most real-time (especially Direct3D where GPU handles timing). Refresh() forces an immediate synchronous WM_PAINT -- use when the developer wants guaranteed immediate visual update. Both work; Invalidate is the baseline, Refresh is optional. --- PERFORMANCE FEATURES (large datasets) --- CIRCULARBUFFERS: PeData.CircularBuffers = true Applies to Pego, Pesgo, Pepso, Pe3do. Only works with AppendData (NOT AppendSubset). Uses a ring-buffer pointer instead of shifting memory. Essential for large datasets (100K+ points) with AppendData. COMPUTESHADER: PeData.ComputeShader = true Requires RenderEngine = Direct3D. Offloads chart construction to GPU (potentially 2000+ cores vs single CPU). Applies to Pesgo and Pe3do. Massive speedup for large datasets. STAGINGBUFFERS: PeData.StagingBufferX/Y/Z = true Keeps GPU-side copies of data arrays. Enables efficient CPU-->GPU transfer pipeline. Use with Direct3D real-time. Enable for each axis that is being updated. FILTER2D3D: PeData.Filter2D3D = true (Pesgo only) Two-tier ComputeShader: first pre-filters sequential 2D line data losslessly, then final shader constructs the scene. Dramatic speed for 250K+ points. Use with ComputeShader = true. REUSEDATAX/Y/Z: PeData.ReuseDataX = true (etc.) Set in the timer tick to tell PE "this axis data hasn't changed." Skips Direct3D buffer processing for that axis. Applies to Pesgo and Pe3do with Direct3D. Which axis to set depends on which data is static: Pesgo contour: X,Y are grid axes, Z is elevation -- if only Z changes, set ReuseDataX = true and ReuseDataY = true. Pe3do surface: X,Z are horizontal plane, Y is elevation -- if only Y changes, set ReuseDataX = true and ReuseDataZ = true. Rule: the "3rd dimension" is Z for Pesgo, Y for Pe3do. SKIPRANGING: PeData.SkipRanging = true (Pe3do only) Tells PE to skip min/max range determination entirely. Only safe when ALL axes are manually scaled. Avoids CPU-intensive ranging for large Pe3do datasets. Set in timer before Invalidate. COMPOSITE2D3D: PeConfigure.Composite2D3D (Pesgo, Pego with D3D) Controls Direct2D/Direct3D layer compositing. Default creates two D2D layers (background + foreground). Setting Background or Foreground forces one layer, reducing overhead. Not real-time specific -- general performance optimization for any Direct3D chart. --- ZOOM MANAGEMENT DURING REAL-TIME --- When using AppendData with manual scaling, the developer typically slides the manual axis extents in the timer: PeGrid.Configure.ManualMinX = counter - windowSize; PeGrid.Configure.ManualMaxX = counter; If the user has interactively zoomed (PeGrid.Zoom.Mode == true), also shift the zoom extents so the zoomed view scrolls with data: if (PeGrid.Zoom.Mode == true) { PeGrid.Zoom.MinX += nNewPoints; PeGrid.Zoom.MaxX += nNewPoints; } Without this, a zoomed view would see data scroll away. Alternatively, omit the zoom shift to let the user see a frozen zoomed view while new data flows at the unzoomed level (Example 146 does this). Note: Pesgo axis scaling has three independent levels: a) Auto-scaled -- chart determines range from data b) Manually scaled -- developer sets ManualMinY/ManualMaxY c) Zoom-controlled -- ZoomMinY/ZoomMaxY override when ZoomMode=true Zoom overlays manual scaling without changing the manual values. See pe-zoom knowledge file for details. --- TIMER PATTERN --- Use System.Windows.Forms.Timer (UI thread safe). In tick handler: 1. Prepare new data in pre-allocated arrays 2. Transfer data (AppendData, FastCopyFrom, or direct index) 3. Set any Reuse/Skip flags 4. Update manual axis extents if needed 5. Refresh: ReinitializeResetImage() + Invalidate/Refresh, or Force3dxVerticeRebuild + Invalidate (Direct3D fast path) Typical intervals: 10-50ms. Pre-allocate data arrays at class scope (not in timer) to avoid per-tick allocation overhead.