# Copyright (c) 2025-2026 Gigasoft, Inc. All rights reserved. === ProEssentials Events (knowledge rev 4.1) & Interaction Patterns === ProEssentials fires .NET events for user interactions. This file documents the actual coding patterns, setup requirements, and gotchas. EVENT WIRING: chart.PeEventName += handler; Handler signature: void Handler(object sender, SpecificEventArgs e) EventArgs in Gigasoft.ProEssentials.EventArg namespace. === PATTERN A: Pixel-to-Data Coordinate Conversion === ConvPixelToGraph converts mouse pixel position to data-coordinate values. Full signature: PeFunction.ConvPixelToGraph(ref int axisIndex, ref int pixelX, ref int pixelY, ref double graphX, ref double graphY, bool rightAxis, bool topAxis, bool viceVersa) Parameters: axisIndex -- INPUT when using OverlapMultiAxes (set which axis to query). OUTPUT when using MultiAxesSubsets without overlap (returns axis hit). Initialize to 0 if not using multi-axis. pixelX/pixelY -- INPUT: mouse pixel coords. OUTPUT: nearest grid intersection. graphX/graphY -- OUTPUT: data coordinate values at that pixel location. rightAxis -- true reads Right Y axis value, false reads Left Y axis. topAxis -- true reads Top X axis value, false reads Bottom X axis. viceVersa -- true reverses: converts graph coordinates TO pixels. Standard usage idiom (in MouseMove handler): System.Drawing.Point pt = chart.PeUserInterface.Cursor.LastMouseMove; System.Drawing.Rectangle rect = chart.PeFunction.GetRectGraph(); if (rect.Contains(pt)) { int nA = 0, nX = pt.X, nY = pt.Y; double fX = 0, fY = 0; chart.PeFunction.ConvPixelToGraph(ref nA, ref nX, ref nY, ref fX, ref fY, false, false, false); // fX, fY now contain data coordinates } Note: ManualMinX/MaxX/MinY/MaxY are only valid AFTER chart has been rendered on screen at least once. Use them to clamp coordinates to chart extents. === PATTERN B: HotSpot Detection via GetHotSpot() === GetHotSpot() returns HotSpotData struct with .Type enum and .Data1/.Data2. Call anytime in MouseMove (does not require a click event). Also: SearchSubsetPointIndex(pixelX, pixelY) returns Point(subsetIndex, pointIndex) for closest data point -- works even when no hot spot is directly hit. HotSpotData struct: .Type = HotSpotType enum, .Data1 and .Data2 meanings per type: None(0) -- no hot spot Subset(1) -- Data1=subset index Point(2) -- Data1=point index Graph(3) -- graph area (no specific item) Table(4) -- Data1=row, Data2=column DataPoint(5) -- Data1=subset index, Data2=point index GraphAnnotation(6) -- Data1=annotation index XAxisAnnotation(7) -- Data1=annotation index YAxisAnnotation(8) -- Data1=annotation index HorzLineAnnotation(9) -- Data1=annotation index VertLineAnnotation(10) -- Data1=annotation index MainTitle(11) -- no extra data SubTitle(12) -- no extra data MultiSubTitle(13) -- Data1=index, Data2=justification (0=left,1=center,2=right) MultiBottomTitle(14) -- Data1=index, Data2=justification YAxisLabel(15) -- Data1=axis, Data2=0(left)/1(right) XAxisLabel(16) -- Data1=0(bottom)/1(top) YAxis(17) -- Data1=axis, Data2=0(left)/1(right) XAxis(18) -- Data1=0(bottom)/1(top) YAxisGridNumber(19) -- grid number clicked RYAxisGridNumber(20) -- right Y grid number clicked XAxisGridNumber(21) -- X grid number clicked TXAxisGridNumber(22) -- top X grid number clicked TableAnnotation0-59(23-82) -- Data1=row, Data2=column (table index = Type-23) ZAxisGridNumber(83) -- Pe3do Z axis grid number PeDataHotSpot event has named args: e.SubsetIndex, e.PointIndex. GetHotSpot() struct uses generic: ds.Data1, ds.Data2. Both access the same underlying data. Enabling hot spots (set BEFORE rendering): PeUserInterface.HotSpot.Data = true; // data points PeUserInterface.HotSpot.Subset = true; // subset legends PeUserInterface.HotSpot.Point = true; // point labels PeUserInterface.HotSpot.Title = true; // title text PeUserInterface.HotSpot.AxisLabel = true; // axis labels PeUserInterface.HotSpot.GridNumberY = true; // Y grid numbers PeUserInterface.HotSpot.GridNumberX = true; // X grid numbers PeUserInterface.HotSpot.Size = HotSpotSize.Large; // or (HotSpotSize)12 for custom === PATTERN C: Custom Tracking Tooltips === PeCustomTrackingDataText -- customize tooltip for DATA area hover/cursor. PeCustomTrackingOtherText -- customize tooltip for NON-DATA areas (titles, axes, legends). Requires matching HotSpot enables (HotSpot.Title, HotSpot.Subset, etc.) Setup requires ALL of: chart.PeUserInterface.Cursor.PromptTracking = true; chart.PeUserInterface.Cursor.PromptLocation = CursorPromptLocation.ToolTip; chart.PeUserInterface.Cursor.TrackingCustomDataText = true; // for data area chart.PeUserInterface.Cursor.TrackingCustomOtherText = true; // for non-data areas In handler, set e.TrackingText to your custom string. Use \n for multiline. Reading interpolated values (works even between data points): chart.PeUserInterface.Cursor.CursorValueX // interpolated X chart.PeUserInterface.Cursor.CursorValueY // interpolated Y chart.PeUserInterface.Cursor.CursorValueZ // interpolated Z (contour/3D) Distinguish trigger source: if (chart.PeUserInterface.Cursor.TrackingPromptTrigger == TrackingTrigger.MouseMove) // tooltip from mouse hover -- pixel between data points // use CursorValueX/Y or ConvPixelToGraph for interpolated coords else // tooltip from keyboard cursor move -- snapped to exact data point // use Cursor.Subset / Cursor.Point to read actual Y data Dynamic tooltip styling (set inside handler before setting TrackingText): chart.PeUserInterface.Cursor.TrackingTooltipTitle = "Custom Title"; chart.PeUserInterface.Cursor.TrackingTooltipBkColor = Color.FromArgb(0, R, G, B); chart.PeUserInterface.Cursor.TrackingTooltipTextColor = Color.FromArgb(0, R, G, B); Alpha is IGNORED (Windows tooltip UX has no transparency). RGB(0,0,0) = black. These properties PERSIST until changed again -- set each time in handler if values need to vary (e.g. different colors for positive vs negative data). See pe-cursor-tooltip knowledge file for full cursor/tooltip architecture. === PATTERN D: Table Annotations as Interactive UI === Table annotations become clickable buttons/controls via per-cell hot spots. Setup: PeAnnotation.Table.HotSpot[row, col] = true; // per cell Event: PeTableAnnotation fires with e.WorkingTable, e.RowIndex, e.ColumnIndex. Use GetHotSpotData() to read HotSpotType.TableAnnotation0 through 59. Table.Working = N switches which table (0-59) subsequent property calls target. Example: write to table 0, then table 1: chart.PeAnnotation.Table.Working = 0; chart.PeAnnotation.Table.Text[0, 2] = "value1"; chart.PeAnnotation.Table.Working = 1; chart.PeAnnotation.Table.Text[0, 2] = "value2"; DrawTable(tableIndex) -- efficient partial redraw of one table only. Use during MouseMove for real-time updating without full ResetImage. Example: chart.PeFunction.DrawTable(0); // redraws only table 0 === PATTERN E: Custom Grid Number Formatting === PeCustomGridNumber event fires for each grid number during rendering. Enable per-axis: PeGrid.Option.CustomGridNumbersY = true; (WorkingAxis-dependent!) Also: PeGrid.Option.CustomGridNumbersX = true; Event args: e.AxisType values: 0=Y, 1=RY, 2=X, 3=TX, 4=ExtraX, 5=ExtraTX e.AxisIndex -- which axis (0-5) when using MultiAxesSubsets or SetExtraAxisX e.NumberValue -- the raw numeric value for this grid line e.NumberString -- SET this to override the displayed string WARNING: Do not debug this event with breakpoints. Breakpoints may trigger WM_PAINT causing image rebuild loops. Use debug strings instead. This advice applies generally to all ProEssentials code during image construction. === PATTERN F: Custom Popup Menus === Setup (in chart configuration code): CustomMenuText[index] = "text" // "|" = separator line CustomMenuText[index] = "Popup|Sub1|Sub2|Sub3" // popup with subitems CustomMenuState[menuIndex, 0] = Checked/UnChecked // simple items CustomMenuState[menuIndex, subIndex] = Checked // popup subitems CustomMenu[menuIndex, subIndex] = CustomMenu.Grayed // disable item CustomMenuLocation[index] = CustomMenuLocation.Bottom PeCustomMenu event: e.MenuIndex identifies which menu, e.SubmenuIndex identifies which popup subitem (0 = main popup header, 1+ = subitems). === PATTERN G: Data Point Dragging === Three-event pattern: DataHotSpot -> MouseMove -> MouseUp. Requires: PeUserInterface.HotSpot.Data = true; HotSpot.Size = Large; // Class-level variables bool bDragging = false; int nDragIndexS, nDragIndexP; // PeDataHotSpot -- capture which point was clicked void chart_PeDataHotSpot(..., DataHotSpotEventArgs e) { bDragging = true; nDragIndexS = e.SubsetIndex; nDragIndexP = e.PointIndex; } // MouseMove -- update data at dragged point void chart_MouseMove(...) { if (!bDragging) return; // ConvPixelToGraph to get fX, fY (see Pattern A) // Clamp to ManualMin/Max extents chart.PeData.X[nDragIndexS, nDragIndexP] = (float)fX; chart.PeData.Y[nDragIndexS, nDragIndexP] = (float)fY; chart.PeFunction.PartialReinitialize(); chart.PeFunction.ResetImage(0, 0); chart.Invalidate(); chart.Refresh(); } // MouseUp -- end drag void chart_MouseUp(...) { bDragging = false; } === PATTERN H: Zoom Events === PeZoomIn fires after user completes a zoom. Read zoom extents: chart.PeGrid.Zoom.MinX / MaxX / MinY / MaxY Use to place annotations within zoomed region or react to zoom changes. === PATTERN I: 3D-Specific Event Patterns === 3D KeyPress camera targeting (examples 400, 403, 404): In KeyPress, read key 0-9 to select an annotation group. Get annotation coordinates, call Pe3do1.PeFunction.SetViewingAt(x, y, z). Set PePlot.Option.ViewingMode = ViewingMode.DataLocation (focus on point). Toggle back: ViewingMode = ViewingMode.Center (default orbit). Adjust PePlot.Option.DxZoom for zoom level at target. 3D Polygon highlight in MouseMove (examples 400, 407): On hot spot hit, build StartPoly->AddPolyPoint->EndPolygon annotations tracing the subset's data to create a highlight polygon overlay. Set PeAnnotation.InFront = false so surface renders in front of highlight. Always: PeFunction.Force3dxAnnotVerticeRebuild = true after any 3D annotation change. Set trailing annotation Text = "" to prevent VirtualLabels from showing. 3D HighLightAnnotationIndex (example 403): PeAnnotation.Graph.HighLightAnnotationIndex = N; Draws a highlighted colored sphere at annotation index N. Set to -1 to clear. Useful for mousemove or animated highlighting. KEY: Use pe_query.py to verify exact event signatures and property paths. pe_query.py search "hotspot" pe_query.py search "cursor" pe_query.py search "tracking"