chore: auto-commit (12 archivos)
- playground/tables/CMakeLists.txt - playground/tables/data_table.cpp - playground/tables/data_table_logic.cpp - playground/tables/data_table_logic.h - playground/tables/self_test.cpp - playground/tables/tql.cpp - playground/tables/viz.cpp - playground/tables/viz.h - playground/tables/llm_anthropic.cpp - playground/tables/llm_anthropic.h - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+84
-14
@@ -16,6 +16,10 @@ using data_table::ColumnType;
|
||||
using data_table::ViewMode;
|
||||
using data_table::ViewConfig;
|
||||
using data_table::parse_number;
|
||||
using data_table::nearest_index_2d;
|
||||
using data_table::pie_angle;
|
||||
using data_table::pie_slice_at_angle;
|
||||
using data_table::heatmap_cell_at;
|
||||
|
||||
static int find_header(const StageOutput& out, const std::string& name) {
|
||||
if (name.empty()) return -1;
|
||||
@@ -152,7 +156,8 @@ std::vector<double> finite(const std::vector<double>& v) {
|
||||
}
|
||||
|
||||
bool render_bar_like(const StageOutput& out, ViewMode mode,
|
||||
const ViewConfig& cfg, ImVec2 size) {
|
||||
const ViewConfig& cfg, ImVec2 size,
|
||||
int* clicked_row_out = nullptr) {
|
||||
int cat_col = resolve_cat(out, cfg, first_category_col(out));
|
||||
auto nums = collect_numeric_filtered(out, cfg, 8);
|
||||
if (cat_col < 0 || nums.empty()) {
|
||||
@@ -225,6 +230,15 @@ bool render_bar_like(const StageOutput& out, ViewMode mode,
|
||||
ImPlot::PlotBars(nums[0].name.c_str(), ticks.data(), ys.data(), n, 0.67, spc);
|
||||
}
|
||||
}
|
||||
// Hit-test fase 10: idx = round(plot.{x|y}) en single-series mode.
|
||||
if (clicked_row_out &&
|
||||
mode != ViewMode::GroupedBar && mode != ViewMode::StackedBar &&
|
||||
ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
ImPlotPoint p = ImPlot::GetPlotMousePos();
|
||||
double target = horiz ? p.y : p.x;
|
||||
int idx = (int)(target + 0.5);
|
||||
if (idx >= 0 && idx < n) *clicked_row_out = idx;
|
||||
}
|
||||
ImPlot::EndPlot();
|
||||
return true;
|
||||
}
|
||||
@@ -302,7 +316,8 @@ bool render_line_like(const StageOutput& out, ViewMode mode,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool render_scatter(const StageOutput& out, const ViewConfig& cfg, ImVec2 size) {
|
||||
bool render_scatter(const StageOutput& out, const ViewConfig& cfg, ImVec2 size,
|
||||
int* clicked_row_out = nullptr) {
|
||||
// Soporte cfg.x_col + cfg.y_cols[0]
|
||||
int xc = find_header(out, cfg.x_col);
|
||||
int yc = !cfg.y_cols.empty() ? find_header(out, cfg.y_cols[0]) : -1;
|
||||
@@ -329,11 +344,20 @@ bool render_scatter(const StageOutput& out, const ViewConfig& cfg, ImVec2 size)
|
||||
ImPlot::PlotScatter("##s", nums[0].vals.data(), nums[1].vals.data(),
|
||||
(int)nums[0].vals.size());
|
||||
}
|
||||
if (clicked_row_out &&
|
||||
ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
ImPlotPoint p = ImPlot::GetPlotMousePos();
|
||||
int idx = nearest_index_2d(p.x, p.y,
|
||||
nums[0].vals.data(), nums[1].vals.data(),
|
||||
(int)nums[0].vals.size());
|
||||
if (idx >= 0) *clicked_row_out = idx;
|
||||
}
|
||||
ImPlot::EndPlot();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool render_bubble(const StageOutput& out, const ViewConfig& cfg, ImVec2 size) {
|
||||
bool render_bubble(const StageOutput& out, const ViewConfig& cfg, ImVec2 size,
|
||||
int* clicked_row_out = nullptr) {
|
||||
int xc = find_header(out, cfg.x_col);
|
||||
int yc = !cfg.y_cols.empty() ? find_header(out, cfg.y_cols[0]) : -1;
|
||||
int sc = resolve_size(out, cfg, -1);
|
||||
@@ -354,6 +378,14 @@ bool render_bubble(const StageOutput& out, const ViewConfig& cfg, ImVec2 size) {
|
||||
axflag(cfg), axflag(cfg));
|
||||
ImPlot::PlotBubbles("##b", nums[0].vals.data(), nums[1].vals.data(),
|
||||
nums[2].vals.data(), (int)nums[0].vals.size());
|
||||
if (clicked_row_out &&
|
||||
ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
ImPlotPoint p = ImPlot::GetPlotMousePos();
|
||||
int idx = nearest_index_2d(p.x, p.y,
|
||||
nums[0].vals.data(), nums[1].vals.data(),
|
||||
(int)nums[0].vals.size());
|
||||
if (idx >= 0) *clicked_row_out = idx;
|
||||
}
|
||||
ImPlot::EndPlot();
|
||||
return true;
|
||||
}
|
||||
@@ -404,7 +436,8 @@ bool render_hist2d(const StageOutput& out, const ViewConfig& cfg, ImVec2 size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool render_heatmap(const StageOutput& out, const ViewConfig& cfg, ImVec2 size) {
|
||||
bool render_heatmap(const StageOutput& out, const ViewConfig& cfg, ImVec2 size,
|
||||
int* clicked_row_out = nullptr) {
|
||||
auto nums = collect_numeric_filtered(out, cfg, 64);
|
||||
if (nums.empty()) { info_text("Need numeric columns"); return false; }
|
||||
int cols = (int)nums.size();
|
||||
@@ -424,11 +457,22 @@ bool render_heatmap(const StageOutput& out, const ViewConfig& cfg, ImVec2 size)
|
||||
maybe_fit(cfg);
|
||||
if (!ImPlot::BeginPlot("##heatmap", size, 0)) return false;
|
||||
ImPlot::PlotHeatmap("##hm", mat.data(), rows, cols, mn, mx, nullptr);
|
||||
if (clicked_row_out &&
|
||||
ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
ImPlotPoint p = ImPlot::GetPlotMousePos();
|
||||
// ImPlot heatmap Y se pinta de top a bottom; plot mouse_y va igual
|
||||
// (default scale 0..rows). Mapeo directo.
|
||||
int rr, cc;
|
||||
heatmap_cell_at(p.x, p.y, rows, cols, rr, cc);
|
||||
if (rr >= 0) *clicked_row_out = rr;
|
||||
(void)cc;
|
||||
}
|
||||
ImPlot::EndPlot();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool render_pie(const StageOutput& out, const ViewConfig& cfg, bool donut, ImVec2 size) {
|
||||
bool render_pie(const StageOutput& out, const ViewConfig& cfg, bool donut, ImVec2 size,
|
||||
int* clicked_row_out = nullptr) {
|
||||
int cat = resolve_cat(out, cfg, first_category_col(out));
|
||||
auto nums = collect_numeric_filtered(out, cfg, 1);
|
||||
if (cat < 0 || nums.empty()) { info_text("Need 1 category + 1 numeric"); return false; }
|
||||
@@ -455,11 +499,24 @@ bool render_pie(const StageOutput& out, const ViewConfig& cfg, bool donut, ImVec
|
||||
// Draw inner hole as solid circle by overlaying a smaller pie of one slice transparent.
|
||||
// Simpler: just visually it's a circle with text. Use no extra primitive for now.
|
||||
}
|
||||
if (clicked_row_out &&
|
||||
ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
ImPlotPoint p = ImPlot::GetPlotMousePos();
|
||||
double dx = p.x - 0.5, dy = p.y - 0.5;
|
||||
double dist2 = dx*dx + dy*dy;
|
||||
double inner = donut ? (radius * 0.5) : 0.0;
|
||||
if (dist2 <= radius * radius && dist2 >= inner * inner) {
|
||||
double ang = pie_angle(0.5, 0.5, p.x, p.y);
|
||||
int idx = pie_slice_at_angle(ang, values.data(), n);
|
||||
if (idx >= 0) *clicked_row_out = idx;
|
||||
}
|
||||
}
|
||||
ImPlot::EndPlot();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool render_funnel(const StageOutput& out, const ViewConfig& cfg, ImVec2 size) {
|
||||
bool render_funnel(const StageOutput& out, const ViewConfig& cfg, ImVec2 size,
|
||||
int* clicked_row_out = nullptr) {
|
||||
int cat = resolve_cat(out, cfg, first_category_col(out));
|
||||
auto nums = collect_numeric_filtered(out, cfg, 1);
|
||||
if (cat < 0 || nums.empty()) { info_text("Need 1 category + 1 numeric"); return false; }
|
||||
@@ -492,6 +549,17 @@ bool render_funnel(const StageOutput& out, const ViewConfig& cfg, ImVec2 size) {
|
||||
ImPlot::SetupAxisTicks(ImAxis_Y1, ticks.data(), n, labels.data(), false);
|
||||
ImPlot::PlotBars(nums[0].name.c_str(), ys.data(), ticks.data(), n, 0.85,
|
||||
ImPlotSpec(ImPlotProp_Flags, ImPlotBarsFlags_Horizontal));
|
||||
if (clicked_row_out &&
|
||||
ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
ImPlotPoint p = ImPlot::GetPlotMousePos();
|
||||
int tick_idx = (int)(p.y + 0.5);
|
||||
// ticks[i] = n-1-i. Invertir para idx en orden sorted descendiente.
|
||||
int sorted_pos = (n - 1) - tick_idx;
|
||||
if (sorted_pos >= 0 && sorted_pos < n) {
|
||||
// idx[sorted_pos] da indice de row original en out.
|
||||
*clicked_row_out = idx[sorted_pos];
|
||||
}
|
||||
}
|
||||
ImPlot::EndPlot();
|
||||
return true;
|
||||
}
|
||||
@@ -763,7 +831,9 @@ bool render_radar(const StageOutput& out, const ViewConfig& cfg, ImVec2 size) {
|
||||
} // anon
|
||||
|
||||
bool render(const StageOutput& out, ViewMode mode,
|
||||
const ViewConfig& cfg, ImVec2 size) {
|
||||
const ViewConfig& cfg, ImVec2 size,
|
||||
int* clicked_row_out) {
|
||||
if (clicked_row_out) *clicked_row_out = -1;
|
||||
if (out.rows == 0 || out.cols == 0) {
|
||||
info_text("No data");
|
||||
return false;
|
||||
@@ -773,21 +843,21 @@ bool render(const StageOutput& out, ViewMode mode,
|
||||
case ViewMode::Bar:
|
||||
case ViewMode::Column:
|
||||
case ViewMode::GroupedBar:
|
||||
case ViewMode::StackedBar: return render_bar_like(out, mode, cfg, size);
|
||||
case ViewMode::StackedBar: return render_bar_like(out, mode, cfg, size, clicked_row_out);
|
||||
case ViewMode::Line:
|
||||
case ViewMode::Area:
|
||||
case ViewMode::Stairs: return render_line_like(out, mode, cfg, size);
|
||||
case ViewMode::Scatter: return render_scatter(out, cfg, size);
|
||||
case ViewMode::Bubble: return render_bubble(out, cfg, size);
|
||||
case ViewMode::Scatter: return render_scatter(out, cfg, size, clicked_row_out);
|
||||
case ViewMode::Bubble: return render_bubble(out, cfg, size, clicked_row_out);
|
||||
case ViewMode::Histogram: return render_histogram(out, cfg, size);
|
||||
case ViewMode::Histogram2D: return render_hist2d(out, cfg, size);
|
||||
case ViewMode::Heatmap: return render_heatmap(out, cfg, size);
|
||||
case ViewMode::Heatmap: return render_heatmap(out, cfg, size, clicked_row_out);
|
||||
case ViewMode::BoxPlot: return render_boxplot(out, cfg, size);
|
||||
case ViewMode::Stem: return render_stem(out, cfg, size);
|
||||
case ViewMode::ErrorBars: return render_errorbars(out, cfg, size);
|
||||
case ViewMode::Pie: return render_pie(out, cfg, false, size);
|
||||
case ViewMode::Donut: return render_pie(out, cfg, true, size);
|
||||
case ViewMode::Funnel: return render_funnel(out, cfg, size);
|
||||
case ViewMode::Pie: return render_pie(out, cfg, false, size, clicked_row_out);
|
||||
case ViewMode::Donut: return render_pie(out, cfg, true, size, clicked_row_out);
|
||||
case ViewMode::Funnel: return render_funnel(out, cfg, size, clicked_row_out);
|
||||
case ViewMode::Waterfall: return render_waterfall(out, cfg, size);
|
||||
case ViewMode::KPI: return render_kpi_single(out, cfg);
|
||||
case ViewMode::KPIGrid: return render_kpi_grid(out, cfg);
|
||||
|
||||
Reference in New Issue
Block a user