2024-12-17 23:51:45 +01:00
//--------------------------------------------------
// ImPlot3D v0.1
// implot3d.cpp
// Date: 2024-11-16
// Author: Breno Cunha Queiroz (brenocq.com)
//
// Acknowledgments:
// ImPlot3D is heavily inspired by ImPlot
// (https://github.com/epezent/implot) by Evan Pezent,
// and follows a similar code style and structure to
// maintain consistency with ImPlot's API.
//--------------------------------------------------
// Table of Contents:
// [SECTION] Includes
// [SECTION] Macros
// [SECTION] Context
// [SECTION] Text Utils
// [SECTION] Legend Utils
// [SECTION] Mouse Position Utils
// [SECTION] Plot Box Utils
// [SECTION] Formatter
// [SECTION] Locator
// [SECTION] Context Menus
// [SECTION] Begin/End Plot
// [SECTION] Setup
// [SECTION] Plot Utils
// [SECTION] Setup Utils
// [SECTION] Miscellaneous
// [SECTION] Styles
// [SECTION] Colormaps
// [SECTION] Context Utils
// [SECTION] Style Utils
// [SECTION] ImPlot3DPoint
// [SECTION] ImPlot3DBox
// [SECTION] ImPlot3DRange
// [SECTION] ImPlot3DQuat
// [SECTION] ImDrawList3D
// [SECTION] ImPlot3DAxis
// [SECTION] ImPlot3DPlot
// [SECTION] ImPlot3DStyle
//-----------------------------------------------------------------------------
// [SECTION] Includes
//-----------------------------------------------------------------------------
# ifndef IMGUI_DEFINE_MATH_OPERATORS
# define IMGUI_DEFINE_MATH_OPERATORS
# endif
# include "implot3d.h"
# include "implot3d_internal.h"
2024-12-18 20:18:27 +01:00
# include <cmath>
2024-12-17 23:51:45 +01:00
# ifndef IMGUI_DISABLE
//-----------------------------------------------------------------------------
// [SECTION] Macros
//-----------------------------------------------------------------------------
# define IMPLOT3D_CHECK_CTX() IM_ASSERT_USER_ERROR(GImPlot3D != nullptr, "No current context. Did you call ImPlot3D::CreateContext() or ImPlot3D::SetCurrentContext()?")
# define IMPLOT3D_CHECK_PLOT() IM_ASSERT_USER_ERROR(GImPlot3D->CurrentPlot != nullptr, "No active plot. Did you call ImPlot3D::BeginPlot()?")
//-----------------------------------------------------------------------------
// [SECTION] Context
//-----------------------------------------------------------------------------
namespace ImPlot3D {
// Global ImPlot3D context
# ifndef GImPlot3D
ImPlot3DContext * GImPlot3D = nullptr ;
# endif
static ImPlot3DQuat init_rotation = ImPlot3DQuat ( - 0.513269 , - 0.212596 , - 0.318184 , 0.76819 ) ;
ImPlot3DContext * CreateContext ( ) {
ImPlot3DContext * ctx = IM_NEW ( ImPlot3DContext ) ( ) ;
if ( GImPlot3D = = nullptr )
SetCurrentContext ( ctx ) ;
InitializeContext ( ctx ) ;
return ctx ;
}
void DestroyContext ( ImPlot3DContext * ctx ) {
if ( ctx = = nullptr )
ctx = GImPlot3D ;
if ( GImPlot3D = = ctx )
SetCurrentContext ( nullptr ) ;
IM_DELETE ( ctx ) ;
}
ImPlot3DContext * GetCurrentContext ( ) { return GImPlot3D ; }
void SetCurrentContext ( ImPlot3DContext * ctx ) { GImPlot3D = ctx ; }
//-----------------------------------------------------------------------------
// [SECTION] Text Utils
//-----------------------------------------------------------------------------
void AddTextRotated ( ImDrawList * draw_list , ImVec2 pos , float angle , ImU32 col , const char * text_begin , const char * text_end ) {
if ( ! text_end )
text_end = text_begin + strlen ( text_begin ) ;
ImGuiContext & g = * GImGui ;
ImFont * font = g . Font ;
// Align to be pixel perfect
pos . x = IM_FLOOR ( pos . x ) ;
pos . y = IM_FLOOR ( pos . y ) ;
const float scale = g . FontSize / font - > FontSize ;
// Measure the size of the text in unrotated coordinates
ImVec2 text_size = font - > CalcTextSizeA ( g . FontSize , FLT_MAX , 0.0f , text_begin , text_end , nullptr ) ;
// Precompute sine and cosine of the angle (note: angle should be positive for rotation in ImGui)
float cos_a = cosf ( - angle ) ;
float sin_a = sinf ( - angle ) ;
const char * s = text_begin ;
int chars_total = ( int ) ( text_end - s ) ;
int chars_rendered = 0 ;
const int vtx_count_max = chars_total * 4 ;
const int idx_count_max = chars_total * 6 ;
draw_list - > PrimReserve ( idx_count_max , vtx_count_max ) ;
// Adjust pen position to center the text
ImVec2 pen = ImVec2 ( - text_size . x * 0.5f , - text_size . y * 0.5f ) ;
while ( s < text_end ) {
unsigned int c = ( unsigned int ) * s ;
if ( c < 0x80 ) {
s + = 1 ;
} else {
s + = ImTextCharFromUtf8 ( & c , s , text_end ) ;
if ( c = = 0 ) // Malformed UTF-8?
break ;
}
const ImFontGlyph * glyph = font - > FindGlyph ( ( ImWchar ) c ) ;
if ( glyph = = nullptr ) {
continue ;
}
// Glyph dimensions and positions
ImVec2 glyph_offset = ImVec2 ( glyph - > X0 , glyph - > Y0 ) * scale ;
ImVec2 glyph_size = ImVec2 ( glyph - > X1 - glyph - > X0 , glyph - > Y1 - glyph - > Y0 ) * scale ;
// Corners of the glyph quad in unrotated space
ImVec2 corners [ 4 ] ;
corners [ 0 ] = pen + glyph_offset ;
corners [ 1 ] = pen + glyph_offset + ImVec2 ( glyph_size . x , 0 ) ;
corners [ 2 ] = pen + glyph_offset + glyph_size ;
corners [ 3 ] = pen + glyph_offset + ImVec2 ( 0 , glyph_size . y ) ;
// Rotate and translate the corners
for ( int i = 0 ; i < 4 ; i + + ) {
float x = corners [ i ] . x ;
float y = corners [ i ] . y ;
corners [ i ] . x = x * cos_a - y * sin_a + pos . x ;
corners [ i ] . y = x * sin_a + y * cos_a + pos . y ;
}
// Texture coordinates
ImVec2 uv0 = ImVec2 ( glyph - > U0 , glyph - > V0 ) ;
ImVec2 uv1 = ImVec2 ( glyph - > U1 , glyph - > V1 ) ;
// Render the glyph quad
draw_list - > PrimQuadUV ( corners [ 0 ] , corners [ 1 ] , corners [ 2 ] , corners [ 3 ] ,
uv0 , ImVec2 ( glyph - > U1 , glyph - > V0 ) ,
uv1 , ImVec2 ( glyph - > U0 , glyph - > V1 ) ,
col ) ;
// Advance the pen position
pen . x + = glyph - > AdvanceX * scale ;
chars_rendered + + ;
}
// Return unused vertices
int chars_skipped = chars_total - chars_rendered ;
draw_list - > PrimUnreserve ( chars_skipped * 6 , chars_skipped * 4 ) ;
}
void AddTextCentered ( ImDrawList * draw_list , ImVec2 top_center , ImU32 col , const char * text_begin ) {
const char * text_end = ImGui : : FindRenderedTextEnd ( text_begin ) ;
ImVec2 text_size = ImGui : : CalcTextSize ( text_begin , text_end , true ) ;
draw_list - > AddText ( ImVec2 ( top_center . x - text_size . x * 0.5f , top_center . y ) , col , text_begin , text_end ) ;
}
//-----------------------------------------------------------------------------
// [SECTION] Legend Utils
//-----------------------------------------------------------------------------
ImVec2 GetLocationPos ( const ImRect & outer_rect , const ImVec2 & inner_size , ImPlot3DLocation loc , const ImVec2 & pad ) {
ImVec2 pos ;
// Legend x coordinate
if ( ImPlot3D : : ImHasFlag ( loc , ImPlot3DLocation_West ) & & ! ImPlot3D : : ImHasFlag ( loc , ImPlot3DLocation_East ) )
pos . x = outer_rect . Min . x + pad . x ;
else if ( ! ImPlot3D : : ImHasFlag ( loc , ImPlot3DLocation_West ) & & ImPlot3D : : ImHasFlag ( loc , ImPlot3DLocation_East ) )
pos . x = outer_rect . Max . x - pad . x - inner_size . x ;
else
pos . x = outer_rect . GetCenter ( ) . x - inner_size . x * 0.5f ;
// Legend y coordinate
if ( ImPlot3D : : ImHasFlag ( loc , ImPlot3DLocation_North ) & & ! ImPlot3D : : ImHasFlag ( loc , ImPlot3DLocation_South ) )
pos . y = outer_rect . Min . y + pad . y ;
else if ( ! ImPlot3D : : ImHasFlag ( loc , ImPlot3DLocation_North ) & & ImPlot3D : : ImHasFlag ( loc , ImPlot3DLocation_South ) )
pos . y = outer_rect . Max . y - pad . y - inner_size . y ;
else
pos . y = outer_rect . GetCenter ( ) . y - inner_size . y * 0.5f ;
pos . x = IM_ROUND ( pos . x ) ;
pos . y = IM_ROUND ( pos . y ) ;
return pos ;
}
ImVec2 CalcLegendSize ( ImPlot3DItemGroup & items , const ImVec2 & pad , const ImVec2 & spacing , bool vertical ) {
const int nItems = items . GetLegendCount ( ) ;
const float txt_ht = ImGui : : GetTextLineHeight ( ) ;
const float icon_size = txt_ht ;
// Get label max width
float max_label_width = 0 ;
float sum_label_width = 0 ;
for ( int i = 0 ; i < nItems ; i + + ) {
const char * label = items . GetLegendLabel ( i ) ;
const float label_width = ImGui : : CalcTextSize ( label , nullptr , true ) . x ;
max_label_width = label_width > max_label_width ? label_width : max_label_width ;
sum_label_width + = label_width ;
}
// Compute legend size
const ImVec2 legend_size = vertical ? ImVec2 ( pad . x * 2 + icon_size + max_label_width , pad . y * 2 + nItems * txt_ht + ( nItems - 1 ) * spacing . y ) : ImVec2 ( pad . x * 2 + icon_size * nItems + sum_label_width + ( nItems - 1 ) * spacing . x , pad . y * 2 + txt_ht ) ;
return legend_size ;
}
void ShowLegendEntries ( ImPlot3DItemGroup & items , const ImRect & legend_bb , bool hovered , const ImVec2 & pad , const ImVec2 & spacing , bool vertical , ImDrawList & draw_list ) {
const float txt_ht = ImGui : : GetTextLineHeight ( ) ;
const float icon_size = txt_ht ;
const float icon_shrink = 2 ;
ImU32 col_txt = GetStyleColorU32 ( ImPlot3DCol_LegendText ) ;
ImU32 col_txt_dis = ImAlphaU32 ( col_txt , 0.25f ) ;
float sum_label_width = 0 ;
const int num_items = items . GetLegendCount ( ) ;
if ( num_items = = 0 )
return ;
ImPlot3DContext & gp = * GImPlot3D ;
// Render legend items
for ( int i = 0 ; i < num_items ; i + + ) {
const int idx = i ;
ImPlot3DItem * item = items . GetLegendItem ( idx ) ;
const char * label = items . GetLegendLabel ( idx ) ;
const float label_width = ImGui : : CalcTextSize ( label , nullptr , true ) . x ;
const ImVec2 top_left = vertical ? legend_bb . Min + pad + ImVec2 ( 0 , i * ( txt_ht + spacing . y ) ) : legend_bb . Min + pad + ImVec2 ( i * ( icon_size + spacing . x ) + sum_label_width , 0 ) ;
sum_label_width + = label_width ;
ImRect icon_bb ;
icon_bb . Min = top_left + ImVec2 ( icon_shrink , icon_shrink ) ;
icon_bb . Max = top_left + ImVec2 ( icon_size - icon_shrink , icon_size - icon_shrink ) ;
ImRect label_bb ;
label_bb . Min = top_left ;
label_bb . Max = top_left + ImVec2 ( label_width + icon_size , icon_size ) ;
ImU32 col_txt_hl ;
ImU32 col_item = ImAlphaU32 ( item - > Color , 1 ) ;
ImRect button_bb ( icon_bb . Min , label_bb . Max ) ;
ImGui : : KeepAliveID ( item - > ID ) ;
bool item_hov = false ;
bool item_hld = false ;
bool item_clk = ImPlot3D : : ImHasFlag ( items . Legend . Flags , ImPlot3DLegendFlags_NoButtons )
? false
: ImGui : : ButtonBehavior ( button_bb , item - > ID , & item_hov , & item_hld ) ;
if ( item_clk )
item - > Show = ! item - > Show ;
const bool hovering = item_hov & & ! ImPlot3D : : ImHasFlag ( items . Legend . Flags , ImPlot3DLegendFlags_NoHighlightItem ) ;
if ( hovering ) {
item - > LegendHovered = true ;
col_txt_hl = ImPlot3D : : ImMixU32 ( col_txt , col_item , 64 ) ;
} else {
item - > LegendHovered = false ;
col_txt_hl = ImGui : : GetColorU32 ( col_txt ) ;
}
ImU32 col_icon ;
if ( item_hld )
col_icon = item - > Show ? ImAlphaU32 ( col_item , 0.5f ) : ImGui : : GetColorU32 ( ImGuiCol_TextDisabled , 0.5f ) ;
else if ( item_hov )
col_icon = item - > Show ? ImAlphaU32 ( col_item , 0.75f ) : ImGui : : GetColorU32 ( ImGuiCol_TextDisabled , 0.75f ) ;
else
col_icon = item - > Show ? col_item : col_txt_dis ;
draw_list . AddRectFilled ( icon_bb . Min , icon_bb . Max , col_icon ) ;
const char * text_display_end = ImGui : : FindRenderedTextEnd ( label , nullptr ) ;
if ( label ! = text_display_end )
draw_list . AddText ( top_left + ImVec2 ( icon_size , 0 ) , item - > Show ? col_txt_hl : col_txt_dis , label , text_display_end ) ;
}
}
void RenderLegend ( ) {
ImPlot3DContext & gp = * GImPlot3D ;
ImPlot3DPlot & plot = * gp . CurrentPlot ;
if ( ImPlot3D : : ImHasFlag ( plot . Flags , ImPlot3DFlags_NoLegend ) | | plot . Items . GetLegendCount ( ) = = 0 )
return ;
ImGuiContext & g = * GImGui ;
ImGuiWindow * window = g . CurrentWindow ;
ImDrawList * draw_list = window - > DrawList ;
const ImGuiIO & IO = ImGui : : GetIO ( ) ;
ImPlot3DLegend & legend = plot . Items . Legend ;
const bool legend_horz = ImPlot3D : : ImHasFlag ( legend . Flags , ImPlot3DLegendFlags_Horizontal ) ;
const ImVec2 legend_size = CalcLegendSize ( plot . Items , gp . Style . LegendInnerPadding , gp . Style . LegendSpacing , ! legend_horz ) ;
const ImVec2 legend_pos = GetLocationPos ( plot . PlotRect ,
legend_size ,
legend . Location ,
gp . Style . LegendPadding ) ;
legend . Rect = ImRect ( legend_pos , legend_pos + legend_size ) ;
// Test hover
legend . Hovered = legend . Rect . Contains ( IO . MousePos ) ;
// Render background
ImU32 col_bg = GetStyleColorU32 ( ImPlot3DCol_LegendBg ) ;
ImU32 col_bd = GetStyleColorU32 ( ImPlot3DCol_LegendBorder ) ;
draw_list - > AddRectFilled ( legend . Rect . Min , legend . Rect . Max , col_bg ) ;
draw_list - > AddRect ( legend . Rect . Min , legend . Rect . Max , col_bd ) ;
// Render legends
ShowLegendEntries ( plot . Items , legend . Rect , legend . Hovered , gp . Style . LegendInnerPadding , gp . Style . LegendSpacing , ! legend_horz , * draw_list ) ;
}
//-----------------------------------------------------------------------------
// [SECTION] Mouse Position Utils
//-----------------------------------------------------------------------------
void RenderMousePos ( ) {
ImPlot3DContext & gp = * GImPlot3D ;
ImPlot3DPlot & plot = * gp . CurrentPlot ;
if ( ImPlot3D : : ImHasFlag ( plot . Flags , ImPlot3DFlags_NoMouseText ) )
return ;
ImVec2 mouse_pos = ImGui : : GetMousePos ( ) ;
ImPlot3DPoint mouse_plot_pos = PixelsToPlotPlane ( mouse_pos , ImPlane3D_YZ , true ) ;
if ( mouse_plot_pos . IsNaN ( ) )
mouse_plot_pos = PixelsToPlotPlane ( mouse_pos , ImPlane3D_XZ , true ) ;
if ( mouse_plot_pos . IsNaN ( ) )
mouse_plot_pos = PixelsToPlotPlane ( mouse_pos , ImPlane3D_XY , true ) ;
char buff [ IMPLOT3D_LABEL_MAX_SIZE ] ;
if ( ! mouse_plot_pos . IsNaN ( ) ) {
ImGuiTextBuffer builder ;
builder . append ( " ( " ) ;
for ( int i = 0 ; i < 3 ; i + + ) {
ImPlot3DAxis & axis = plot . Axes [ i ] ;
if ( i > 0 )
builder . append ( " , " ) ;
axis . Formatter ( mouse_plot_pos [ i ] , buff , IMPLOT3D_LABEL_MAX_SIZE , axis . FormatterData ) ;
builder . append ( buff ) ;
}
builder . append ( " ) " ) ;
const ImVec2 size = ImGui : : CalcTextSize ( builder . c_str ( ) ) ;
// TODO custom location/padding
const ImVec2 pos = GetLocationPos ( plot . PlotRect , size , ImPlot3DLocation_SouthEast , ImVec2 ( 10 , 10 ) ) ;
ImDrawList & draw_list = * ImGui : : GetWindowDrawList ( ) ;
draw_list . AddText ( pos , GetStyleColorU32 ( ImPlot3DCol_InlayText ) , builder . c_str ( ) ) ;
}
}
//-----------------------------------------------------------------------------
// [SECTION] Plot Box Utils
//-----------------------------------------------------------------------------
// Faces of the box (defined by 4 corner indices)
static const int faces [ 6 ] [ 4 ] = {
{ 0 , 3 , 7 , 4 } , // X-min face
{ 0 , 4 , 5 , 1 } , // Y-min face
{ 0 , 1 , 2 , 3 } , // Z-min face
{ 1 , 2 , 6 , 5 } , // X-max face
{ 3 , 7 , 6 , 2 } , // Y-max face
{ 4 , 5 , 6 , 7 } , // Z-max face
} ;
// Edges of the box (defined by 2 corner indices)
static const int edges [ 12 ] [ 2 ] = {
// Bottom face edges
{ 0 , 1 } ,
{ 1 , 2 } ,
{ 2 , 3 } ,
{ 3 , 0 } ,
// Top face edges
{ 4 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } ,
{ 7 , 4 } ,
// Vertical edges
{ 0 , 4 } ,
{ 1 , 5 } ,
{ 2 , 6 } ,
{ 3 , 7 } ,
} ;
// Face edges (4 edge indices for each face)
static const int face_edges [ 6 ] [ 4 ] = {
{ 3 , 11 , 8 , 7 } , // X-min face
{ 0 , 8 , 4 , 9 } , // Y-min face
{ 0 , 1 , 2 , 3 } , // Z-min face
{ 1 , 9 , 5 , 10 } , // X-max face
{ 2 , 10 , 6 , 11 } , // Y-max face
{ 4 , 5 , 6 , 7 } , // Z-max face
} ;
// Lookup table for axis_corners based on active_faces (3D plot)
static const int axis_corners_lookup_3d [ 8 ] [ 3 ] [ 2 ] = {
// Index 0: active_faces = {0, 0, 0}
{ { 3 , 2 } , { 1 , 2 } , { 1 , 5 } } ,
// Index 1: active_faces = {0, 0, 1}
{ { 7 , 6 } , { 5 , 6 } , { 1 , 5 } } ,
// Index 2: active_faces = {0, 1, 0}
{ { 0 , 1 } , { 1 , 2 } , { 2 , 6 } } ,
// Index 3: active_faces = {0, 1, 1}
{ { 4 , 5 } , { 5 , 6 } , { 2 , 6 } } ,
// Index 4: active_faces = {1, 0, 0}
{ { 3 , 2 } , { 0 , 3 } , { 0 , 4 } } ,
// Index 5: active_faces = {1, 0, 1}
{ { 7 , 6 } , { 4 , 7 } , { 0 , 4 } } ,
// Index 6: active_faces = {1, 1, 0}
{ { 0 , 1 } , { 0 , 3 } , { 3 , 7 } } ,
// Index 7: active_faces = {1, 1, 1}
{ { 4 , 5 } , { 4 , 7 } , { 3 , 7 } } ,
} ;
int GetMouseOverPlane ( const ImPlot3DPlot & plot , const bool * active_faces , const ImVec2 * corners_pix , int * plane_out = nullptr ) {
ImGuiIO & io = ImGui : : GetIO ( ) ;
ImVec2 mouse_pos = io . MousePos ;
if ( plane_out )
* plane_out = - 1 ;
// Check each active face
for ( int a = 0 ; a < 3 ; a + + ) {
int face_idx = a + 3 * active_faces [ a ] ;
ImVec2 p0 = corners_pix [ faces [ face_idx ] [ 0 ] ] ;
ImVec2 p1 = corners_pix [ faces [ face_idx ] [ 1 ] ] ;
ImVec2 p2 = corners_pix [ faces [ face_idx ] [ 2 ] ] ;
ImVec2 p3 = corners_pix [ faces [ face_idx ] [ 3 ] ] ;
// Check if the mouse is inside the face's quad (using a triangle check)
if ( ImTriangleContainsPoint ( p0 , p1 , p2 , mouse_pos ) | | ImTriangleContainsPoint ( p2 , p3 , p0 , mouse_pos ) ) {
if ( plane_out )
* plane_out = a ;
return a ; // Return the plane index: 0 -> YZ, 1 -> XZ, 2 -> XY
}
}
return - 1 ; // Not over any active plane
}
int GetMouseOverAxis ( const ImPlot3DPlot & plot , const bool * active_faces , const ImVec2 * corners_pix , const int plane_2d , int * edge_out = nullptr ) {
const float axis_proximity_threshold = 15.0f ; // Distance in pixels to consider the mouse "close" to an axis
ImGuiIO & io = ImGui : : GetIO ( ) ;
ImVec2 mouse_pos = io . MousePos ;
if ( edge_out )
* edge_out = - 1 ;
bool visible_edges [ 12 ] ;
for ( int i = 0 ; i < 12 ; i + + )
visible_edges [ i ] = false ;
for ( int a = 0 ; a < 3 ; a + + ) {
int face_idx = a + 3 * active_faces [ a ] ;
if ( plane_2d ! = - 1 & & a ! = plane_2d )
continue ;
for ( size_t i = 0 ; i < 4 ; i + + )
visible_edges [ face_edges [ face_idx ] [ i ] ] = true ;
}
// Check each edge for proximity to the mouse
for ( int edge = 0 ; edge < 12 ; edge + + ) {
if ( ! visible_edges [ edge ] )
continue ;
ImVec2 p0 = corners_pix [ edges [ edge ] [ 0 ] ] ;
ImVec2 p1 = corners_pix [ edges [ edge ] [ 1 ] ] ;
// Check distance to the edge
ImVec2 closest_point = ImLineClosestPoint ( p0 , p1 , mouse_pos ) ;
float dist = ImLengthSqr ( mouse_pos - closest_point ) ;
if ( dist < = axis_proximity_threshold ) {
if ( edge_out )
* edge_out = edge ;
// Determine which axis the edge belongs to
if ( edge = = 0 | | edge = = 2 | | edge = = 4 | | edge = = 6 )
return 0 ; // X-axis
else if ( edge = = 1 | | edge = = 3 | | edge = = 5 | | edge = = 7 )
return 1 ; // Y-axis
else
return 2 ; // Z-axis
}
}
return - 1 ; // Not over any axis
}
void RenderPlotBackground ( ImDrawList * draw_list , const ImPlot3DPlot & plot , const ImVec2 * corners_pix , const bool * active_faces , const int plane_2d ) {
const ImVec4 col_bg = GetStyleColorVec4 ( ImPlot3DCol_PlotBg ) ;
const ImVec4 col_bg_hov = col_bg + ImVec4 ( 0.03 , 0.03 , 0.03 , 0.0 ) ;
int hovered_plane = - 1 ;
if ( ! plot . Held ) {
// If the mouse is not held, highlight plane hovering when mouse over it
hovered_plane = GetMouseOverPlane ( plot , active_faces , corners_pix ) ;
if ( GetMouseOverAxis ( plot , active_faces , corners_pix , plane_2d ) ! = - 1 )
hovered_plane = - 1 ;
} else {
// If the mouse is held, highlight the held plane
hovered_plane = plot . HeldPlaneIdx ;
}
for ( int a = 0 ; a < 3 ; a + + ) {
int idx [ 4 ] ; // Corner indices
for ( int i = 0 ; i < 4 ; i + + )
idx [ i ] = faces [ a + 3 * active_faces [ a ] ] [ i ] ;
const ImU32 col = ImGui : : ColorConvertFloat4ToU32 ( ( hovered_plane = = a ) ? col_bg_hov : col_bg ) ;
draw_list - > AddQuadFilled ( corners_pix [ idx [ 0 ] ] , corners_pix [ idx [ 1 ] ] , corners_pix [ idx [ 2 ] ] , corners_pix [ idx [ 3 ] ] , col ) ;
}
}
void RenderPlotBorder ( ImDrawList * draw_list , const ImPlot3DPlot & plot , const ImVec2 * corners_pix , const bool * active_faces , const int plane_2d ) {
ImGuiIO & io = ImGui : : GetIO ( ) ;
int hovered_edge = - 1 ;
if ( ! plot . Held )
GetMouseOverAxis ( plot , active_faces , corners_pix , plane_2d , & hovered_edge ) ;
else
hovered_edge = plot . HeldEdgeIdx ;
bool render_edge [ 12 ] ;
for ( int i = 0 ; i < 12 ; i + + )
render_edge [ i ] = false ;
for ( int a = 0 ; a < 3 ; a + + ) {
int face_idx = a + 3 * active_faces [ a ] ;
if ( plane_2d ! = - 1 & & a ! = plane_2d )
continue ;
for ( size_t i = 0 ; i < 4 ; i + + )
render_edge [ face_edges [ face_idx ] [ i ] ] = true ;
}
ImU32 col_bd = GetStyleColorU32 ( ImPlot3DCol_PlotBorder ) ;
for ( int i = 0 ; i < 12 ; i + + ) {
if ( render_edge [ i ] ) {
int idx0 = edges [ i ] [ 0 ] ;
int idx1 = edges [ i ] [ 1 ] ;
float thickness = i = = hovered_edge ? 3.0f : 1.0f ;
draw_list - > AddLine ( corners_pix [ idx0 ] , corners_pix [ idx1 ] , col_bd , thickness ) ;
}
}
}
void RenderGrid ( ImDrawList * draw_list , const ImPlot3DPlot & plot , const ImPlot3DPoint * corners , const bool * active_faces , const int plane_2d ) {
ImVec4 col_grid = GetStyleColorVec4 ( ImPlot3DCol_AxisGrid ) ;
ImU32 col_grid_minor = ImGui : : GetColorU32 ( col_grid * ImVec4 ( 1 , 1 , 1 , 0.3f ) ) ;
ImU32 col_grid_major = ImGui : : GetColorU32 ( col_grid * ImVec4 ( 1 , 1 , 1 , 0.6f ) ) ;
for ( int face = 0 ; face < 3 ; face + + ) {
if ( plane_2d ! = - 1 & & face ! = plane_2d )
continue ;
int face_idx = face + 3 * active_faces [ face ] ;
const ImPlot3DAxis & axis_u = plot . Axes [ ( face + 1 ) % 3 ] ;
const ImPlot3DAxis & axis_v = plot . Axes [ ( face + 2 ) % 3 ] ;
// Get the two axes (u and v) that define the face plane
int idx0 = faces [ face_idx ] [ 0 ] ;
int idx1 = faces [ face_idx ] [ 1 ] ;
int idx2 = faces [ face_idx ] [ 2 ] ;
int idx3 = faces [ face_idx ] [ 3 ] ;
// Corners of the face in plot space
ImPlot3DPoint p0 = corners [ idx0 ] ;
ImPlot3DPoint p1 = corners [ idx1 ] ;
ImPlot3DPoint p2 = corners [ idx2 ] ;
ImPlot3DPoint p3 = corners [ idx3 ] ;
// Vectors along the edges
ImPlot3DPoint u_vec = p1 - p0 ;
ImPlot3DPoint v_vec = p3 - p0 ;
// Render grid lines along u axis (axis_u)
if ( ! ImPlot3D : : ImHasFlag ( axis_u . Flags , ImPlot3DAxisFlags_NoGridLines ) )
for ( int t = 0 ; t < axis_u . Ticker . TickCount ( ) ; + + t ) {
const ImPlot3DTick & tick = axis_u . Ticker . Ticks [ t ] ;
// Compute position along u
float t_u = ( tick . PlotPos - axis_u . Range . Min ) / ( axis_u . Range . Max - axis_u . Range . Min ) ;
ImPlot3DPoint p_start = p0 + u_vec * t_u ;
ImPlot3DPoint p_end = p3 + u_vec * t_u ;
// Convert to pixel coordinates
ImVec2 p_start_pix = PlotToPixels ( p_start ) ;
ImVec2 p_end_pix = PlotToPixels ( p_end ) ;
// Get color
ImU32 col_line = tick . Major ? col_grid_major : col_grid_minor ;
// Draw the grid line
draw_list - > AddLine ( p_start_pix , p_end_pix , col_line ) ;
}
// Render grid lines along v axis (axis_v)
if ( ! ImPlot3D : : ImHasFlag ( axis_v . Flags , ImPlot3DAxisFlags_NoGridLines ) )
for ( int t = 0 ; t < axis_v . Ticker . TickCount ( ) ; + + t ) {
const ImPlot3DTick & tick = axis_v . Ticker . Ticks [ t ] ;
// Compute position along v
float t_v = ( tick . PlotPos - axis_v . Range . Min ) / ( axis_v . Range . Max - axis_v . Range . Min ) ;
ImPlot3DPoint p_start = p0 + v_vec * t_v ;
ImPlot3DPoint p_end = p1 + v_vec * t_v ;
// Convert to pixel coordinates
ImVec2 p_start_pix = PlotToPixels ( p_start ) ;
ImVec2 p_end_pix = PlotToPixels ( p_end ) ;
// Get color
ImU32 col_line = tick . Major ? col_grid_major : col_grid_minor ;
// Draw the grid line
draw_list - > AddLine ( p_start_pix , p_end_pix , col_line ) ;
}
}
}
void RenderTickMarks ( ImDrawList * draw_list , const ImPlot3DPlot & plot , const ImPlot3DPoint * corners , const ImVec2 * corners_pix , const int axis_corners [ 3 ] [ 2 ] , const int plane_2d ) {
ImU32 col_tick = GetStyleColorU32 ( ImPlot3DCol_AxisTick ) ;
auto DeterminePlaneForAxis = [ & ] ( int axis_idx ) {
if ( plane_2d ! = - 1 )
return plane_2d ;
// If no plane chosen (-1), use:
// X or Y axis -> XY plane (2)
// Z axis -> YZ plane (0)
if ( axis_idx = = 2 )
return 1 ; // Z-axis use XZ plane
else
return 2 ; // X or Y-axis use XY plane
} ;
for ( int a = 0 ; a < 3 ; a + + ) {
const ImPlot3DAxis & axis = plot . Axes [ a ] ;
if ( ImPlot3D : : ImHasFlag ( axis . Flags , ImPlot3DAxisFlags_NoTickMarks ) )
continue ;
int idx0 = axis_corners [ a ] [ 0 ] ;
int idx1 = axis_corners [ a ] [ 1 ] ;
if ( idx0 = = idx1 ) // axis not visible or invalid
continue ;
ImPlot3DPoint axis_start = corners [ idx0 ] ;
ImPlot3DPoint axis_end = corners [ idx1 ] ;
ImPlot3DPoint axis_dir = axis_end - axis_start ;
float axis_len = axis_dir . Length ( ) ;
if ( axis_len < 1e-12 f )
continue ;
axis_dir / = axis_len ;
// Draw axis line
ImVec2 axis_start_pix = corners_pix [ idx0 ] ;
ImVec2 axis_end_pix = corners_pix [ idx1 ] ;
draw_list - > AddLine ( axis_start_pix , axis_end_pix , col_tick ) ;
// Choose plane
int chosen_plane = DeterminePlaneForAxis ( a ) ;
// Project axis_dir onto chosen plane
ImPlot3DPoint proj_dir = axis_dir ;
if ( chosen_plane = = 0 ) {
// YZ plane: zero out x
proj_dir . x = 0.0f ;
} else if ( chosen_plane = = 1 ) {
// XZ plane: zero out y
proj_dir . y = 0.0f ;
} else if ( chosen_plane = = 2 ) {
// XY plane: zero out z
proj_dir . z = 0.0f ;
}
float proj_len = proj_dir . Length ( ) ;
if ( proj_len < 1e-12 f ) {
// Axis is parallel to plane normal or something degenerate, skip ticks
continue ;
}
proj_dir / = proj_len ;
// Rotate 90 degrees in chosen plane
ImPlot3DPoint tick_dir ;
if ( chosen_plane = = 0 ) {
// YZ plane
// proj_dir=(0,py,pz), rotate 90°: (py,pz) -> (-pz,py)
tick_dir = ImPlot3DPoint ( 0 , - proj_dir . z , proj_dir . y ) ;
} else if ( chosen_plane = = 1 ) {
// XZ plane (plane=1)
// proj_dir=(px,0,pz), rotate 90°: (px,pz) -> (-pz,px)
tick_dir = ImPlot3DPoint ( - proj_dir . z , 0 , proj_dir . x ) ;
} else {
// XY plane
// proj_dir=(px,py,0), rotate by 90°: (px,py) -> (-py,px)
tick_dir = ImPlot3DPoint ( - proj_dir . y , proj_dir . x , 0 ) ;
}
tick_dir . Normalize ( ) ;
// Tick lengths in NDC units
const float major_size_ndc = 0.06f ;
const float minor_size_ndc = 0.03f ;
for ( int t = 0 ; t < axis . Ticker . TickCount ( ) ; + + t ) {
const ImPlot3DTick & tick = axis . Ticker . Ticks [ t ] ;
float v = ( tick . PlotPos - axis . Range . Min ) / ( axis . Range . Max - axis . Range . Min ) ;
ImPlot3DPoint tick_pos_ndc = PlotToNDC ( axis_start + axis_dir * ( v * axis_len ) ) ;
// Half tick on each side of the axis line
float size_tick_ndc = tick . Major ? major_size_ndc : minor_size_ndc ;
ImPlot3DPoint half_tick_ndc = tick_dir * ( size_tick_ndc * 0.5f ) ;
ImPlot3DPoint T1_ndc = tick_pos_ndc - half_tick_ndc ;
ImPlot3DPoint T2_ndc = tick_pos_ndc + half_tick_ndc ;
ImVec2 T1_screen = NDCToPixels ( T1_ndc ) ;
ImVec2 T2_screen = NDCToPixels ( T2_ndc ) ;
draw_list - > AddLine ( T1_screen , T2_screen , col_tick ) ;
}
}
}
void RenderTickLabels ( ImDrawList * draw_list , const ImPlot3DPlot & plot , const ImPlot3DPoint * corners , const ImVec2 * corners_pix , const int axis_corners [ 3 ] [ 2 ] ) {
ImVec2 box_center_pix = PlotToPixels ( plot . RangeCenter ( ) ) ;
ImU32 col_tick_txt = GetStyleColorU32 ( ImPlot3DCol_AxisText ) ;
for ( int a = 0 ; a < 3 ; a + + ) {
const ImPlot3DAxis & axis = plot . Axes [ a ] ;
if ( ImPlot3D : : ImHasFlag ( axis . Flags , ImPlot3DAxisFlags_NoTickLabels ) )
continue ;
// Corner indices for this axis
int idx0 = axis_corners [ a ] [ 0 ] ;
int idx1 = axis_corners [ a ] [ 1 ] ;
// If normal to the 2D plot, ignore the ticks
if ( idx0 = = idx1 )
continue ;
// Start and end points of the axis in plot space
ImPlot3DPoint axis_start = corners [ idx0 ] ;
ImPlot3DPoint axis_end = corners [ idx1 ] ;
// Direction vector along the axis
ImPlot3DPoint axis_dir = axis_end - axis_start ;
// Convert axis start and end to screen space
ImVec2 axis_start_pix = corners_pix [ idx0 ] ;
ImVec2 axis_end_pix = corners_pix [ idx1 ] ;
// Screen space axis direction
ImVec2 axis_screen_dir = axis_end_pix - axis_start_pix ;
float axis_length = ImSqrt ( ImLengthSqr ( axis_screen_dir ) ) ;
if ( axis_length ! = 0.0f )
axis_screen_dir / = axis_length ;
else
axis_screen_dir = ImVec2 ( 1.0f , 0.0f ) ; // Default direction if length is zero
// Perpendicular direction in screen space
ImVec2 offset_dir_pix = ImVec2 ( - axis_screen_dir . y , axis_screen_dir . x ) ;
// Make sure direction points away from cube center
ImVec2 box_center_pix = PlotToPixels ( plot . RangeCenter ( ) ) ;
ImVec2 axis_center_pix = ( axis_start_pix + axis_end_pix ) * 0.5f ;
ImVec2 center_to_axis_pix = axis_center_pix - box_center_pix ;
center_to_axis_pix / = ImSqrt ( ImLengthSqr ( center_to_axis_pix ) ) ;
if ( ImDot ( offset_dir_pix , center_to_axis_pix ) < 0.0f )
offset_dir_pix = - offset_dir_pix ;
// Adjust the offset magnitude
float offset_magnitude = 20.0f ; // TODO Calculate based on label size
ImVec2 offset_pix = offset_dir_pix * offset_magnitude ;
// Compute angle perpendicular to axis in screen space
float angle = atan2f ( - axis_screen_dir . y , axis_screen_dir . x ) + IM_PI * 0.5f ;
// Normalize angle to be between -π and π
if ( angle > IM_PI )
angle - = 2 * IM_PI ;
if ( angle < - IM_PI )
angle + = 2 * IM_PI ;
// Adjust angle to keep labels upright
if ( angle > IM_PI * 0.5f )
angle - = IM_PI ;
if ( angle < - IM_PI * 0.5f )
angle + = IM_PI ;
// Loop over ticks
for ( int t = 0 ; t < axis . Ticker . TickCount ( ) ; + + t ) {
const ImPlot3DTick & tick = axis . Ticker . Ticks [ t ] ;
if ( ! tick . ShowLabel )
continue ;
// Compute position along the axis
float t_axis = ( tick . PlotPos - axis . Range . Min ) / ( axis . Range . Max - axis . Range . Min ) ;
ImPlot3DPoint tick_pos = axis_start + axis_dir * t_axis ;
// Convert to pixel coordinates
ImVec2 tick_pos_pix = PlotToPixels ( tick_pos ) ;
// Get the tick label text
const char * label = axis . Ticker . GetText ( tick ) ;
// Adjust label position by offset
ImVec2 label_pos_pix = tick_pos_pix + offset_pix ;
// Render the tick label
AddTextRotated ( draw_list , label_pos_pix , angle , col_tick_txt , label ) ;
}
}
}
void RenderAxisLabels ( ImDrawList * draw_list , const ImPlot3DPlot & plot , const ImPlot3DPoint * corners , const ImVec2 * corners_pix , const int axis_corners [ 3 ] [ 2 ] ) {
ImPlot3DPoint range_center = plot . RangeCenter ( ) ;
for ( int a = 0 ; a < 3 ; a + + ) {
const ImPlot3DAxis & axis = plot . Axes [ a ] ;
if ( ! axis . HasLabel ( ) )
continue ;
const char * label = axis . GetLabel ( ) ;
// Corner indices
int idx0 = axis_corners [ a ] [ 0 ] ;
int idx1 = axis_corners [ a ] [ 1 ] ;
// If normal to the 2D plot, ignore axis label
if ( idx0 = = idx1 )
continue ;
// Position at the end of the axis
ImPlot3DPoint label_pos = ( corners [ idx0 ] + corners [ idx1 ] ) * 0.5f ;
// Add offset
label_pos + = ( label_pos - range_center ) * 0.4f ;
// Convert to pixel coordinates
ImVec2 label_pos_pix = PlotToPixels ( label_pos ) ;
// Adjust label position and angle
ImU32 col_ax_txt = GetStyleColorU32 ( ImPlot3DCol_AxisText ) ;
// Compute text angle
ImVec2 screen_delta = corners_pix [ idx1 ] - corners_pix [ idx0 ] ;
float angle = atan2f ( - screen_delta . y , screen_delta . x ) ;
if ( angle > IM_PI * 0.5f )
angle - = IM_PI ;
if ( angle < - IM_PI * 0.5f )
angle + = IM_PI ;
AddTextRotated ( draw_list , label_pos_pix , angle , col_ax_txt , label ) ;
}
}
// Function to compute active faces based on the rotation
// If the plot is close to 2D, plane_2d is set to the plane index (0 -> YZ, 1 -> XZ, 2 -> XY)
// plane_2d is set to -1 otherwise
void ComputeActiveFaces ( bool * active_faces , const ImPlot3DQuat & rotation , int * plane_2d = nullptr ) {
if ( plane_2d )
* plane_2d = - 1 ;
ImPlot3DPoint rot_face_n [ 3 ] = {
rotation * ImPlot3DPoint ( 1.0f , 0.0f , 0.0f ) ,
rotation * ImPlot3DPoint ( 0.0f , 1.0f , 0.0f ) ,
rotation * ImPlot3DPoint ( 0.0f , 0.0f , 1.0f ) ,
} ;
int num_deg = 0 ; // Check number of planes that are degenerate (seen as a line)
for ( int i = 0 ; i < 3 ; + + i ) {
// Determine the active face based on the Z component
if ( fabs ( rot_face_n [ i ] . z ) < 0.025 ) {
// If aligned with the plane, choose the min face for bottom/left
active_faces [ i ] = rot_face_n [ i ] . x + rot_face_n [ i ] . y < 0.0f ;
num_deg + + ;
} else {
// Otherwise, determine based on the Z component
active_faces [ i ] = rot_face_n [ i ] . z < 0.0f ;
// Set this plane as possible 2d plane
if ( plane_2d )
* plane_2d = i ;
}
}
// Only return 2d plane if there are exactly 2 degenerate planes
if ( num_deg ! = 2 & & plane_2d )
* plane_2d = - 1 ;
}
// Function to compute the box corners in plot space
void ComputeBoxCorners ( ImPlot3DPoint * corners , const ImPlot3DPoint & range_min , const ImPlot3DPoint & range_max ) {
corners [ 0 ] = ImPlot3DPoint ( range_min . x , range_min . y , range_min . z ) ; // 0
corners [ 1 ] = ImPlot3DPoint ( range_max . x , range_min . y , range_min . z ) ; // 1
corners [ 2 ] = ImPlot3DPoint ( range_max . x , range_max . y , range_min . z ) ; // 2
corners [ 3 ] = ImPlot3DPoint ( range_min . x , range_max . y , range_min . z ) ; // 3
corners [ 4 ] = ImPlot3DPoint ( range_min . x , range_min . y , range_max . z ) ; // 4
corners [ 5 ] = ImPlot3DPoint ( range_max . x , range_min . y , range_max . z ) ; // 5
corners [ 6 ] = ImPlot3DPoint ( range_max . x , range_max . y , range_max . z ) ; // 6
corners [ 7 ] = ImPlot3DPoint ( range_min . x , range_max . y , range_max . z ) ; // 7
}
// Function to compute the box corners in pixel space
void ComputeBoxCornersPix ( ImVec2 * corners_pix , const ImPlot3DPoint * corners ) {
for ( int i = 0 ; i < 8 ; i + + ) {
corners_pix [ i ] = PlotToPixels ( corners [ i ] ) ;
}
}
void RenderPlotBox ( ImDrawList * draw_list , const ImPlot3DPlot & plot ) {
// Get plot parameters
const ImRect & plot_area = plot . PlotRect ;
const ImPlot3DQuat & rotation = plot . Rotation ;
ImPlot3DPoint range_min = plot . RangeMin ( ) ;
ImPlot3DPoint range_max = plot . RangeMax ( ) ;
ImPlot3DPoint range_center = plot . RangeCenter ( ) ;
// Compute active faces
bool active_faces [ 3 ] ;
int plane_2d = - 1 ;
ComputeActiveFaces ( active_faces , rotation , & plane_2d ) ;
bool is_2d = plane_2d ! = - 1 ;
// Compute box corners in plot space
ImPlot3DPoint corners [ 8 ] ;
ComputeBoxCorners ( corners , range_min , range_max ) ;
// Compute box corners in pixel space
ImVec2 corners_pix [ 8 ] ;
ComputeBoxCornersPix ( corners_pix , corners ) ;
// Compute axes start and end corners (given current rotation)
int axis_corners [ 3 ] [ 2 ] ;
if ( is_2d ) {
int face = plane_2d + 3 * active_faces [ plane_2d ] ; // Face of the 2D plot
int common_edges [ 2 ] = { - 1 , - 1 } ; // Edges shared by the 3 faces
// Find the common edges between the 3 faces
for ( int i = 0 ; i < 4 ; i + + ) {
int edge = face_edges [ face ] [ i ] ;
for ( int j = 0 ; j < 2 ; j + + ) {
int axis = ( plane_2d + 1 + j ) % 3 ;
int face_idx = axis + active_faces [ axis ] * 3 ;
for ( int k = 0 ; k < 4 ; k + + ) {
if ( face_edges [ face_idx ] [ k ] = = edge ) {
common_edges [ j ] = edge ;
break ;
}
}
}
}
// Get corners from 2 edges (origin is the corner in common)
int origin_corner = - 1 ;
int x_corner = - 1 ;
int y_corner = - 1 ;
for ( int i = 0 ; i < 2 ; i + + )
for ( int j = 0 ; j < 2 ; j + + )
if ( edges [ common_edges [ 0 ] ] [ i ] = = edges [ common_edges [ 1 ] ] [ j ] ) {
origin_corner = edges [ common_edges [ 0 ] ] [ i ] ;
x_corner = edges [ common_edges [ 0 ] ] [ ! i ] ;
y_corner = edges [ common_edges [ 1 ] ] [ ! j ] ;
}
// Swap x and y if they are flipped
ImVec2 x_vec = corners_pix [ x_corner ] - corners_pix [ origin_corner ] ;
ImVec2 y_vec = corners_pix [ y_corner ] - corners_pix [ origin_corner ] ;
if ( y_vec . x > x_vec . x )
ImSwap ( x_corner , y_corner ) ;
// Check which 3d axis the 2d axis refers to
ImPlot3DPoint origin_3d = corners [ origin_corner ] ;
ImPlot3DPoint x_3d = ( corners [ x_corner ] - origin_3d ) . Normalized ( ) ;
ImPlot3DPoint y_3d = ( corners [ y_corner ] - origin_3d ) . Normalized ( ) ;
int x_axis = - 1 ;
bool x_inverted = false ;
int y_axis = - 1 ;
bool y_inverted = false ;
for ( int i = 0 ; i < 2 ; i + + ) {
int axis_i = ( plane_2d + 1 + i ) % 3 ;
if ( y_axis ! = - 1 | | ( ImAbs ( x_3d [ axis_i ] ) > 1e-8 f & & x_axis = = - 1 ) ) {
x_axis = axis_i ;
x_inverted = x_3d [ axis_i ] < 0.0f ;
} else {
y_axis = axis_i ;
y_inverted = y_3d [ axis_i ] < 0.0f ;
}
}
// Set the 3d axis corners based on the 2d axis corners
axis_corners [ plane_2d ] [ 0 ] = - 1 ;
axis_corners [ plane_2d ] [ 1 ] = - 1 ;
if ( x_inverted ) {
axis_corners [ x_axis ] [ 0 ] = x_corner ;
axis_corners [ x_axis ] [ 1 ] = origin_corner ;
} else {
axis_corners [ x_axis ] [ 0 ] = origin_corner ;
axis_corners [ x_axis ] [ 1 ] = x_corner ;
}
if ( y_inverted ) {
axis_corners [ y_axis ] [ 0 ] = y_corner ;
axis_corners [ y_axis ] [ 1 ] = origin_corner ;
} else {
axis_corners [ y_axis ] [ 0 ] = origin_corner ;
axis_corners [ y_axis ] [ 1 ] = y_corner ;
}
} else {
int index = ( active_faces [ 0 ] < < 2 ) | ( active_faces [ 1 ] < < 1 ) | ( active_faces [ 2 ] ) ;
for ( int a = 0 ; a < 3 ; a + + ) {
axis_corners [ a ] [ 0 ] = axis_corners_lookup_3d [ index ] [ a ] [ 0 ] ;
axis_corners [ a ] [ 1 ] = axis_corners_lookup_3d [ index ] [ a ] [ 1 ] ;
}
}
// Render components
RenderPlotBackground ( draw_list , plot , corners_pix , active_faces , plane_2d ) ;
RenderPlotBorder ( draw_list , plot , corners_pix , active_faces , plane_2d ) ;
RenderGrid ( draw_list , plot , corners , active_faces , plane_2d ) ;
RenderTickMarks ( draw_list , plot , corners , corners_pix , axis_corners , plane_2d ) ;
RenderTickLabels ( draw_list , plot , corners , corners_pix , axis_corners ) ;
RenderAxisLabels ( draw_list , plot , corners , corners_pix , axis_corners ) ;
}
//-----------------------------------------------------------------------------
// [SECTION] Formatter
//-----------------------------------------------------------------------------
int Formatter_Default ( float value , char * buff , int size , void * data ) {
char * fmt = ( char * ) data ;
return ImFormatString ( buff , size , fmt , value ) ;
}
//------------------------------------------------------------------------------
// [SECTION] Locator
//------------------------------------------------------------------------------
double NiceNum ( double x , bool round ) {
double f ;
double nf ;
int expv = ( int ) floor ( ImLog10 ( x ) ) ;
f = x / ImPow ( 10.0 , ( double ) expv ) ;
if ( round )
if ( f < 1.5 )
nf = 1 ;
else if ( f < 3 )
nf = 2 ;
else if ( f < 7 )
nf = 5 ;
else
nf = 10 ;
else if ( f < = 1 )
nf = 1 ;
else if ( f < = 2 )
nf = 2 ;
else if ( f < = 5 )
nf = 5 ;
else
nf = 10 ;
return nf * ImPow ( 10.0 , expv ) ;
}
void Locator_Default ( ImPlot3DTicker & ticker , const ImPlot3DRange & range , ImPlot3DFormatter formatter , void * formatter_data ) {
if ( range . Min = = range . Max )
return ;
const int nMinor = 5 ;
const int nMajor = 3 ;
const int max_ticks_labels = 7 ;
const double nice_range = NiceNum ( range . Size ( ) * 0.99 , false ) ;
const double interval = NiceNum ( nice_range / ( nMajor - 1 ) , true ) ;
const double graphmin = floor ( range . Min / interval ) * interval ;
const double graphmax = ceil ( range . Max / interval ) * interval ;
bool first_major_set = false ;
int first_major_idx = 0 ;
const int idx0 = ticker . TickCount ( ) ; // ticker may have user custom ticks
ImVec2 total_size ( 0 , 0 ) ;
for ( double major = graphmin ; major < graphmax + 0.5 * interval ; major + = interval ) {
// is this zero? combat zero formatting issues
if ( major - interval < 0 & & major + interval > 0 )
major = 0 ;
if ( range . Contains ( major ) ) {
if ( ! first_major_set ) {
first_major_idx = ticker . TickCount ( ) ;
first_major_set = true ;
}
total_size + = ticker . AddTick ( major , true , true , formatter , formatter_data ) . LabelSize ;
}
for ( int i = 1 ; i < nMinor ; + + i ) {
double minor = major + i * interval / nMinor ;
if ( range . Contains ( minor ) ) {
total_size + = ticker . AddTick ( minor , false , true , formatter , formatter_data ) . LabelSize ;
}
}
}
// Prune tick labels
if ( ticker . TickCount ( ) > max_ticks_labels ) {
for ( int i = first_major_idx - 1 ; i > = idx0 ; i - = 2 )
ticker . Ticks [ i ] . ShowLabel = false ;
for ( int i = first_major_idx + 1 ; i < ticker . TickCount ( ) ; i + = 2 )
ticker . Ticks [ i ] . ShowLabel = false ;
}
}
//------------------------------------------------------------------------------
// [SECTION] Context Menus
//------------------------------------------------------------------------------
bool ShowLegendContextMenu ( ImPlot3DLegend & legend , bool visible ) {
const float s = ImGui : : GetFrameHeight ( ) ;
bool ret = false ;
if ( ImGui : : Checkbox ( " Show " , & visible ) )
ret = true ;
if ( ImGui : : RadioButton ( " H " , ImPlot3D : : ImHasFlag ( legend . Flags , ImPlot3DLegendFlags_Horizontal ) ) )
legend . Flags | = ImPlot3DLegendFlags_Horizontal ;
ImGui : : SameLine ( ) ;
if ( ImGui : : RadioButton ( " V " , ! ImPlot3D : : ImHasFlag ( legend . Flags , ImPlot3DLegendFlags_Horizontal ) ) )
legend . Flags & = ~ ImPlot3DLegendFlags_Horizontal ;
ImGui : : PushStyleVar ( ImGuiStyleVar_ItemSpacing , ImVec2 ( 2 , 2 ) ) ;
// clang-format off
if ( ImGui : : Button ( " NW " , ImVec2 ( 1.5f * s , s ) ) ) { legend . Location = ImPlot3DLocation_NorthWest ; } ImGui : : SameLine ( ) ;
if ( ImGui : : Button ( " N " , ImVec2 ( 1.5f * s , s ) ) ) { legend . Location = ImPlot3DLocation_North ; } ImGui : : SameLine ( ) ;
if ( ImGui : : Button ( " NE " , ImVec2 ( 1.5f * s , s ) ) ) { legend . Location = ImPlot3DLocation_NorthEast ; }
if ( ImGui : : Button ( " W " , ImVec2 ( 1.5f * s , s ) ) ) { legend . Location = ImPlot3DLocation_West ; } ImGui : : SameLine ( ) ;
if ( ImGui : : InvisibleButton ( " C " , ImVec2 ( 1.5f * s , s ) ) ) { } ImGui : : SameLine ( ) ;
if ( ImGui : : Button ( " E " , ImVec2 ( 1.5f * s , s ) ) ) { legend . Location = ImPlot3DLocation_East ; }
if ( ImGui : : Button ( " SW " , ImVec2 ( 1.5f * s , s ) ) ) { legend . Location = ImPlot3DLocation_SouthWest ; } ImGui : : SameLine ( ) ;
if ( ImGui : : Button ( " S " , ImVec2 ( 1.5f * s , s ) ) ) { legend . Location = ImPlot3DLocation_South ; } ImGui : : SameLine ( ) ;
if ( ImGui : : Button ( " SE " , ImVec2 ( 1.5f * s , s ) ) ) { legend . Location = ImPlot3DLocation_SouthEast ; }
// clang-format on
ImGui : : PopStyleVar ( ) ;
return ret ;
}
void ShowAxisContextMenu ( ImPlot3DAxis & axis ) {
ImGui : : PushItemWidth ( 75 ) ;
bool always_locked = axis . IsRangeLocked ( ) | | axis . IsAutoFitting ( ) ;
bool label = axis . HasLabel ( ) ;
bool grid = axis . HasGridLines ( ) ;
bool ticks = axis . HasTickMarks ( ) ;
bool labels = axis . HasTickLabels ( ) ;
double drag_speed = ( axis . Range . Size ( ) < = FLT_EPSILON ) ? FLT_EPSILON * 1.0e+13 : 0.01 * axis . Range . Size ( ) ; // recover from almost equal axis limits.
ImGui : : BeginDisabled ( always_locked ) ;
ImGui : : CheckboxFlags ( " ##LockMin " , ( unsigned int * ) & axis . Flags , ImPlot3DAxisFlags_LockMin ) ;
ImGui : : EndDisabled ( ) ;
ImGui : : SameLine ( ) ;
ImGui : : BeginDisabled ( axis . IsLockedMin ( ) | | always_locked ) ;
float temp_min = axis . Range . Min ;
if ( ImGui : : DragFloat ( " Min " , & temp_min , ( float ) drag_speed , - HUGE_VAL , axis . Range . Max - FLT_EPSILON ) ) {
axis . SetMin ( temp_min , true ) ;
}
ImGui : : EndDisabled ( ) ;
ImGui : : BeginDisabled ( always_locked ) ;
ImGui : : CheckboxFlags ( " ##LockMax " , ( unsigned int * ) & axis . Flags , ImPlot3DAxisFlags_LockMax ) ;
ImGui : : EndDisabled ( ) ;
ImGui : : SameLine ( ) ;
ImGui : : BeginDisabled ( axis . IsLockedMax ( ) | | always_locked ) ;
float temp_max = axis . Range . Max ;
if ( ImGui : : DragFloat ( " Max " , & temp_max , ( float ) drag_speed , axis . Range . Min + FLT_EPSILON , HUGE_VAL ) ) {
axis . SetMax ( temp_max , true ) ;
}
ImGui : : EndDisabled ( ) ;
ImGui : : Separator ( ) ;
// Flags
ImGui : : CheckboxFlags ( " Auto-Fit " , ( unsigned int * ) & axis . Flags , ImPlot3DAxisFlags_AutoFit ) ;
ImGui : : Separator ( ) ;
ImGui : : BeginDisabled ( axis . Label . empty ( ) ) ;
if ( ImGui : : Checkbox ( " Label " , & label ) )
ImFlipFlag ( axis . Flags , ImPlot3DAxisFlags_NoLabel ) ;
ImGui : : EndDisabled ( ) ;
if ( ImGui : : Checkbox ( " Grid Lines " , & grid ) )
ImFlipFlag ( axis . Flags , ImPlot3DAxisFlags_NoGridLines ) ;
if ( ImGui : : Checkbox ( " Tick Marks " , & ticks ) )
ImFlipFlag ( axis . Flags , ImPlot3DAxisFlags_NoTickMarks ) ;
if ( ImGui : : Checkbox ( " Tick Labels " , & labels ) )
ImFlipFlag ( axis . Flags , ImPlot3DAxisFlags_NoTickLabels ) ;
}
void ShowPlotContextMenu ( ImPlot3DPlot & plot ) {
ImPlot3DContext & gp = * GImPlot3D ;
const bool owns_legend = gp . CurrentItems = = & plot . Items ;
char buf [ 16 ] = { } ;
const char * axis_labels [ 3 ] = { " X-Axis " , " Y-Axis " , " Z-Axis " } ;
for ( int i = 0 ; i < 3 ; i + + ) {
ImPlot3DAxis & axis = plot . Axes [ i ] ;
ImGui : : PushID ( i ) ;
ImFormatString ( buf , sizeof ( buf ) - 1 , i = = 0 ? " X-Axis " : " X-Axis %d " , i + 1 ) ;
if ( ImGui : : BeginMenu ( axis . HasLabel ( ) ? axis . GetLabel ( ) : axis_labels [ i ] ) ) {
ShowAxisContextMenu ( axis ) ;
ImGui : : EndMenu ( ) ;
}
ImGui : : PopID ( ) ;
}
ImGui : : Separator ( ) ;
if ( ( ImGui : : BeginMenu ( " Legend " ) ) ) {
if ( ShowLegendContextMenu ( plot . Items . Legend , ! ImPlot3D : : ImHasFlag ( plot . Flags , ImPlot3DFlags_NoLegend ) ) )
ImFlipFlag ( plot . Flags , ImPlot3DFlags_NoLegend ) ;
ImGui : : EndMenu ( ) ;
}
if ( ( ImGui : : BeginMenu ( " Settings " ) ) ) {
ImGui : : BeginDisabled ( plot . Title . empty ( ) ) ;
if ( ImGui : : MenuItem ( " Title " , nullptr , plot . HasTitle ( ) ) )
ImFlipFlag ( plot . Flags , ImPlot3DFlags_NoTitle ) ;
ImGui : : EndDisabled ( ) ;
ImGui : : EndMenu ( ) ;
}
}
//-----------------------------------------------------------------------------
// [SECTION] Begin/End Plot
//-----------------------------------------------------------------------------
bool BeginPlot ( const char * title_id , const ImVec2 & size , ImPlot3DFlags flags ) {
IMPLOT3D_CHECK_CTX ( ) ;
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot = = nullptr , " Mismatched BeginPlot()/EndPlot()! " ) ;
// Get window
ImGuiContext & g = * GImGui ;
ImGuiWindow * window = g . CurrentWindow ;
// Skip if needed
if ( window - > SkipItems )
return false ;
// Get or create plot
const ImGuiID ID = window - > GetID ( title_id ) ;
const bool just_created = gp . Plots . GetByKey ( ID ) = = nullptr ;
gp . CurrentPlot = gp . Plots . GetOrAddByKey ( ID ) ;
gp . CurrentItems = & gp . CurrentPlot - > Items ;
ImPlot3DPlot & plot = * gp . CurrentPlot ;
// Populate plot
plot . ID = ID ;
plot . JustCreated = just_created ;
if ( just_created ) {
plot . Rotation = init_rotation ;
plot . FitThisFrame = true ;
for ( int i = 0 ; i < 3 ; i + + ) {
plot . Axes [ i ] = ImPlot3DAxis ( ) ;
plot . Axes [ i ] . FitThisFrame = true ;
}
}
if ( plot . PreviousFlags ! = flags )
plot . Flags = flags ;
plot . PreviousFlags = flags ;
plot . SetupLocked = false ;
plot . OpenContextThisFrame = false ;
// Populate title
plot . SetTitle ( title_id ) ;
// Calculate frame size
ImVec2 frame_size = ImGui : : CalcItemSize ( size , gp . Style . PlotDefaultSize . x , gp . Style . PlotDefaultSize . y ) ;
if ( frame_size . x < gp . Style . PlotMinSize . x & & size . x < 0.0f )
frame_size . x = gp . Style . PlotMinSize . x ;
if ( frame_size . y < gp . Style . PlotMinSize . y & & size . y < 0.0f )
frame_size . y = gp . Style . PlotMinSize . y ;
// Create child window to capture scroll
ImGui : : BeginChild ( title_id , frame_size , false , ImGuiWindowFlags_NoScrollbar ) ;
window = ImGui : : GetCurrentWindow ( ) ;
window - > ScrollMax . y = 1.0f ;
plot . FrameRect = ImRect ( window - > DC . CursorPos , window - > DC . CursorPos + frame_size ) ;
ImGui : : ItemSize ( plot . FrameRect ) ;
if ( ! ImGui : : ItemAdd ( plot . FrameRect , plot . ID , & plot . FrameRect ) ) {
gp . CurrentPlot = nullptr ;
gp . CurrentItems = nullptr ;
ImGui : : EndChild ( ) ;
return false ;
}
// Reset legend
plot . Items . Legend . Reset ( ) ;
// Push frame rect clipping
ImGui : : PushClipRect ( plot . FrameRect . Min , plot . FrameRect . Max , true ) ;
plot . DrawList . _Flags = window - > DrawList - > Flags ;
plot . DrawList . _SharedData = ImGui : : GetDrawListSharedData ( ) ;
return true ;
}
void EndPlot ( ) {
IMPLOT3D_CHECK_CTX ( ) ;
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " Mismatched BeginPlot()/EndPlot()! " ) ;
ImPlot3DPlot & plot = * gp . CurrentPlot ;
// Move triangles from 3D draw list to ImGui draw list
plot . DrawList . SortedMoveToImGuiDrawList ( ) ;
// Handle data fitting
if ( plot . FitThisFrame ) {
plot . FitThisFrame = false ;
for ( int i = 0 ; i < 3 ; i + + ) {
if ( plot . Axes [ i ] . FitThisFrame ) {
plot . Axes [ i ] . FitThisFrame = false ;
plot . Axes [ i ] . ApplyFit ( ) ;
}
}
}
// Lock setup if not already done
SetupLock ( ) ;
// Reset legend hover
plot . Items . Legend . Hovered = false ;
// Render legend
RenderLegend ( ) ;
// Render mouse position
RenderMousePos ( ) ;
// Legend context menu
if ( ImGui : : BeginPopup ( " ##LegendContext " ) ) {
ImGui : : Text ( " Legend " ) ;
ImGui : : Separator ( ) ;
if ( ShowLegendContextMenu ( plot . Items . Legend , ! ImPlot3D : : ImHasFlag ( plot . Flags , ImPlot3DFlags_NoLegend ) ) )
ImFlipFlag ( plot . Flags , ImPlot3DFlags_NoLegend ) ;
ImGui : : EndPopup ( ) ;
}
// Axis context menus
static const char * axis_contexts [ 3 ] = { " ##XAxisContext " , " ##YAxisContext " , " ##ZAxisContext " } ;
for ( int i = 0 ; i < 3 ; i + + ) {
ImPlot3DAxis & axis = plot . Axes [ i ] ;
if ( ImGui : : BeginPopup ( axis_contexts [ i ] ) ) {
ImGui : : Text ( axis . HasLabel ( ) ? axis . GetLabel ( ) : " %c-Axis " , ' X ' + i ) ;
ImGui : : Separator ( ) ;
ShowAxisContextMenu ( axis ) ;
ImGui : : EndPopup ( ) ;
}
}
// Plot context menu
if ( ImGui : : BeginPopup ( " ##PlotContext " ) ) {
ShowPlotContextMenu ( plot ) ;
ImGui : : EndPopup ( ) ;
}
// Pop frame rect clipping
ImGui : : PopClipRect ( ) ;
// End child window
ImGui : : EndChild ( ) ;
// Reset current plot
gp . CurrentPlot = nullptr ;
gp . CurrentItems = nullptr ;
// Reset the plot items for the next frame
for ( int i = 0 ; i < plot . Items . GetItemCount ( ) ; i + + )
plot . Items . GetItemByIndex ( i ) - > SeenThisFrame = false ;
}
//-----------------------------------------------------------------------------
// [SECTION] Setup
//-----------------------------------------------------------------------------
void SetupAxis ( ImAxis3D idx , const char * label , ImPlot3DAxisFlags flags ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr & & ! gp . CurrentPlot - > SetupLocked ,
" SetupAxis() needs to be called after BeginPlot() and before any setup locking functions (e.g. PlotX)! " ) ;
// Get plot and axis
ImPlot3DPlot & plot = * gp . CurrentPlot ;
ImPlot3DAxis & axis = plot . Axes [ idx ] ;
if ( axis . PreviousFlags ! = flags )
axis . Flags = flags ;
axis . PreviousFlags = flags ;
axis . SetLabel ( label ) ;
}
void SetupAxisLimits ( ImAxis3D idx , double min_lim , double max_lim , ImPlot3DCond cond ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr & & ! gp . CurrentPlot - > SetupLocked ,
" SetupAxisLimits() needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)! " ) ; // get plot and axis
ImPlot3DPlot & plot = * gp . CurrentPlot ;
ImPlot3DAxis & axis = plot . Axes [ idx ] ;
if ( ! plot . Initialized | | cond = = ImPlot3DCond_Always ) {
axis . SetRange ( min_lim , max_lim ) ;
axis . RangeCond = cond ;
axis . FitThisFrame = false ;
}
}
void SetupAxes ( const char * x_label , const char * y_label , const char * z_label , ImPlot3DAxisFlags x_flags , ImPlot3DAxisFlags y_flags , ImPlot3DAxisFlags z_flags ) {
SetupAxis ( ImAxis3D_X , x_label , x_flags ) ;
SetupAxis ( ImAxis3D_Y , y_label , y_flags ) ;
SetupAxis ( ImAxis3D_Z , z_label , z_flags ) ;
}
void SetupAxesLimits ( double x_min , double x_max , double y_min , double y_max , double z_min , double z_max , ImPlot3DCond cond ) {
SetupAxisLimits ( ImAxis3D_X , x_min , x_max , cond ) ;
SetupAxisLimits ( ImAxis3D_Y , y_min , y_max , cond ) ;
SetupAxisLimits ( ImAxis3D_Z , z_min , z_max , cond ) ;
if ( cond = = ImPlot3DCond_Once )
GImPlot3D - > CurrentPlot - > FitThisFrame = false ;
}
void SetupLegend ( ImPlot3DLocation location , ImPlot3DLegendFlags flags ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr & & ! gp . CurrentPlot - > SetupLocked ,
" SetupLegend() needs to be called after BeginPlot() and before any setup locking functions (e.g. PlotX)! " ) ;
IM_ASSERT_USER_ERROR ( gp . CurrentItems ! = nullptr ,
" SetupLegend() needs to be called within an itemized context! " ) ;
ImPlot3DLegend & legend = gp . CurrentItems - > Legend ;
if ( legend . PreviousLocation ! = location )
legend . Location = location ;
legend . PreviousLocation = location ;
if ( legend . PreviousFlags ! = flags )
legend . Flags = flags ;
legend . PreviousFlags = flags ;
}
//-----------------------------------------------------------------------------
// [SECTION] Plot Utils
//-----------------------------------------------------------------------------
ImPlot3DPlot * GetCurrentPlot ( ) {
return GImPlot3D - > CurrentPlot ;
}
void BustPlotCache ( ) {
ImPlot3DContext & gp = * GImPlot3D ;
gp . Plots . Clear ( ) ;
}
ImVec2 PlotToPixels ( const ImPlot3DPoint & point ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " PlotToPixels() needs to be called between BeginPlot() and EndPlot()! " ) ;
return NDCToPixels ( PlotToNDC ( point ) ) ;
}
ImVec2 PlotToPixels ( double x , double y , double z ) {
return PlotToPixels ( ImPlot3DPoint ( x , y , z ) ) ;
}
ImPlot3DRay PixelsToPlotRay ( const ImVec2 & pix ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " PixelsToPlotRay() needs to be called between BeginPlot() and EndPlot()! " ) ;
return NDCRayToPlotRay ( PixelsToNDCRay ( pix ) ) ;
}
ImPlot3DRay PixelsToPlotRay ( double x , double y ) {
return PixelsToPlotRay ( ImVec2 ( x , y ) ) ;
}
ImPlot3DPoint PixelsToPlotPlane ( const ImVec2 & pix , ImPlane3D plane , bool mask ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " PixelsToPlotPlane() needs to be called between BeginPlot() and EndPlot()! " ) ;
ImPlot3DPlot & plot = * gp . CurrentPlot ;
ImPlot3DRay ray = PixelsToNDCRay ( pix ) ;
const ImPlot3DPoint & O = ray . Origin ;
const ImPlot3DPoint & D = ray . Direction ;
// Helper lambda to check intersection with a given coordinate and return intersection point if valid.
auto IntersectPlane = [ & ] ( float coord ) - > ImPlot3DPoint {
// Solve for t in O[axis] + D[axis]*t = coord
float denom = 0.0f ;
float numer = 0.0f ;
if ( plane = = ImPlane3D_YZ ) {
denom = D . x ;
numer = coord - O . x ;
} else if ( plane = = ImPlane3D_XZ ) {
denom = D . y ;
numer = coord - O . y ;
} else if ( plane = = ImPlane3D_XY ) {
denom = D . z ;
numer = coord - O . z ;
}
if ( ImAbs ( denom ) < 1e-12 f ) {
// Ray is parallel or nearly parallel to the plane
return ImPlot3DPoint ( NAN , NAN , NAN ) ;
}
float t = numer / denom ;
if ( t < 0.0f ) {
// Intersection behind the ray origin
return ImPlot3DPoint ( NAN , NAN , NAN ) ;
}
return O + D * t ;
} ;
// Helper lambda to check if point P is within the plot box
auto InRange = [ & ] ( const ImPlot3DPoint & P ) {
return P . x > = - 0.5 & & P . x < = 0.5 & &
P . y > = - 0.5 & & P . y < = 0.5 & &
P . z > = - 0.5 & & P . z < = 0.5 ;
} ;
// Compute which plane to intersect with
bool active_faces [ 3 ] ;
ComputeActiveFaces ( active_faces , plot . Rotation ) ;
// Calculate intersection point with the planes
ImPlot3DPoint P = IntersectPlane ( active_faces [ plane ] ? 0.5 : - 0.5 ) ;
if ( P . IsNaN ( ) )
return P ;
// Handle mask (if one of the intersections is out of range, set it to NAN)
if ( mask ) {
switch ( plane ) {
case ImPlane3D_YZ :
if ( ! InRange ( ImPlot3DPoint ( 0.0 , P . y , P . z ) ) )
return ImPlot3DPoint ( NAN , NAN , NAN ) ;
break ;
case ImPlane3D_XZ :
if ( ! InRange ( ImPlot3DPoint ( P . x , 0.0 , P . z ) ) )
return ImPlot3DPoint ( NAN , NAN , NAN ) ;
break ;
case ImPlane3D_XY :
if ( ! InRange ( ImPlot3DPoint ( P . x , P . y , 0.0 ) ) )
return ImPlot3DPoint ( NAN , NAN , NAN ) ;
break ;
}
}
return NDCToPlot ( P ) ;
}
ImPlot3DPoint PixelsToPlotPlane ( double x , double y , ImPlane3D plane , bool mask ) {
return PixelsToPlotPlane ( ImVec2 ( x , y ) , plane , mask ) ;
}
ImVec2 GetPlotPos ( ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " GetPlotPos() needs to be called between BeginPlot() and EndPlot()! " ) ;
SetupLock ( ) ;
return gp . CurrentPlot - > PlotRect . Min ;
}
ImVec2 GetPlotSize ( ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " GetPlotSize() needs to be called between BeginPlot() and EndPlot()! " ) ;
SetupLock ( ) ;
return gp . CurrentPlot - > PlotRect . GetSize ( ) ;
}
ImVec2 GetFramePos ( ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " GetFramePos() needs to be called between BeginPlot() and EndPlot()! " ) ;
return gp . CurrentPlot - > FrameRect . Min ;
}
ImVec2 GetFrameSize ( ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " GetFrameSize() needs to be called between BeginPlot() and EndPlot()! " ) ;
return gp . CurrentPlot - > FrameRect . GetSize ( ) ;
}
ImPlot3DPoint PlotToNDC ( const ImPlot3DPoint & point ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " PlotToNDC() needs to be called between BeginPlot() and EndPlot()! " ) ;
ImPlot3DPlot & plot = * gp . CurrentPlot ;
SetupLock ( ) ;
ImPlot3DPoint ndc_point ;
for ( int i = 0 ; i < 3 ; i + + )
ndc_point [ i ] = plot . Axes [ i ] . PlotToNDC ( point [ i ] ) ;
return ndc_point ;
}
ImPlot3DPoint NDCToPlot ( const ImPlot3DPoint & point ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " NDCToPlot() needs to be called between BeginPlot() and EndPlot()! " ) ;
ImPlot3DPlot & plot = * gp . CurrentPlot ;
SetupLock ( ) ;
ImPlot3DPoint plot_point ;
for ( int i = 0 ; i < 3 ; i + + )
plot_point [ i ] = plot . Axes [ i ] . NDCToPlot ( point [ i ] ) ;
return plot_point ;
}
ImVec2 NDCToPixels ( const ImPlot3DPoint & point ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " NDCToPixels() needs to be called between BeginPlot() and EndPlot()! " ) ;
ImPlot3DPlot & plot = * gp . CurrentPlot ;
SetupLock ( ) ;
float zoom = ImMin ( plot . PlotRect . GetWidth ( ) , plot . PlotRect . GetHeight ( ) ) / 1.8f ;
ImVec2 center = plot . PlotRect . GetCenter ( ) ;
ImPlot3DPoint point_pix = zoom * ( plot . Rotation * point ) ;
point_pix . y * = - 1.0f ; // Invert y-axis
point_pix . x + = center . x ;
point_pix . y + = center . y ;
return { point_pix . x , point_pix . y } ;
}
ImPlot3DRay PixelsToNDCRay ( const ImVec2 & pix ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " PixelsToNDCRay() needs to be called between BeginPlot() and EndPlot()! " ) ;
ImPlot3DPlot & plot = * gp . CurrentPlot ;
SetupLock ( ) ;
// Calculate zoom factor and plot center
float zoom = ImMin ( plot . PlotRect . GetWidth ( ) , plot . PlotRect . GetHeight ( ) ) / 1.8f ;
ImVec2 center = plot . PlotRect . GetCenter ( ) ;
// Undo screen transformations to get back to NDC space
float x = ( pix . x - center . x ) / zoom ;
float y = - ( pix . y - center . y ) / zoom ; // Invert y-axis
// Define near and far points in NDC space along the z-axis
ImPlot3DPoint ndc_near = plot . Rotation . Inverse ( ) * ImPlot3DPoint ( x , y , - 10.0f ) ;
ImPlot3DPoint ndc_far = plot . Rotation . Inverse ( ) * ImPlot3DPoint ( x , y , 10.0f ) ;
// Create the ray in NDC space
ImPlot3DRay ndc_ray ;
ndc_ray . Origin = ndc_near ;
ndc_ray . Direction = ( ndc_far - ndc_near ) . Normalized ( ) ;
return ndc_ray ;
}
ImPlot3DRay NDCRayToPlotRay ( const ImPlot3DRay & ray ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " NDCRayToPlotRay() needs to be called between BeginPlot() and EndPlot()! " ) ;
ImPlot3DPlot & plot = * gp . CurrentPlot ;
SetupLock ( ) ;
// Convert NDC origin and a point along the ray to plot coordinates
ImPlot3DPoint plot_origin = NDCToPlot ( ray . Origin ) ;
ImPlot3DPoint ndc_point_along_ray = ray . Origin + ray . Direction ;
ImPlot3DPoint plot_point_along_ray = NDCToPlot ( ndc_point_along_ray ) ;
// Compute the direction in plot coordinates
ImPlot3DPoint plot_direction = ( plot_point_along_ray - plot_origin ) . Normalized ( ) ;
// Create the ray in plot coordinates
ImPlot3DRay plot_ray ;
plot_ray . Origin = plot_origin ;
plot_ray . Direction = plot_direction ;
return plot_ray ;
}
//-----------------------------------------------------------------------------
// [SECTION] Setup Utils
//-----------------------------------------------------------------------------
static const float MOUSE_CURSOR_DRAG_THRESHOLD = 5.0f ;
static const float ANIMATION_ANGULAR_VELOCITY = 2 * 3.1415f ;
void HandleInput ( ImPlot3DPlot & plot ) {
ImGuiIO & IO = ImGui : : GetIO ( ) ;
// clang-format off
const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowOverlap
| ImGuiButtonFlags_PressedOnClick
| ImGuiButtonFlags_PressedOnDoubleClick
| ImGuiButtonFlags_MouseButtonLeft
| ImGuiButtonFlags_MouseButtonRight
| ImGuiButtonFlags_MouseButtonMiddle ;
// clang-format on
const bool plot_clicked = ImGui : : ButtonBehavior ( plot . PlotRect , plot . ID , & plot . Hovered , & plot . Held , plot_button_flags ) ;
# if (IMGUI_VERSION_NUM < 18966)
ImGui : : SetItemAllowOverlap ( ) ; // Handled by ButtonBehavior()
# endif
// State
const ImVec2 rot_drag = ImGui : : GetMouseDragDelta ( ImGuiMouseButton_Right ) ;
const bool rotating = ImLengthSqr ( rot_drag ) > MOUSE_CURSOR_DRAG_THRESHOLD ;
// Check if any axis/plane is hovered
const ImPlot3DQuat & rotation = plot . Rotation ;
ImPlot3DPoint range_min = plot . RangeMin ( ) ;
ImPlot3DPoint range_max = plot . RangeMax ( ) ;
bool active_faces [ 3 ] ;
int plane_2d = - 1 ;
ComputeActiveFaces ( active_faces , rotation , & plane_2d ) ;
ImPlot3DPoint corners [ 8 ] ;
ComputeBoxCorners ( corners , range_min , range_max ) ;
ImVec2 corners_pix [ 8 ] ;
ComputeBoxCornersPix ( corners_pix , corners ) ;
int hovered_plane_idx = - 1 ;
int hovered_plane = GetMouseOverPlane ( plot , active_faces , corners_pix , & hovered_plane_idx ) ;
int hovered_edge_idx = - 1 ;
int hovered_axis = GetMouseOverAxis ( plot , active_faces , corners_pix , plane_2d , & hovered_edge_idx ) ;
if ( hovered_axis ! = - 1 ) {
hovered_plane_idx = - 1 ;
hovered_plane = - 1 ;
}
// If the user is no longer pressing the translation/zoom buttons, set axes as not held
if ( ! ImGui : : IsMouseDown ( ImGuiMouseButton_Left ) & & ! ImGui : : IsMouseDown ( ImGuiMouseButton_Middle ) ) {
for ( int i = 0 ; i < 3 ; i + + )
plot . Axes [ i ] . Held = false ;
}
// Reset held edge/plane indices (it will be set if mouse button is down)
if ( ! plot . Held ) {
plot . HeldEdgeIdx = - 1 ;
plot . HeldPlaneIdx = - 1 ;
}
// Check which axes should be transformed (fit/zoom/translate)
bool any_axis_held = plot . Axes [ 0 ] . Held | | plot . Axes [ 1 ] . Held | | plot . Axes [ 2 ] . Held ;
static bool transform_axis [ 3 ] = { false , false , false } ;
if ( ! any_axis_held ) {
// Only update the transformation axes if the user is not already performing a transformation
transform_axis [ 0 ] = transform_axis [ 1 ] = transform_axis [ 2 ] = false ;
if ( hovered_axis ! = - 1 ) {
transform_axis [ hovered_axis ] = true ;
} else if ( hovered_plane ! = - 1 ) {
transform_axis [ ( hovered_plane + 1 ) % 3 ] = true ;
transform_axis [ ( hovered_plane + 2 ) % 3 ] = true ;
} else {
transform_axis [ 0 ] = transform_axis [ 1 ] = transform_axis [ 2 ] = true ;
}
}
// Handle translation/zoom fit with double click
if ( plot_clicked & & ImGui : : IsMouseDoubleClicked ( ImGuiMouseButton_Left ) | | ImGui : : IsMouseDoubleClicked ( ImGuiMouseButton_Middle ) ) {
plot . FitThisFrame = true ;
for ( int i = 0 ; i < 3 ; i + + )
plot . Axes [ i ] . FitThisFrame = transform_axis [ i ] ;
}
// Handle auto fit
for ( int i = 0 ; i < 3 ; i + + )
if ( plot . Axes [ i ] . IsAutoFitting ( ) ) {
plot . FitThisFrame = true ;
plot . Axes [ i ] . FitThisFrame = true ;
}
// Handle translation with right mouse button
if ( plot . Held & & ImGui : : IsMouseDown ( ImGuiMouseButton_Left ) ) {
ImVec2 delta ( IO . MouseDelta . x , IO . MouseDelta . y ) ;
if ( transform_axis [ 0 ] & & transform_axis [ 1 ] & & transform_axis [ 2 ] ) {
// Perform unconstrained translation (translate on the viewer plane)
// Compute delta_pixels in 3D (invert y-axis)
ImPlot3DPoint delta_pixels ( delta . x , - delta . y , 0.0f ) ;
// Convert delta to NDC space
float zoom = ImMin ( plot . PlotRect . GetWidth ( ) , plot . PlotRect . GetHeight ( ) ) / 1.8f ;
ImPlot3DPoint delta_NDC = plot . Rotation . Inverse ( ) * ( delta_pixels / zoom ) ;
// Convert delta to plot space
ImPlot3DPoint delta_plot = delta_NDC * ( plot . RangeMax ( ) - plot . RangeMin ( ) ) ;
// Adjust plot range to translate the plot
for ( int i = 0 ; i < 3 ; i + + ) {
if ( transform_axis [ i ] ) {
plot . Axes [ i ] . SetRange ( plot . Axes [ i ] . Range . Min - delta_plot [ i ] , plot . Axes [ i ] . Range . Max - delta_plot [ i ] ) ;
plot . Axes [ i ] . Held = true ;
}
// If no axis was held before (user started translating in this frame), set the held edge/plane indices
if ( ! any_axis_held ) {
plot . HeldEdgeIdx = hovered_edge_idx ;
plot . HeldPlaneIdx = hovered_plane_idx ;
}
}
} else if ( transform_axis [ 0 ] | | transform_axis [ 1 ] | | transform_axis [ 2 ] ) {
// Translate along plane/axis
// Mouse delta in pixels
ImVec2 mouse_pos = ImGui : : GetMousePos ( ) ;
ImVec2 mouse_delta ( IO . MouseDelta . x , IO . MouseDelta . y ) ;
// TODO Choose best plane given transform_axis and current view
// For now it crashes when transforming only one axis in the 2D view
ImPlane3D plane = ImPlane3D_XY ;
if ( transform_axis [ 1 ] & & transform_axis [ 2 ] )
plane = ImPlane3D_YZ ;
else if ( transform_axis [ 0 ] & & transform_axis [ 2 ] )
plane = ImPlane3D_XZ ;
else if ( transform_axis [ 2 ] )
plane = ImPlane3D_YZ ;
ImPlot3DPoint mouse_plot = PixelsToPlotPlane ( mouse_pos , plane , false ) ;
ImPlot3DPoint mouse_delta_plot = PixelsToPlotPlane ( mouse_pos + mouse_delta , plane , false ) ;
ImPlot3DPoint delta_plot = mouse_delta_plot - mouse_plot ;
// Apply translation to the selected axes
for ( int i = 0 ; i < 3 ; i + + ) {
if ( transform_axis [ i ] ) {
plot . Axes [ i ] . SetRange ( plot . Axes [ i ] . Range . Min - delta_plot [ i ] ,
plot . Axes [ i ] . Range . Max - delta_plot [ i ] ) ;
plot . Axes [ i ] . Held = true ;
}
if ( ! any_axis_held ) {
plot . HeldEdgeIdx = hovered_edge_idx ;
plot . HeldPlaneIdx = hovered_plane_idx ;
}
}
}
}
// Handle context click with right mouse button
if ( plot . Held & & ImGui : : IsMouseClicked ( ImGuiMouseButton_Right ) )
plot . ContextClick = true ;
if ( rotating | | ImGui : : IsMouseDoubleClicked ( ImGuiMouseButton_Right ) )
plot . ContextClick = false ;
// Handle reset rotation with left mouse double click
if ( plot . Held & & ImGui : : IsMouseDoubleClicked ( ImGuiMouseButton_Right ) ) {
plot . RotationAnimationEnd = plot . Rotation ;
// Calculate rotation to align the z-axis with the camera direction
if ( hovered_plane = = - 1 ) {
plot . RotationAnimationEnd = init_rotation ;
} else {
// Compute plane normal
ImPlot3DPoint axis_normal = ImPlot3DPoint ( 0.0f , 0.0f , 0.0f ) ;
axis_normal [ hovered_plane ] = active_faces [ hovered_plane ] ? - 1.0f : 1.0f ;
// Compute rotation to align the plane normal with the z-axis
ImPlot3DQuat align_normal = ImPlot3DQuat : : FromTwoVectors ( plot . RotationAnimationEnd * axis_normal , ImPlot3DPoint ( 0.0f , 0.0f , 1.0f ) ) ;
plot . RotationAnimationEnd = align_normal * plot . RotationAnimationEnd ;
if ( hovered_plane ! = 2 ) {
// Compute rotation to point z-axis up
ImPlot3DQuat align_up = ImPlot3DQuat : : FromTwoVectors ( plot . RotationAnimationEnd * ImPlot3DPoint ( 0.0f , 0.0f , 1.0f ) , ImPlot3DPoint ( 0.0f , 1.0f , 0.0f ) ) ;
plot . RotationAnimationEnd = align_up * plot . RotationAnimationEnd ;
} else {
// Find the axis most aligned with the up direction
ImPlot3DPoint up ( 0.0f , 1.0f , 0.0f ) ;
ImPlot3DPoint x_axis = plot . RotationAnimationEnd * ImPlot3DPoint ( 1.0f , 0.0f , 0.0f ) ;
ImPlot3DPoint y_axis = plot . RotationAnimationEnd * ImPlot3DPoint ( 0.0f , 1.0f , 0.0f ) ;
ImPlot3DPoint neg_x_axis = plot . RotationAnimationEnd * ImPlot3DPoint ( - 1.0f , 0.0f , 0.0f ) ;
ImPlot3DPoint neg_y_axis = plot . RotationAnimationEnd * ImPlot3DPoint ( 0.0f , - 1.0f , 0.0f ) ;
struct AxisAlignment {
ImPlot3DPoint axis ;
float dot ;
} ;
AxisAlignment candidates [ ] = {
{ x_axis , x_axis . Dot ( up ) } ,
{ y_axis , y_axis . Dot ( up ) } ,
{ neg_x_axis , neg_x_axis . Dot ( up ) } ,
{ neg_y_axis , neg_y_axis . Dot ( up ) } ,
} ;
// Find the candidate with the maximum dot product
AxisAlignment * best_candidate = & candidates [ 0 ] ;
for ( int i = 1 ; i < 4 ; + + i ) {
if ( candidates [ i ] . dot > best_candidate - > dot ) {
best_candidate = & candidates [ i ] ;
}
}
// Compute the rotation to align the best candidate with the up direction
ImPlot3DQuat align_up = ImPlot3DQuat : : FromTwoVectors ( best_candidate - > axis , up ) ;
plot . RotationAnimationEnd = align_up * plot . RotationAnimationEnd ;
}
}
// Compute the angular distance between current and target rotation
float dot_product = ImClamp ( plot . Rotation . Dot ( plot . RotationAnimationEnd ) , - 1.0f , 1.0f ) ;
float angle = 2.0f * acosf ( fabsf ( dot_product ) ) ;
// Calculate animation time for constant the angular velocity
plot . AnimationTime = angle / ANIMATION_ANGULAR_VELOCITY ;
}
// Handle rotation with left mouse dragging
if ( plot . Held & & ImGui : : IsMouseDown ( ImGuiMouseButton_Right ) ) {
ImVec2 delta ( IO . MouseDelta . x , IO . MouseDelta . y ) ;
// Map delta to rotation angles (in radians)
float angle_x = delta . x * ( 3.1415f / 180.0f ) ;
float angle_y = delta . y * ( 3.1415f / 180.0f ) ;
// Create quaternions for the rotations
ImPlot3DQuat quat_x ( angle_y , ImPlot3DPoint ( 1.0f , 0.0f , 0.0f ) ) ;
ImPlot3DQuat quat_z ( angle_x , ImPlot3DPoint ( 0.0f , 0.0f , 1.0f ) ) ;
// Combine the new rotations with the current rotation
plot . Rotation = quat_x * plot . Rotation * quat_z ;
plot . Rotation . Normalize ( ) ;
}
// Handle zoom with mouse wheel
if ( plot . Hovered & & ( ImGui : : IsMouseDown ( ImGuiMouseButton_Middle ) | | IO . MouseWheel ! = 0 ) ) {
float delta = ImGui : : IsMouseDown ( ImGuiMouseButton_Middle ) ? ( - 0.01f * IO . MouseDelta . y ) : ( - 0.1f * IO . MouseWheel ) ;
float zoom = 1.0f + delta ;
for ( int i = 0 ; i < 3 ; i + + ) {
ImPlot3DAxis & axis = plot . Axes [ i ] ;
float center = ( axis . Range . Min + axis . Range . Max ) * 0.5f ;
float size = axis . Range . Max - axis . Range . Min ;
size * = zoom ;
if ( transform_axis [ i ] ) {
plot . Axes [ i ] . SetRange ( center - size * 0.5f , center + size * 0.5f ) ;
plot . Axes [ i ] . Held = true ;
}
// If no axis was held before (user started zoom in this frame), set the held edge/plane indices
if ( ! any_axis_held ) {
plot . HeldEdgeIdx = hovered_edge_idx ;
plot . HeldPlaneIdx = hovered_plane_idx ;
}
}
}
// Handle context menu (should not happen if it is not a double click action)
bool not_double_click = ( float ) ( ImGui : : GetTime ( ) - IO . MouseClickedTime [ ImGuiMouseButton_Right ] ) > IO . MouseDoubleClickTime ;
if ( plot . Hovered & & plot . ContextClick & & not_double_click & & ! ImGui : : IsMouseDown ( ImGuiMouseButton_Right ) ) {
plot . ContextClick = false ;
plot . OpenContextThisFrame = true ;
}
// TODO Only open context menu if the mouse is not in the middle of double click action
const char * axis_contexts [ 3 ] = { " ##XAxisContext " , " ##YAxisContext " , " ##ZAxisContext " } ;
if ( plot . OpenContextThisFrame ) {
if ( plot . Items . Legend . Hovered )
ImGui : : OpenPopup ( " ##LegendContext " ) ;
else if ( hovered_axis ! = - 1 ) {
ImGui : : OpenPopup ( axis_contexts [ hovered_axis ] ) ;
} else if ( hovered_plane ! = - 1 ) {
ImGui : : OpenPopup ( axis_contexts [ hovered_plane ] ) ;
} else if ( plot . Hovered ) {
ImGui : : OpenPopup ( " ##PlotContext " ) ;
}
}
}
void SetupLock ( ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentPlot ! = nullptr , " SetupLock() needs to be called between BeginPlot() and EndPlot()! " ) ;
ImPlot3DPlot & plot = * gp . CurrentPlot ;
if ( plot . SetupLocked )
return ;
// Lock setup
plot . SetupLocked = true ;
ImGuiContext & g = * GImGui ;
ImGuiWindow * window = g . CurrentWindow ;
ImDrawList * draw_list = window - > DrawList ;
ImGui : : PushClipRect ( plot . FrameRect . Min , plot . FrameRect . Max , true ) ;
// Set default formatter/locator
for ( int i = 0 ; i < 3 ; i + + ) {
ImPlot3DAxis & axis = plot . Axes [ i ] ;
// Set formatter
if ( axis . Formatter = = nullptr ) {
axis . Formatter = Formatter_Default ;
if ( axis . FormatterData = = nullptr )
axis . FormatterData = ( void * ) IMPLOT3D_LABEL_FORMAT ;
}
// Set locator
if ( axis . Locator = = nullptr )
axis . Locator = Locator_Default ;
}
// Draw frame background
ImU32 f_bg_color = GetStyleColorU32 ( ImPlot3DCol_FrameBg ) ;
draw_list - > AddRectFilled ( plot . FrameRect . Min , plot . FrameRect . Max , f_bg_color ) ;
// Compute canvas/canvas rectangle
plot . CanvasRect = ImRect ( plot . FrameRect . Min + gp . Style . PlotPadding , plot . FrameRect . Max - gp . Style . PlotPadding ) ;
plot . PlotRect = plot . CanvasRect ;
// Compute ticks
for ( int i = 0 ; i < 3 ; i + + ) {
ImPlot3DAxis & axis = plot . Axes [ i ] ;
axis . Ticker . Reset ( ) ;
axis . Locator ( axis . Ticker , axis . Range , axis . Formatter , axis . FormatterData ) ;
}
// Render title
if ( plot . HasTitle ( ) ) {
ImU32 col = GetStyleColorU32 ( ImPlot3DCol_TitleText ) ;
ImVec2 top_center = ImVec2 ( plot . FrameRect . GetCenter ( ) . x , plot . CanvasRect . Min . y ) ;
AddTextCentered ( draw_list , top_center , col , plot . GetTitle ( ) ) ;
plot . PlotRect . Min . y + = ImGui : : GetTextLineHeight ( ) + gp . Style . LabelPadding . y ;
}
// Handle animation
if ( plot . AnimationTime > 0.0f ) {
float dt = ImGui : : GetIO ( ) . DeltaTime ;
float t = ImClamp ( dt / plot . AnimationTime , 0.0f , 1.0f ) ;
plot . AnimationTime - = dt ;
if ( plot . AnimationTime < 0.0f )
plot . AnimationTime = 0.0f ;
plot . Rotation = ImPlot3DQuat : : Slerp ( plot . Rotation , plot . RotationAnimationEnd , t ) ;
}
plot . Initialized = true ;
// Handle user input
HandleInput ( plot ) ;
// Render plot box
RenderPlotBox ( draw_list , plot ) ;
ImGui : : PopClipRect ( ) ;
}
//-----------------------------------------------------------------------------
// [SECTION] Miscellaneous
//-----------------------------------------------------------------------------
ImDrawList * GetPlotDrawList ( ) {
return ImGui : : GetWindowDrawList ( ) ;
}
//-----------------------------------------------------------------------------
// [SECTION] Styles
//-----------------------------------------------------------------------------
struct ImPlot3DStyleVarInfo {
ImGuiDataType Type ;
ImU32 Count ;
ImU32 Offset ;
void * GetVarPtr ( ImPlot3DStyle * style ) const { return ( void * ) ( ( unsigned char * ) style + Offset ) ; }
} ;
static const ImPlot3DStyleVarInfo GPlot3DStyleVarInfo [ ] =
{
// Item style
{ ImGuiDataType_Float , 1 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , LineWeight ) } , // ImPlot3DStyleVar_LineWeight
{ ImGuiDataType_S32 , 1 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , Marker ) } , // ImPlot3DStyleVar_Marker
{ ImGuiDataType_Float , 1 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , MarkerSize ) } , // ImPlot3DStyleVar_MarkerSize
{ ImGuiDataType_Float , 1 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , MarkerWeight ) } , // ImPlot3DStyleVar_MarkerWeight
{ ImGuiDataType_Float , 1 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , FillAlpha ) } , // ImPlot3DStyleVar_FillAlpha
// Plot style
{ ImGuiDataType_Float , 2 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , PlotDefaultSize ) } , // ImPlot3DStyleVar_Plot3DDefaultSize
{ ImGuiDataType_Float , 2 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , PlotMinSize ) } , // ImPlot3DStyleVar_Plot3DMinSize
{ ImGuiDataType_Float , 2 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , PlotPadding ) } , // ImPlot3DStyleVar_Plot3DPadding
// Label style
{ ImGuiDataType_Float , 2 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , LabelPadding ) } , // ImPlot3DStyleVar_LabelPaddine
{ ImGuiDataType_Float , 2 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , LegendPadding ) } , // ImPlot3DStyleVar_LegendPadding
{ ImGuiDataType_Float , 2 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , LegendInnerPadding ) } , // ImPlot3DStyleVar_LegendInnerPadding
{ ImGuiDataType_Float , 2 , ( ImU32 ) IM_OFFSETOF ( ImPlot3DStyle , LegendSpacing ) } , // ImPlot3DStyleVar_LegendSpacing
} ;
static const ImPlot3DStyleVarInfo * GetPlotStyleVarInfo ( ImPlot3DStyleVar idx ) {
IM_ASSERT ( idx > = 0 & & idx < ImPlot3DStyleVar_COUNT ) ;
IM_ASSERT ( IM_ARRAYSIZE ( GPlot3DStyleVarInfo ) = = ImPlot3DStyleVar_COUNT ) ;
return & GPlot3DStyleVarInfo [ idx ] ;
}
ImPlot3DStyle & GetStyle ( ) { return GImPlot3D - > Style ; }
void StyleColorsAuto ( ImPlot3DStyle * dst ) {
ImPlot3DStyle * style = dst ? dst : & ImPlot3D : : GetStyle ( ) ;
ImVec4 * colors = style - > Colors ;
colors [ ImPlot3DCol_Line ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_Fill ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_MarkerOutline ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_MarkerFill ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_TitleText ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_InlayText ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_FrameBg ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_PlotBg ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_PlotBorder ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_LegendBg ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_LegendBorder ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_LegendText ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_AxisText ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_AxisGrid ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_AxisTick ] = IMPLOT3D_AUTO_COL ;
}
void StyleColorsDark ( ImPlot3DStyle * dst ) {
ImPlot3DStyle * style = dst ? dst : & ImPlot3D : : GetStyle ( ) ;
ImVec4 * colors = style - > Colors ;
colors [ ImPlot3DCol_Line ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_Fill ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_MarkerOutline ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_MarkerFill ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_TitleText ] = ImVec4 ( 1.00f , 1.00f , 1.00f , 1.00f ) ;
colors [ ImPlot3DCol_InlayText ] = ImVec4 ( 1.00f , 1.00f , 1.00f , 1.00f ) ;
colors [ ImPlot3DCol_FrameBg ] = ImVec4 ( 1.00f , 1.00f , 1.00f , 0.07f ) ;
colors [ ImPlot3DCol_PlotBg ] = ImVec4 ( 0.00f , 0.00f , 0.00f , 0.50f ) ;
colors [ ImPlot3DCol_PlotBorder ] = ImVec4 ( 0.43f , 0.43f , 0.50f , 0.50f ) ;
colors [ ImPlot3DCol_LegendBg ] = ImVec4 ( 0.08f , 0.08f , 0.08f , 0.94f ) ;
colors [ ImPlot3DCol_LegendBorder ] = ImVec4 ( 0.43f , 0.43f , 0.50f , 0.50f ) ;
colors [ ImPlot3DCol_LegendText ] = ImVec4 ( 1.00f , 1.00f , 1.00f , 1.00f ) ;
colors [ ImPlot3DCol_AxisText ] = ImVec4 ( 1.00f , 1.00f , 1.00f , 1.00f ) ;
colors [ ImPlot3DCol_AxisGrid ] = ImVec4 ( 1.00f , 1.00f , 1.00f , 0.25f ) ;
colors [ ImPlot3DCol_AxisTick ] = IMPLOT3D_AUTO_COL ;
}
void StyleColorsLight ( ImPlot3DStyle * dst ) {
ImPlot3DStyle * style = dst ? dst : & ImPlot3D : : GetStyle ( ) ;
ImVec4 * colors = style - > Colors ;
colors [ ImPlot3DCol_Line ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_Fill ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_MarkerOutline ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_MarkerFill ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_TitleText ] = ImVec4 ( 0.00f , 0.00f , 0.00f , 1.00f ) ;
colors [ ImPlot3DCol_InlayText ] = ImVec4 ( 0.00f , 0.00f , 0.00f , 1.00f ) ;
colors [ ImPlot3DCol_FrameBg ] = ImVec4 ( 1.00f , 1.00f , 1.00f , 1.00f ) ;
colors [ ImPlot3DCol_PlotBg ] = ImVec4 ( 0.42f , 0.57f , 1.00f , 0.13f ) ;
colors [ ImPlot3DCol_PlotBorder ] = ImVec4 ( 0.00f , 0.00f , 0.00f , 0.00f ) ;
colors [ ImPlot3DCol_LegendBg ] = ImVec4 ( 1.00f , 1.00f , 1.00f , 0.98f ) ;
colors [ ImPlot3DCol_LegendBorder ] = ImVec4 ( 0.82f , 0.82f , 0.82f , 0.80f ) ;
colors [ ImPlot3DCol_LegendText ] = ImVec4 ( 0.00f , 0.00f , 0.00f , 1.00f ) ;
colors [ ImPlot3DCol_AxisText ] = ImVec4 ( 0.00f , 0.00f , 0.00f , 1.00f ) ;
colors [ ImPlot3DCol_AxisGrid ] = ImVec4 ( 1.00f , 1.00f , 1.00f , 1.00f ) ;
colors [ ImPlot3DCol_AxisTick ] = IMPLOT3D_AUTO_COL ;
}
void StyleColorsClassic ( ImPlot3DStyle * dst ) {
ImPlot3DStyle * style = dst ? dst : & ImPlot3D : : GetStyle ( ) ;
ImVec4 * colors = style - > Colors ;
colors [ ImPlot3DCol_Line ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_Fill ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_MarkerOutline ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_MarkerFill ] = IMPLOT3D_AUTO_COL ;
colors [ ImPlot3DCol_TitleText ] = ImVec4 ( 0.90f , 0.90f , 0.90f , 1.00f ) ;
colors [ ImPlot3DCol_InlayText ] = ImVec4 ( 0.90f , 0.90f , 0.90f , 1.00f ) ;
colors [ ImPlot3DCol_FrameBg ] = ImVec4 ( 0.43f , 0.43f , 0.43f , 0.39f ) ;
colors [ ImPlot3DCol_PlotBg ] = ImVec4 ( 0.00f , 0.00f , 0.00f , 0.35f ) ;
colors [ ImPlot3DCol_PlotBorder ] = ImVec4 ( 0.50f , 0.50f , 0.50f , 0.50f ) ;
colors [ ImPlot3DCol_LegendBg ] = ImVec4 ( 0.11f , 0.11f , 0.14f , 0.92f ) ;
colors [ ImPlot3DCol_LegendBorder ] = ImVec4 ( 0.50f , 0.50f , 0.50f , 0.50f ) ;
colors [ ImPlot3DCol_LegendText ] = ImVec4 ( 0.90f , 0.90f , 0.90f , 1.00f ) ;
colors [ ImPlot3DCol_AxisText ] = ImVec4 ( 0.90f , 0.90f , 0.90f , 1.00f ) ;
colors [ ImPlot3DCol_AxisGrid ] = ImVec4 ( 0.90f , 0.90f , 0.90f , 0.25f ) ;
colors [ ImPlot3DCol_AxisTick ] = IMPLOT3D_AUTO_COL ;
}
void PushStyleColor ( ImPlot3DCol idx , ImU32 col ) {
ImPlot3DContext & gp = * GImPlot3D ;
ImGuiColorMod backup ;
backup . Col = ( ImGuiCol ) idx ;
backup . BackupValue = gp . Style . Colors [ idx ] ;
gp . ColorModifiers . push_back ( backup ) ;
gp . Style . Colors [ idx ] = ImGui : : ColorConvertU32ToFloat4 ( col ) ;
}
void PushStyleColor ( ImPlot3DCol idx , const ImVec4 & col ) {
ImPlot3DContext & gp = * GImPlot3D ;
ImGuiColorMod backup ;
backup . Col = ( ImGuiCol ) idx ;
backup . BackupValue = gp . Style . Colors [ idx ] ;
gp . ColorModifiers . push_back ( backup ) ;
gp . Style . Colors [ idx ] = col ;
}
void PopStyleColor ( int count ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( count < = gp . ColorModifiers . Size , " You can't pop more modifiers than have been pushed! " ) ;
while ( count > 0 ) {
ImGuiColorMod & backup = gp . ColorModifiers . back ( ) ;
gp . Style . Colors [ backup . Col ] = backup . BackupValue ;
gp . ColorModifiers . pop_back ( ) ;
count - - ;
}
}
void PushStyleVar ( ImPlot3DStyleVar idx , float val ) {
ImPlot3DContext & gp = * GImPlot3D ;
const ImPlot3DStyleVarInfo * var_info = GetPlotStyleVarInfo ( idx ) ;
if ( var_info - > Type = = ImGuiDataType_Float & & var_info - > Count = = 1 ) {
float * pvar = ( float * ) var_info - > GetVarPtr ( & gp . Style ) ;
gp . StyleModifiers . push_back ( ImGuiStyleMod ( ( ImGuiStyleVar ) idx , * pvar ) ) ;
* pvar = val ;
return ;
}
IM_ASSERT ( 0 & & " Called PushStyleVar() float variant but variable is not a float! " ) ;
}
void PushStyleVar ( ImPlot3DStyleVar idx , int val ) {
ImPlot3DContext & gp = * GImPlot3D ;
const ImPlot3DStyleVarInfo * var_info = GetPlotStyleVarInfo ( idx ) ;
if ( var_info - > Type = = ImGuiDataType_S32 & & var_info - > Count = = 1 ) {
int * pvar = ( int * ) var_info - > GetVarPtr ( & gp . Style ) ;
gp . StyleModifiers . push_back ( ImGuiStyleMod ( ( ImGuiStyleVar ) idx , * pvar ) ) ;
* pvar = val ;
return ;
} else if ( var_info - > Type = = ImGuiDataType_Float & & var_info - > Count = = 1 ) {
float * pvar = ( float * ) var_info - > GetVarPtr ( & gp . Style ) ;
gp . StyleModifiers . push_back ( ImGuiStyleMod ( ( ImGuiStyleVar ) idx , * pvar ) ) ;
* pvar = ( float ) val ;
return ;
}
IM_ASSERT ( 0 & & " Called PushStyleVar() int variant but variable is not a int! " ) ;
}
void PushStyleVar ( ImPlot3DStyleVar idx , const ImVec2 & val ) {
ImPlot3DContext & gp = * GImPlot3D ;
const ImPlot3DStyleVarInfo * var_info = GetPlotStyleVarInfo ( idx ) ;
if ( var_info - > Type = = ImGuiDataType_Float & & var_info - > Count = = 2 ) {
ImVec2 * pvar = ( ImVec2 * ) var_info - > GetVarPtr ( & gp . Style ) ;
gp . StyleModifiers . push_back ( ImGuiStyleMod ( ( ImGuiStyleVar ) idx , * pvar ) ) ;
* pvar = val ;
return ;
}
IM_ASSERT ( 0 & & " Called PushStyleVar() ImVec2 variant but variable is not a ImVec2! " ) ;
}
void PopStyleVar ( int count ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( count < = gp . StyleModifiers . Size , " You can't pop more modifiers than have been pushed! " ) ;
while ( count > 0 ) {
ImGuiStyleMod & backup = gp . StyleModifiers . back ( ) ;
const ImPlot3DStyleVarInfo * info = GetPlotStyleVarInfo ( backup . VarIdx ) ;
void * data = info - > GetVarPtr ( & gp . Style ) ;
if ( info - > Type = = ImGuiDataType_Float & & info - > Count = = 1 ) {
( ( float * ) data ) [ 0 ] = backup . BackupFloat [ 0 ] ;
} else if ( info - > Type = = ImGuiDataType_Float & & info - > Count = = 2 ) {
( ( float * ) data ) [ 0 ] = backup . BackupFloat [ 0 ] ;
( ( float * ) data ) [ 1 ] = backup . BackupFloat [ 1 ] ;
} else if ( info - > Type = = ImGuiDataType_S32 & & info - > Count = = 1 ) {
( ( int * ) data ) [ 0 ] = backup . BackupInt [ 0 ] ;
}
gp . StyleModifiers . pop_back ( ) ;
count - - ;
}
}
ImVec4 GetStyleColorVec4 ( ImPlot3DCol idx ) {
return IsColorAuto ( idx ) ? GetAutoColor ( idx ) : GImPlot3D - > Style . Colors [ idx ] ;
}
ImU32 GetStyleColorU32 ( ImPlot3DCol idx ) {
return ImGui : : ColorConvertFloat4ToU32 ( ImPlot3D : : GetStyleColorVec4 ( idx ) ) ;
}
//------------------------------------------------------------------------------
// [SECTION] Colormaps
//------------------------------------------------------------------------------
ImPlot3DColormap AddColormap ( const char * name , const ImVec4 * colormap , int size , bool qual ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( size > 1 , " The colormap size must be greater than 1! " ) ;
IM_ASSERT_USER_ERROR ( gp . ColormapData . GetIndex ( name ) = = - 1 , " The colormap name has already been used! " ) ;
ImVector < ImU32 > buffer ;
buffer . resize ( size ) ;
for ( int i = 0 ; i < size ; + + i )
buffer [ i ] = ImGui : : ColorConvertFloat4ToU32 ( colormap [ i ] ) ;
return gp . ColormapData . Append ( name , buffer . Data , size , qual ) ;
}
ImPlot3DColormap AddColormap ( const char * name , const ImU32 * colormap , int size , bool qual ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( size > 1 , " The colormap size must be greater than 1! " ) ;
IM_ASSERT_USER_ERROR ( gp . ColormapData . GetIndex ( name ) = = - 1 , " The colormap name has already be used! " ) ;
return gp . ColormapData . Append ( name , colormap , size , qual ) ;
}
int GetColormapCount ( ) {
ImPlot3DContext & gp = * GImPlot3D ;
return gp . ColormapData . Count ;
}
const char * GetColormapName ( ImPlot3DColormap colormap ) {
ImPlot3DContext & gp = * GImPlot3D ;
return gp . ColormapData . GetName ( colormap ) ;
}
ImPlot3DColormap GetColormapIndex ( const char * name ) {
ImPlot3DContext & gp = * GImPlot3D ;
return gp . ColormapData . GetIndex ( name ) ;
}
void PushColormap ( ImPlot3DColormap colormap ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( colormap > = 0 & & colormap < gp . ColormapData . Count , " The colormap index is invalid! " ) ;
gp . ColormapModifiers . push_back ( gp . Style . Colormap ) ;
gp . Style . Colormap = colormap ;
}
void PushColormap ( const char * name ) {
ImPlot3DContext & gp = * GImPlot3D ;
ImPlot3DColormap idx = gp . ColormapData . GetIndex ( name ) ;
IM_ASSERT_USER_ERROR ( idx ! = - 1 , " The colormap name is invalid! " ) ;
PushColormap ( idx ) ;
}
void PopColormap ( int count ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( count < = gp . ColormapModifiers . Size , " You can't pop more modifiers than have been pushed! " ) ;
while ( count > 0 ) {
const ImPlot3DColormap & backup = gp . ColormapModifiers . back ( ) ;
gp . Style . Colormap = backup ;
gp . ColormapModifiers . pop_back ( ) ;
count - - ;
}
}
ImU32 NextColormapColorU32 ( ) {
ImPlot3DContext & gp = * GImPlot3D ;
IM_ASSERT_USER_ERROR ( gp . CurrentItems ! = nullptr , " NextColormapColor() needs to be called between BeginPlot() and EndPlot()! " ) ;
int idx = gp . CurrentItems - > ColormapIdx % gp . ColormapData . GetKeyCount ( gp . Style . Colormap ) ;
ImU32 col = gp . ColormapData . GetKeyColor ( gp . Style . Colormap , idx ) ;
gp . CurrentItems - > ColormapIdx + + ;
return col ;
}
ImVec4 NextColormapColor ( ) {
return ImGui : : ColorConvertU32ToFloat4 ( NextColormapColorU32 ( ) ) ;
}
int GetColormapSize ( ImPlot3DColormap cmap ) {
ImPlot3DContext & gp = * GImPlot3D ;
cmap = cmap = = IMPLOT3D_AUTO ? gp . Style . Colormap : cmap ;
IM_ASSERT_USER_ERROR ( cmap > = 0 & & cmap < gp . ColormapData . Count , " Invalid colormap index! " ) ;
return gp . ColormapData . GetKeyCount ( cmap ) ;
}
ImU32 GetColormapColorU32 ( int idx , ImPlot3DColormap cmap ) {
ImPlot3DContext & gp = * GImPlot3D ;
cmap = cmap = = IMPLOT3D_AUTO ? gp . Style . Colormap : cmap ;
IM_ASSERT_USER_ERROR ( cmap > = 0 & & cmap < gp . ColormapData . Count , " Invalid colormap index! " ) ;
idx = idx % gp . ColormapData . GetKeyCount ( cmap ) ;
return gp . ColormapData . GetKeyColor ( cmap , idx ) ;
}
ImVec4 GetColormapColor ( int idx , ImPlot3DColormap cmap ) {
return ImGui : : ColorConvertU32ToFloat4 ( GetColormapColorU32 ( idx , cmap ) ) ;
}
ImU32 SampleColormapU32 ( float t , ImPlot3DColormap cmap ) {
ImPlot3DContext & gp = * GImPlot3D ;
cmap = cmap = = IMPLOT3D_AUTO ? gp . Style . Colormap : cmap ;
IM_ASSERT_USER_ERROR ( cmap > = 0 & & cmap < gp . ColormapData . Count , " Invalid colormap index! " ) ;
return gp . ColormapData . LerpTable ( cmap , t ) ;
}
ImVec4 SampleColormap ( float t , ImPlot3DColormap cmap ) {
return ImGui : : ColorConvertU32ToFloat4 ( SampleColormapU32 ( t , cmap ) ) ;
}
//-----------------------------------------------------------------------------
// [SECTION] Context Utils
//-----------------------------------------------------------------------------
# define IMPLOT3D_APPEND_CMAP(name, qual) ctx->ColormapData.Append(#name, name, sizeof(name) / sizeof(ImU32), qual)
# define IM_RGB(r, g, b) IM_COL32(r, g, b, 255)
void InitializeContext ( ImPlot3DContext * ctx ) {
ResetContext ( ctx ) ;
const ImU32 Deep [ ] = { 4289753676 , 4283598045 , 4285048917 , 4283584196 , 4289950337 , 4284512403 , 4291005402 , 4287401100 , 4285839820 , 4291671396 } ;
const ImU32 Dark [ ] = { 4280031972 , 4290281015 , 4283084621 , 4288892568 , 4278222847 , 4281597951 , 4280833702 , 4290740727 , 4288256409 } ;
const ImU32 Pastel [ ] = { 4289639675 , 4293119411 , 4291161036 , 4293184478 , 4289124862 , 4291624959 , 4290631909 , 4293712637 , 4294111986 } ;
const ImU32 Paired [ ] = { 4293119554 , 4290017311 , 4287291314 , 4281114675 , 4288256763 , 4280031971 , 4285513725 , 4278222847 , 4292260554 , 4288298346 , 4288282623 , 4280834481 } ;
const ImU32 Viridis [ ] = { 4283695428 , 4285867080 , 4287054913 , 4287455029 , 4287526954 , 4287402273 , 4286883874 , 4285579076 , 4283552122 , 4280737725 , 4280674301 } ;
const ImU32 Plasma [ ] = { 4287039501 , 4288480321 , 4289200234 , 4288941455 , 4287638193 , 4286072780 , 4284638433 , 4283139314 , 4281771772 , 4280667900 , 4280416752 } ;
const ImU32 Hot [ ] = { 4278190144 , 4278190208 , 4278190271 , 4278190335 , 4278206719 , 4278223103 , 4278239231 , 4278255615 , 4283826175 , 4289396735 , 4294967295 } ;
const ImU32 Cool [ ] = { 4294967040 , 4294960666 , 4294954035 , 4294947661 , 4294941030 , 4294934656 , 4294928025 , 4294921651 , 4294915020 , 4294908646 , 4294902015 } ;
const ImU32 Pink [ ] = { 4278190154 , 4282532475 , 4284308894 , 4285690554 , 4286879686 , 4287870160 , 4288794330 , 4289651940 , 4291685869 , 4293392118 , 4294967295 } ;
const ImU32 Jet [ ] = { 4289331200 , 4294901760 , 4294923520 , 4294945280 , 4294967040 , 4289396565 , 4283826090 , 4278255615 , 4278233855 , 4278212095 , 4278190335 } ;
const ImU32 Twilight [ ] = { IM_RGB ( 226 , 217 , 226 ) , IM_RGB ( 166 , 191 , 202 ) , IM_RGB ( 109 , 144 , 192 ) , IM_RGB ( 95 , 88 , 176 ) , IM_RGB ( 83 , 30 , 124 ) , IM_RGB ( 47 , 20 , 54 ) , IM_RGB ( 100 , 25 , 75 ) , IM_RGB ( 159 , 60 , 80 ) , IM_RGB ( 192 , 117 , 94 ) , IM_RGB ( 208 , 179 , 158 ) , IM_RGB ( 226 , 217 , 226 ) } ;
const ImU32 RdBu [ ] = { IM_RGB ( 103 , 0 , 31 ) , IM_RGB ( 178 , 24 , 43 ) , IM_RGB ( 214 , 96 , 77 ) , IM_RGB ( 244 , 165 , 130 ) , IM_RGB ( 253 , 219 , 199 ) , IM_RGB ( 247 , 247 , 247 ) , IM_RGB ( 209 , 229 , 240 ) , IM_RGB ( 146 , 197 , 222 ) , IM_RGB ( 67 , 147 , 195 ) , IM_RGB ( 33 , 102 , 172 ) , IM_RGB ( 5 , 48 , 97 ) } ;
const ImU32 BrBG [ ] = { IM_RGB ( 84 , 48 , 5 ) , IM_RGB ( 140 , 81 , 10 ) , IM_RGB ( 191 , 129 , 45 ) , IM_RGB ( 223 , 194 , 125 ) , IM_RGB ( 246 , 232 , 195 ) , IM_RGB ( 245 , 245 , 245 ) , IM_RGB ( 199 , 234 , 229 ) , IM_RGB ( 128 , 205 , 193 ) , IM_RGB ( 53 , 151 , 143 ) , IM_RGB ( 1 , 102 , 94 ) , IM_RGB ( 0 , 60 , 48 ) } ;
const ImU32 PiYG [ ] = { IM_RGB ( 142 , 1 , 82 ) , IM_RGB ( 197 , 27 , 125 ) , IM_RGB ( 222 , 119 , 174 ) , IM_RGB ( 241 , 182 , 218 ) , IM_RGB ( 253 , 224 , 239 ) , IM_RGB ( 247 , 247 , 247 ) , IM_RGB ( 230 , 245 , 208 ) , IM_RGB ( 184 , 225 , 134 ) , IM_RGB ( 127 , 188 , 65 ) , IM_RGB ( 77 , 146 , 33 ) , IM_RGB ( 39 , 100 , 25 ) } ;
const ImU32 Spectral [ ] = { IM_RGB ( 158 , 1 , 66 ) , IM_RGB ( 213 , 62 , 79 ) , IM_RGB ( 244 , 109 , 67 ) , IM_RGB ( 253 , 174 , 97 ) , IM_RGB ( 254 , 224 , 139 ) , IM_RGB ( 255 , 255 , 191 ) , IM_RGB ( 230 , 245 , 152 ) , IM_RGB ( 171 , 221 , 164 ) , IM_RGB ( 102 , 194 , 165 ) , IM_RGB ( 50 , 136 , 189 ) , IM_RGB ( 94 , 79 , 162 ) } ;
const ImU32 Greys [ ] = { IM_COL32_WHITE , IM_COL32_BLACK } ;
IMPLOT3D_APPEND_CMAP ( Deep , true ) ;
IMPLOT3D_APPEND_CMAP ( Dark , true ) ;
IMPLOT3D_APPEND_CMAP ( Pastel , true ) ;
IMPLOT3D_APPEND_CMAP ( Paired , true ) ;
IMPLOT3D_APPEND_CMAP ( Viridis , false ) ;
IMPLOT3D_APPEND_CMAP ( Plasma , false ) ;
IMPLOT3D_APPEND_CMAP ( Hot , false ) ;
IMPLOT3D_APPEND_CMAP ( Cool , false ) ;
IMPLOT3D_APPEND_CMAP ( Pink , false ) ;
IMPLOT3D_APPEND_CMAP ( Jet , false ) ;
IMPLOT3D_APPEND_CMAP ( Twilight , false ) ;
IMPLOT3D_APPEND_CMAP ( RdBu , false ) ;
IMPLOT3D_APPEND_CMAP ( BrBG , false ) ;
IMPLOT3D_APPEND_CMAP ( PiYG , false ) ;
IMPLOT3D_APPEND_CMAP ( Spectral , false ) ;
IMPLOT3D_APPEND_CMAP ( Greys , false ) ;
}
void ResetContext ( ImPlot3DContext * ctx ) {
ctx - > Plots . Clear ( ) ;
ctx - > CurrentPlot = nullptr ;
ctx - > CurrentItems = nullptr ;
ctx - > NextItemData . Reset ( ) ;
ctx - > Style = ImPlot3DStyle ( ) ;
}
//-----------------------------------------------------------------------------
// [SECTION] Style Utils
//-----------------------------------------------------------------------------
bool IsColorAuto ( const ImVec4 & col ) {
return col . w = = - 1.0f ;
}
bool IsColorAuto ( ImPlot3DCol idx ) {
return IsColorAuto ( GImPlot3D - > Style . Colors [ idx ] ) ;
}
ImVec4 GetAutoColor ( ImPlot3DCol idx ) {
switch ( idx ) {
case ImPlot3DCol_Line : return IMPLOT3D_AUTO_COL ; // Plot dependent
case ImPlot3DCol_Fill : return IMPLOT3D_AUTO_COL ; // Plot dependent
case ImPlot3DCol_MarkerOutline : return IMPLOT3D_AUTO_COL ; // Plot dependent
case ImPlot3DCol_MarkerFill : return IMPLOT3D_AUTO_COL ; // Plot dependent
case ImPlot3DCol_TitleText : return ImGui : : GetStyleColorVec4 ( ImGuiCol_Text ) ;
case ImPlot3DCol_InlayText : return ImGui : : GetStyleColorVec4 ( ImGuiCol_Text ) ;
case ImPlot3DCol_FrameBg : return ImGui : : GetStyleColorVec4 ( ImGuiCol_FrameBg ) ;
case ImPlot3DCol_PlotBg : return ImGui : : GetStyleColorVec4 ( ImGuiCol_WindowBg ) ;
case ImPlot3DCol_PlotBorder : return ImGui : : GetStyleColorVec4 ( ImGuiCol_Border ) ;
case ImPlot3DCol_LegendBg : return ImGui : : GetStyleColorVec4 ( ImGuiCol_PopupBg ) ;
case ImPlot3DCol_LegendBorder : return ImGui : : GetStyleColorVec4 ( ImGuiCol_Border ) ;
case ImPlot3DCol_LegendText : return ImGui : : GetStyleColorVec4 ( ImGuiCol_Text ) ;
case ImPlot3DCol_AxisText : return ImGui : : GetStyleColorVec4 ( ImGuiCol_Text ) ;
case ImPlot3DCol_AxisGrid : return ImGui : : GetStyleColorVec4 ( ImGuiCol_Text ) * ImVec4 ( 1 , 1 , 1 , 0.25f ) ;
case ImPlot3DCol_AxisTick : return GetStyleColorVec4 ( ImPlot3DCol_AxisGrid ) ;
default : return IMPLOT3D_AUTO_COL ;
}
}
const char * GetStyleColorName ( ImPlot3DCol idx ) {
static const char * color_names [ ImPlot3DCol_COUNT ] = {
" Line " ,
" Fill " ,
" MarkerOutline " ,
" MarkerFill " ,
" TitleText " ,
" InlayText " ,
" FrameBg " ,
" PlotBg " ,
" PlotBorder " ,
" LegendBg " ,
" LegendBorder " ,
" LegendText " ,
" AxisText " ,
" AxisGrid " ,
" AxisTick " ,
} ;
return color_names [ idx ] ;
}
const ImPlot3DNextItemData & GetItemData ( ) { return GImPlot3D - > NextItemData ; }
} // namespace ImPlot3D
//-----------------------------------------------------------------------------
// [SECTION] ImPlot3DPoint
//-----------------------------------------------------------------------------
ImPlot3DPoint ImPlot3DPoint : : operator * ( float rhs ) const { return ImPlot3DPoint ( x * rhs , y * rhs , z * rhs ) ; }
ImPlot3DPoint ImPlot3DPoint : : operator / ( float rhs ) const { return ImPlot3DPoint ( x / rhs , y / rhs , z / rhs ) ; }
ImPlot3DPoint ImPlot3DPoint : : operator + ( const ImPlot3DPoint & rhs ) const { return ImPlot3DPoint ( x + rhs . x , y + rhs . y , z + rhs . z ) ; }
ImPlot3DPoint ImPlot3DPoint : : operator - ( const ImPlot3DPoint & rhs ) const { return ImPlot3DPoint ( x - rhs . x , y - rhs . y , z - rhs . z ) ; }
ImPlot3DPoint ImPlot3DPoint : : operator * ( const ImPlot3DPoint & rhs ) const { return ImPlot3DPoint ( x * rhs . x , y * rhs . y , z * rhs . z ) ; }
ImPlot3DPoint ImPlot3DPoint : : operator / ( const ImPlot3DPoint & rhs ) const { return ImPlot3DPoint ( x / rhs . x , y / rhs . y , z / rhs . z ) ; }
ImPlot3DPoint ImPlot3DPoint : : operator - ( ) const { return ImPlot3DPoint ( - x , - y , - z ) ; }
ImPlot3DPoint & ImPlot3DPoint : : operator * = ( float rhs ) {
x * = rhs ;
y * = rhs ;
z * = rhs ;
return * this ;
}
ImPlot3DPoint & ImPlot3DPoint : : operator / = ( float rhs ) {
x / = rhs ;
y / = rhs ;
z / = rhs ;
return * this ;
}
ImPlot3DPoint & ImPlot3DPoint : : operator + = ( const ImPlot3DPoint & rhs ) {
x + = rhs . x ;
y + = rhs . y ;
z + = rhs . z ;
return * this ;
}
ImPlot3DPoint & ImPlot3DPoint : : operator - = ( const ImPlot3DPoint & rhs ) {
x - = rhs . x ;
y - = rhs . y ;
z - = rhs . z ;
return * this ;
}
ImPlot3DPoint & ImPlot3DPoint : : operator * = ( const ImPlot3DPoint & rhs ) {
x * = rhs . x ;
y * = rhs . y ;
z * = rhs . z ;
return * this ;
}
ImPlot3DPoint & ImPlot3DPoint : : operator / = ( const ImPlot3DPoint & rhs ) {
x / = rhs . x ;
y / = rhs . y ;
z / = rhs . z ;
return * this ;
}
bool ImPlot3DPoint : : operator = = ( const ImPlot3DPoint & rhs ) const { return x = = rhs . x & & y = = rhs . y & & z = = rhs . z ; }
bool ImPlot3DPoint : : operator ! = ( const ImPlot3DPoint & rhs ) const { return ! ( * this = = rhs ) ; }
float ImPlot3DPoint : : Dot ( const ImPlot3DPoint & rhs ) const { return x * rhs . x + y * rhs . y + z * rhs . z ; }
ImPlot3DPoint ImPlot3DPoint : : Cross ( const ImPlot3DPoint & rhs ) const {
return ImPlot3DPoint ( y * rhs . z - z * rhs . y , z * rhs . x - x * rhs . z , x * rhs . y - y * rhs . x ) ;
}
float ImPlot3DPoint : : Length ( ) const { return ImSqrt ( x * x + y * y + z * z ) ; }
float ImPlot3DPoint : : LengthSquared ( ) const { return x * x + y * y + z * z ; }
void ImPlot3DPoint : : Normalize ( ) {
float l = Length ( ) ;
x / = l ;
y / = l ;
z / = l ;
}
ImPlot3DPoint ImPlot3DPoint : : Normalized ( ) const {
float l = Length ( ) ;
return ImPlot3DPoint ( x / l , y / l , z / l ) ;
}
ImPlot3DPoint operator * ( float lhs , const ImPlot3DPoint & rhs ) {
return ImPlot3DPoint ( lhs * rhs . x , lhs * rhs . y , lhs * rhs . z ) ;
}
bool ImPlot3DPoint : : IsNaN ( ) const {
return ImPlot3D : : ImNan ( x ) | | ImPlot3D : : ImNan ( y ) | | ImPlot3D : : ImNan ( z ) ;
}
//-----------------------------------------------------------------------------
// [SECTION] ImPlot3DBox
//-----------------------------------------------------------------------------
void ImPlot3DBox : : Expand ( const ImPlot3DPoint & point ) {
Min . x = ImMin ( Min . x , point . x ) ;
Min . y = ImMin ( Min . y , point . y ) ;
Min . z = ImMin ( Min . z , point . z ) ;
Max . x = ImMax ( Max . x , point . x ) ;
Max . y = ImMax ( Max . y , point . y ) ;
Max . z = ImMax ( Max . z , point . z ) ;
}
bool ImPlot3DBox : : Contains ( const ImPlot3DPoint & point ) const {
return ( point . x > = Min . x & & point . x < = Max . x ) & &
( point . y > = Min . y & & point . y < = Max . y ) & &
( point . z > = Min . z & & point . z < = Max . z ) ;
}
bool ImPlot3DBox : : ClipLineSegment ( const ImPlot3DPoint & p0 , const ImPlot3DPoint & p1 , ImPlot3DPoint & p0_clipped , ImPlot3DPoint & p1_clipped ) const {
// Check if the line segment is completely inside the box
if ( Contains ( p0 ) & & Contains ( p1 ) ) {
p0_clipped = p0 ;
p1_clipped = p1 ;
return true ;
}
// Perform Liang-Barsky 3D clipping
double t0 = 0.0 ;
double t1 = 1.0 ;
ImPlot3DPoint d = p1 - p0 ;
// Define the clipping boundaries
const double xmin = Min . x , xmax = Max . x ;
const double ymin = Min . y , ymax = Max . y ;
const double zmin = Min . z , zmax = Max . z ;
// Lambda function to update t0 and t1
auto update = [ & ] ( double p , double q ) - > bool {
if ( p = = 0.0 ) {
if ( q < 0.0 )
return false ; // Line is parallel and outside the boundary
else
return true ; // Line is parallel and inside or coincident with boundary
}
double r = q / p ;
if ( p < 0.0 ) {
if ( r > t1 )
return false ; // Line is outside
if ( r > t0 )
t0 = r ; // Move up t0
} else {
if ( r < t0 )
return false ; // Line is outside
if ( r < t1 )
t1 = r ; // Move down t1
}
return true ;
} ;
// Clip against each boundary
if ( ! update ( - d . x , p0 . x - xmin ) )
return false ; // Left
if ( ! update ( d . x , xmax - p0 . x ) )
return false ; // Right
if ( ! update ( - d . y , p0 . y - ymin ) )
return false ; // Bottom
if ( ! update ( d . y , ymax - p0 . y ) )
return false ; // Top
if ( ! update ( - d . z , p0 . z - zmin ) )
return false ; // Near
if ( ! update ( d . z , zmax - p0 . z ) )
return false ; // Far
// Compute clipped points
p0_clipped = p0 + d * t0 ;
p1_clipped = p0 + d * t1 ;
return true ;
}
//-----------------------------------------------------------------------------
// [SECTION] ImPlot3DRange
//-----------------------------------------------------------------------------
void ImPlot3DRange : : Expand ( float value ) {
Min = ImMin ( Min , value ) ;
Max = ImMax ( Max , value ) ;
}
bool ImPlot3DRange : : Contains ( float value ) const {
return value > = Min & & value < = Max ;
}
//-----------------------------------------------------------------------------
// [SECTION] ImPlot3DQuat
//-----------------------------------------------------------------------------
ImPlot3DQuat : : ImPlot3DQuat ( float _angle , const ImPlot3DPoint & _axis ) {
float half_angle = _angle * 0.5f ;
float s = std : : sin ( half_angle ) ;
x = s * _axis . x ;
y = s * _axis . y ;
z = s * _axis . z ;
w = std : : cos ( half_angle ) ;
}
ImPlot3DQuat ImPlot3DQuat : : FromTwoVectors ( const ImPlot3DPoint & v0 , const ImPlot3DPoint & v1 ) {
ImPlot3DQuat q ;
// Compute the dot product and lengths of the vectors
float dot = v0 . Dot ( v1 ) ;
float length_v0 = v0 . Length ( ) ;
float length_v1 = v1 . Length ( ) ;
// Normalize the dot product
float normalized_dot = dot / ( length_v0 * length_v1 ) ;
// Handle edge cases: if vectors are very close or identical
const float epsilon = 1e-6 f ;
if ( std : : fabs ( normalized_dot - 1.0f ) < epsilon ) {
// v0 and v1 are nearly identical; return an identity quaternion
q . x = 0.0f ;
q . y = 0.0f ;
q . z = 0.0f ;
q . w = 1.0f ;
return q ;
}
// Handle edge case: if vectors are opposite
if ( std : : fabs ( normalized_dot + 1.0f ) < epsilon ) {
// v0 and v1 are opposite; choose an arbitrary orthogonal axis
ImPlot3DPoint arbitrary_axis = std : : fabs ( v0 . x ) > std : : fabs ( v0 . z ) ? ImPlot3DPoint ( - v0 . y , v0 . x , 0.0f )
: ImPlot3DPoint ( 0.0f , - v0 . z , v0 . y ) ;
arbitrary_axis . Normalize ( ) ;
q . x = arbitrary_axis . x ;
q . y = arbitrary_axis . y ;
q . z = arbitrary_axis . z ;
q . w = 0.0f ;
return q ;
}
// General case
ImPlot3DPoint axis = v0 . Cross ( v1 ) ;
axis . Normalize ( ) ;
float angle = std : : acos ( normalized_dot ) ;
float half_angle = angle * 0.5f ;
float s = std : : sin ( half_angle ) ;
q . x = s * axis . x ;
q . y = s * axis . y ;
q . z = s * axis . z ;
q . w = std : : cos ( half_angle ) ;
return q ;
}
float ImPlot3DQuat : : Length ( ) const {
return std : : sqrt ( x * x + y * y + z * z + w * w ) ;
}
ImPlot3DQuat ImPlot3DQuat : : Normalized ( ) const {
float l = Length ( ) ;
return ImPlot3DQuat ( x / l , y / l , z / l , w / l ) ;
}
ImPlot3DQuat ImPlot3DQuat : : Conjugate ( ) const {
return ImPlot3DQuat ( - x , - y , - z , w ) ;
}
ImPlot3DQuat ImPlot3DQuat : : Inverse ( ) const {
float l_squared = x * x + y * y + z * z + w * w ;
return ImPlot3DQuat ( - x / l_squared , - y / l_squared , - z / l_squared , w / l_squared ) ;
}
ImPlot3DQuat ImPlot3DQuat : : operator * ( const ImPlot3DQuat & rhs ) const {
return ImPlot3DQuat (
w * rhs . x + x * rhs . w + y * rhs . z - z * rhs . y ,
w * rhs . y - x * rhs . z + y * rhs . w + z * rhs . x ,
w * rhs . z + x * rhs . y - y * rhs . x + z * rhs . w ,
w * rhs . w - x * rhs . x - y * rhs . y - z * rhs . z ) ;
}
ImPlot3DQuat & ImPlot3DQuat : : Normalize ( ) {
float l = Length ( ) ;
x / = l ;
y / = l ;
z / = l ;
w / = l ;
return * this ;
}
ImPlot3DPoint ImPlot3DQuat : : operator * ( const ImPlot3DPoint & point ) const {
// Extract vector part of the quaternion
ImPlot3DPoint qv ( x , y , z ) ;
// Compute the cross products needed for rotation
ImPlot3DPoint uv = qv . Cross ( point ) ; // uv = qv x point
ImPlot3DPoint uuv = qv . Cross ( uv ) ; // uuv = qv x uv
// Compute the rotated vector
return point + ( uv * w * 2.0f ) + ( uuv * 2.0f ) ;
}
bool ImPlot3DQuat : : operator = = ( const ImPlot3DQuat & rhs ) const {
return x = = rhs . x & & y = = rhs . y & & z = = rhs . z & & w = = rhs . w ;
}
bool ImPlot3DQuat : : operator ! = ( const ImPlot3DQuat & rhs ) const {
return ! ( * this = = rhs ) ;
}
ImPlot3DQuat ImPlot3DQuat : : Slerp ( const ImPlot3DQuat & q1 , const ImPlot3DQuat & q2 , float t ) {
// Clamp t to [0, 1]
t = ImClamp ( t , 0.0f , 1.0f ) ;
// Compute the dot product (cosine of the angle between quaternions)
float dot = q1 . x * q2 . x + q1 . y * q2 . y + q1 . z * q2 . z + q1 . w * q2 . w ;
// If the dot product is negative, negate one quaternion to take the shorter path
ImPlot3DQuat q2_ = q2 ;
if ( dot < 0.0f ) {
q2_ = ImPlot3DQuat ( - q2 . x , - q2 . y , - q2 . z , - q2 . w ) ;
dot = - dot ;
}
// If the quaternions are very close, use linear interpolation to avoid numerical instability
if ( dot > 0.9995f ) {
return ImPlot3DQuat (
q1 . x + t * ( q2_ . x - q1 . x ) ,
q1 . y + t * ( q2_ . y - q1 . y ) ,
q1 . z + t * ( q2_ . z - q1 . z ) ,
q1 . w + t * ( q2_ . w - q1 . w ) )
. Normalized ( ) ;
}
// Compute the angle and the interpolation factors
float theta_0 = std : : acos ( dot ) ; // Angle between input quaternions
float theta = theta_0 * t ; // Interpolated angle
float sin_theta = std : : sin ( theta ) ; // Sine of interpolated angle
float sin_theta_0 = std : : sin ( theta_0 ) ; // Sine of original angle
float s1 = std : : cos ( theta ) - dot * sin_theta / sin_theta_0 ;
float s2 = sin_theta / sin_theta_0 ;
// Interpolate and return the result
return ImPlot3DQuat (
s1 * q1 . x + s2 * q2_ . x ,
s1 * q1 . y + s2 * q2_ . y ,
s1 * q1 . z + s2 * q2_ . z ,
s1 * q1 . w + s2 * q2_ . w ) ;
}
float ImPlot3DQuat : : Dot ( const ImPlot3DQuat & rhs ) const {
return x * rhs . x + y * rhs . y + z * rhs . z + w * rhs . w ;
}
//-----------------------------------------------------------------------------
// [SECTION] ImDrawList3D
//-----------------------------------------------------------------------------
void ImDrawList3D : : PrimReserve ( int idx_count , int vtx_count ) {
IM_ASSERT_PARANOID ( idx_count > = 0 & & vtx_count > = 0 & & idx_count % 3 = = 0 ) ;
int vtx_buffer_old_size = VtxBuffer . Size ;
VtxBuffer . resize ( vtx_buffer_old_size + vtx_count ) ;
_VtxWritePtr = VtxBuffer . Data + vtx_buffer_old_size ;
int idx_buffer_old_size = IdxBuffer . Size ;
IdxBuffer . resize ( idx_buffer_old_size + idx_count ) ;
_IdxWritePtr = IdxBuffer . Data + idx_buffer_old_size ;
int z_buffer_old_size = ZBuffer . Size ;
ZBuffer . resize ( z_buffer_old_size + idx_count / 3 ) ;
_ZWritePtr = ZBuffer . Data + z_buffer_old_size ;
}
void ImDrawList3D : : PrimUnreserve ( int idx_count , int vtx_count ) {
IM_ASSERT_PARANOID ( idx_count > = 0 & & vtx_count > = 0 & & idx_count % 3 = = 0 ) ;
VtxBuffer . shrink ( VtxBuffer . Size - vtx_count ) ;
IdxBuffer . shrink ( IdxBuffer . Size - idx_count ) ;
ZBuffer . shrink ( ZBuffer . Size - idx_count / 3 ) ;
}
void ImDrawList3D : : SortedMoveToImGuiDrawList ( ) {
ImDrawList & draw_list = * ImGui : : GetWindowDrawList ( ) ;
const int tri_count = ZBuffer . Size ;
if ( tri_count = = 0 ) {
// No triangles, just clear and return
VtxBuffer . clear ( ) ;
IdxBuffer . clear ( ) ;
ZBuffer . clear ( ) ;
_VtxCurrentIdx = 0 ;
_VtxWritePtr = VtxBuffer . Data ;
_IdxWritePtr = IdxBuffer . Data ;
_ZWritePtr = ZBuffer . Data ;
return ;
}
// Build an array of (z, tri_idx)
struct TriRef {
float z ;
int tri_idx ;
} ;
TriRef * tris = ( TriRef * ) IM_ALLOC ( sizeof ( TriRef ) * tri_count ) ;
for ( int i = 0 ; i < tri_count ; i + + ) {
tris [ i ] . z = ZBuffer [ i ] ;
tris [ i ] . tri_idx = i ;
}
// Sort by z (distance from viewer)
ImQsort ( tris , ( size_t ) tri_count , sizeof ( TriRef ) ,
[ ] ( const void * a , const void * b ) {
float za = ( ( const TriRef * ) a ) - > z ;
float zb = ( ( const TriRef * ) b ) - > z ;
return ( za < zb ) ? - 1 : ( za > zb ) ? 1
: 0 ;
} ) ;
// Reserve space in the ImGui draw list
draw_list . PrimReserve ( IdxBuffer . Size , VtxBuffer . Size ) ;
// Copy vertices (no reordering needed)
memcpy ( draw_list . _VtxWritePtr , VtxBuffer . Data , VtxBuffer . Size * sizeof ( ImDrawVert ) ) ;
unsigned int idx_offset = draw_list . _VtxCurrentIdx ;
draw_list . _VtxWritePtr + = VtxBuffer . Size ;
draw_list . _VtxCurrentIdx + = ( unsigned int ) VtxBuffer . Size ;
// Maximum index allowed to not overflow ImDrawIdx
unsigned int max_index_allowed = MaxIdx ( ) - idx_offset ;
// Copy indices with triangle sorting based on distance from viewer
ImDrawIdx * idx_out = draw_list . _IdxWritePtr ;
ImDrawIdx * idx_in = IdxBuffer . Data ;
int triangles_added = 0 ;
for ( int i = 0 ; i < tri_count ; i + + ) {
int tri_i = tris [ i ] . tri_idx ;
int base_idx = tri_i * 3 ;
unsigned int i0 = ( unsigned int ) idx_in [ base_idx + 0 ] ;
unsigned int i1 = ( unsigned int ) idx_in [ base_idx + 1 ] ;
unsigned int i2 = ( unsigned int ) idx_in [ base_idx + 2 ] ;
// Check if after adding offset any of these indices exceed max_index_allowed
if ( i0 > max_index_allowed | | i1 > max_index_allowed | | i2 > max_index_allowed )
break ;
idx_out [ 0 ] = ( ImDrawIdx ) ( i0 + idx_offset ) ;
idx_out [ 1 ] = ( ImDrawIdx ) ( i1 + idx_offset ) ;
idx_out [ 2 ] = ( ImDrawIdx ) ( i2 + idx_offset ) ;
idx_out + = 3 ;
triangles_added + + ;
}
draw_list . _IdxWritePtr = idx_out ;
// Clear local buffers since we've moved them
VtxBuffer . clear ( ) ;
IdxBuffer . clear ( ) ;
ZBuffer . clear ( ) ;
_VtxCurrentIdx = 0 ;
_VtxWritePtr = VtxBuffer . Data ;
_IdxWritePtr = IdxBuffer . Data ;
_ZWritePtr = ZBuffer . Data ;
IM_FREE ( tris ) ;
}
//-----------------------------------------------------------------------------
// [SECTION] ImPlot3DAxis
//-----------------------------------------------------------------------------
bool ImPlot3DAxis : : HasLabel ( ) const { return ! Label . empty ( ) & & ! ImPlot3D : : ImHasFlag ( Flags , ImPlot3DAxisFlags_NoLabel ) ; }
bool ImPlot3DAxis : : HasGridLines ( ) const { return ! ImPlot3D : : ImHasFlag ( Flags , ImPlot3DAxisFlags_NoGridLines ) ; }
bool ImPlot3DAxis : : HasTickLabels ( ) const { return ! ImPlot3D : : ImHasFlag ( Flags , ImPlot3DAxisFlags_NoTickLabels ) ; }
bool ImPlot3DAxis : : HasTickMarks ( ) const { return ! ImPlot3D : : ImHasFlag ( Flags , ImPlot3DAxisFlags_NoTickMarks ) ; }
bool ImPlot3DAxis : : IsAutoFitting ( ) const { return ImPlot3D : : ImHasFlag ( Flags , ImPlot3DAxisFlags_AutoFit ) ; }
void ImPlot3DAxis : : ExtendFit ( float value ) {
FitExtents . Min = ImMin ( FitExtents . Min , value ) ;
FitExtents . Max = ImMax ( FitExtents . Max , value ) ;
}
void ImPlot3DAxis : : ApplyFit ( ) {
if ( ! IsLockedMin ( ) & & ! ImPlot3D : : ImNanOrInf ( FitExtents . Min ) )
Range . Min = FitExtents . Min ;
if ( ! IsLockedMax ( ) & & ! ImPlot3D : : ImNanOrInf ( FitExtents . Max ) )
Range . Max = FitExtents . Max ;
if ( ImPlot3D : : ImAlmostEqual ( Range . Min , Range . Max ) ) {
Range . Max + = 0.5 ;
Range . Min - = 0.5 ;
}
FitExtents . Min = HUGE_VAL ;
FitExtents . Max = - HUGE_VAL ;
}
float ImPlot3DAxis : : PlotToNDC ( float value ) const {
return ( value - Range . Min ) / ( Range . Max - Range . Min ) - 0.5f ;
}
float ImPlot3DAxis : : NDCToPlot ( float value ) const {
return Range . Min + ( value + 0.5f ) * ( Range . Max - Range . Min ) ;
}
//-----------------------------------------------------------------------------
// [SECTION] ImPlot3DPlot
//-----------------------------------------------------------------------------
void ImPlot3DPlot : : ExtendFit ( const ImPlot3DPoint & point ) {
for ( int i = 0 ; i < 3 ; i + + ) {
if ( ! ImPlot3D : : ImNanOrInf ( point [ i ] ) & & Axes [ i ] . FitThisFrame )
Axes [ i ] . ExtendFit ( point [ i ] ) ;
}
}
ImPlot3DPoint ImPlot3DPlot : : RangeMin ( ) const {
return ImPlot3DPoint ( Axes [ 0 ] . Range . Min , Axes [ 1 ] . Range . Min , Axes [ 2 ] . Range . Min ) ;
}
ImPlot3DPoint ImPlot3DPlot : : RangeMax ( ) const {
return ImPlot3DPoint ( Axes [ 0 ] . Range . Max , Axes [ 1 ] . Range . Max , Axes [ 2 ] . Range . Max ) ;
}
ImPlot3DPoint ImPlot3DPlot : : RangeCenter ( ) const {
return ImPlot3DPoint (
( Axes [ 0 ] . Range . Min + Axes [ 0 ] . Range . Max ) * 0.5f ,
( Axes [ 1 ] . Range . Min + Axes [ 1 ] . Range . Max ) * 0.5f ,
( Axes [ 2 ] . Range . Min + Axes [ 2 ] . Range . Max ) * 0.5f ) ;
}
void ImPlot3DPlot : : SetRange ( const ImPlot3DPoint & min , const ImPlot3DPoint & max ) {
Axes [ 0 ] . SetRange ( min . x , max . x ) ;
Axes [ 1 ] . SetRange ( min . y , max . y ) ;
Axes [ 2 ] . SetRange ( min . z , max . z ) ;
}
//-----------------------------------------------------------------------------
// [SECTION] ImPlot3DStyle
//-----------------------------------------------------------------------------
ImPlot3DStyle : : ImPlot3DStyle ( ) {
// Item style
LineWeight = 1.0f ;
Marker = ImPlot3DMarker_None ;
MarkerSize = 4.0f ;
MarkerWeight = 1.0f ;
FillAlpha = 1.0f ;
// Plot style
PlotDefaultSize = ImVec2 ( 400 , 400 ) ;
PlotMinSize = ImVec2 ( 200 , 200 ) ;
PlotPadding = ImVec2 ( 10 , 10 ) ;
LabelPadding = ImVec2 ( 5 , 5 ) ;
// Legend style
LegendPadding = ImVec2 ( 10 , 10 ) ;
LegendInnerPadding = ImVec2 ( 5 , 5 ) ;
LegendSpacing = ImVec2 ( 5 , 0 ) ;
// Colors
ImPlot3D : : StyleColorsAuto ( this ) ;
Colormap = ImPlot3DColormap_Deep ;
} ;
# endif // #ifndef IMGUI_DISABLE