diff --git a/implot.cpp b/implot.cpp index 2b6f8a4..f258b51 100644 --- a/implot.cpp +++ b/implot.cpp @@ -2483,68 +2483,9 @@ ImPlotLimits GetPlotQuery(int y_axis_in) { return result; } -bool GrabLineH(const char* id, double* value, const ImVec4& col, float thickness) { +bool DragLineX(const char* id, double* value, bool show_label, const ImVec4& col, float thickness) { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GrabLineH() needs to be called between BeginPlot() and EndPlot()!"); - const float grab_size = ImMax(5.0f, thickness); - float xl = gp.BB_Plot.Min.x; - float xr = gp.BB_Plot.Max.x; - float y = IM_ROUND(PlotToPixels(0, *value).y); - const bool outside = y < (gp.BB_Plot.Min.y - grab_size / 2) || y > (gp.BB_Plot.Max.y + grab_size / 2); - if (outside) - return false; - float len = gp.Style.MajorTickLen.y; - ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; - ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); - ImDrawList& DrawList = *GetPlotDrawList(); - - PushPlotClipRect(); - DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness); - DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness); - DrawList.AddLine(ImVec2(xr,y), ImVec2(xr-len,y), col32, 3*thickness); - PopPlotClipRect(); - - if (gp.CurrentPlot->Selecting || gp.CurrentPlot->Querying) - return false; - - ImVec2 old_cursor_pos = ImGui::GetCursorScreenPos(); - ImVec2 new_cursor_pos = ImVec2(xl, y - grab_size / 2.0f); - ImGui::SetItemAllowOverlap(); - ImGui::GetCurrentWindow()->DC.CursorPos = new_cursor_pos; - ImGui::InvisibleButton(id, ImVec2(xr - xl, grab_size)); - ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; - int yax = GetCurrentYAxis(); - if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); - double range_y = gp.YTicks[yax].Size > 1 ? (gp.YTicks[yax].Ticks[1].PlotPos - gp.YTicks[yax].Ticks[0].PlotPos) : gp.CurrentPlot->YAxis[yax].Range.Size(); - char buf[32]; - snprintf(buf, 32, "%s = %.*f", id, Precision(range_y), *value); - ImVec2 size = ImGui::CalcTextSize(buf); - const int pad = 2; - PushPlotClipRect(); - if (yax == 0) { - DrawList.AddRectFilled(ImVec2(xl,y-pad-size.y/2), ImVec2(xl + size.x + 2 * pad, y+pad+size.y/2), col32); - DrawList.AddText(ImVec2(xl+pad,y-size.y/2),CalcTextColor(color),buf); - } - else { - DrawList.AddRectFilled(ImVec2(xr-size.x-2*pad,y-pad-size.y/2), ImVec2(xr, y+pad+size.y/2), col32); - DrawList.AddText(ImVec2(xr-size.x-pad,y-size.y/2),CalcTextColor(color),buf); - } - PopPlotClipRect(); - } - - bool dragging = false; - if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { - *value = ImPlot::GetPlotMousePos().y; - *value = ImClamp(*value, gp.Y[yax].Axis->Range.Min, gp.Y[yax].Axis->Range.Max); - dragging = true; - } - return dragging; -} - -bool GrabLineV(const char* id, double* value, const ImVec4& col, float thickness) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GrabLineV() needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragLineX() needs to be called between BeginPlot() and EndPlot()!"); const float grab_size = ImMax(5.0f, thickness); float yt = gp.BB_Plot.Min.y; float yb = gp.BB_Plot.Max.y; @@ -2556,34 +2497,36 @@ bool GrabLineV(const char* id, double* value, const ImVec4& col, float thickness ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); ImDrawList& DrawList = *GetPlotDrawList(); - PushPlotClipRect(); DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness); DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness); DrawList.AddLine(ImVec2(x,yb), ImVec2(x,yb-len), col32, 3*thickness); PopPlotClipRect(); - if (gp.CurrentPlot->Selecting || gp.CurrentPlot->Querying) return false; - ImVec2 old_cursor_pos = ImGui::GetCursorScreenPos(); ImVec2 new_cursor_pos = ImVec2(x - grab_size / 2.0f, yt); ImGui::GetCurrentWindow()->DC.CursorPos = new_cursor_pos; - ImGui::InvisibleButton(id, ImVec2(grab_size, yb-yt), ImGuiButtonFlags_DontClosePopups); + ImGui::InvisibleButton(id, ImVec2(grab_size, yb-yt)); ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; - if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { + if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { + gp.Hov_Plot = false; ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); - double range_x = gp.XTicks.Size > 1 ? (gp.XTicks.Ticks[1].PlotPos - gp.XTicks.Ticks[0].PlotPos) : gp.CurrentPlot->XAxis.Range.Size(); - char buf[32]; - snprintf(buf, 32, "%s = %.*f", id, Precision(range_x), *value); - ImVec2 size = ImGui::CalcTextSize(buf); - const int pad = 2; - PushPlotClipRect(); - DrawList.AddRectFilled(ImVec2(x - size.x/2 - pad, yb - size.y - 2*pad), ImVec2(x + pad + size.x/2, yb), col32); - DrawList.AddText(ImVec2(x - size.x/2, yb - size.y - pad), CalcTextColor(color), buf); - PopPlotClipRect(); + if (show_label) { + PushPlotClipRect(); + double range_x = gp.XTicks.Size > 1 ? (gp.XTicks.Ticks[1].PlotPos - gp.XTicks.Ticks[0].PlotPos) : gp.CurrentPlot->XAxis.Range.Size(); + char buf[32]; + snprintf(buf, 32, "%s = %.*f", id, Precision(range_x), *value); + ImVec2 size = ImGui::CalcTextSize(buf); + const int pad = 2; + ImVec2 label_pos = ImVec2(x - size.x/2 - pad, yb - size.y - 2*pad); + ImVec2 label_size = size + ImVec2(pad*2,pad*2); + label_pos = ClampLabelPos(label_pos, label_size, gp.BB_Plot.Min, gp.BB_Plot.Max); + DrawList.AddRectFilled(label_pos, label_pos + label_size, col32); + DrawList.AddText(label_pos + ImVec2(pad,pad),CalcTextColor(color),buf); + PopPlotClipRect(); + } } - bool dragging = false; if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { *value = ImPlot::GetPlotMousePos().x; @@ -2593,9 +2536,73 @@ bool GrabLineV(const char* id, double* value, const ImVec4& col, float thickness return dragging; } -bool GrabPoint(const char* id, double* x, double* y, const ImVec4& col, float radius) { +bool DragLineY(const char* id, double* value, bool show_label, const ImVec4& col, float thickness) { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GrabPoint() needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragLineY() needs to be called between BeginPlot() and EndPlot()!"); + const float grab_size = ImMax(5.0f, thickness); + float xl = gp.BB_Plot.Min.x; + float xr = gp.BB_Plot.Max.x; + float y = IM_ROUND(PlotToPixels(0, *value).y); + const bool outside = y < (gp.BB_Plot.Min.y - grab_size / 2) || y > (gp.BB_Plot.Max.y + grab_size / 2); + if (outside) + return false; + float len = gp.Style.MajorTickLen.y; + ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; + ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); + ImDrawList& DrawList = *GetPlotDrawList(); + PushPlotClipRect(); + DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness); + DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness); + DrawList.AddLine(ImVec2(xr,y), ImVec2(xr-len,y), col32, 3*thickness); + PopPlotClipRect(); + if (gp.CurrentPlot->Selecting || gp.CurrentPlot->Querying) + return false; + ImVec2 old_cursor_pos = ImGui::GetCursorScreenPos(); + ImVec2 new_cursor_pos = ImVec2(xl, y - grab_size / 2.0f); + ImGui::SetItemAllowOverlap(); + ImGui::GetCurrentWindow()->DC.CursorPos = new_cursor_pos; + ImGui::InvisibleButton(id, ImVec2(xr - xl, grab_size)); + ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; + int yax = GetCurrentYAxis(); + if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { + gp.Hov_Plot = false; + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); + if (show_label) { + PushPlotClipRect(); + double range_y = gp.YTicks[yax].Size > 1 ? (gp.YTicks[yax].Ticks[1].PlotPos - gp.YTicks[yax].Ticks[0].PlotPos) : gp.CurrentPlot->YAxis[yax].Range.Size(); + char buf[32]; + snprintf(buf, 32, "%s = %.*f", id, Precision(range_y), *value); + ImVec2 size = ImGui::CalcTextSize(buf); + const int pad = 2; + if (yax == 0) { + ImVec2 label_pos = ImVec2(xl,y-pad-size.y/2); + ImVec2 label_size = size + ImVec2(pad*2,pad*2); + label_pos = ClampLabelPos(label_pos, label_size, gp.BB_Plot.Min, gp.BB_Plot.Max); + DrawList.AddRectFilled(label_pos, label_pos + label_size, col32); + DrawList.AddText(label_pos + ImVec2(pad,pad),CalcTextColor(color),buf); + } + else { + ImVec2 label_pos = ImVec2(xr-size.x-2*pad,y-pad-size.y/2); + ImVec2 label_size = size + ImVec2(pad*2,pad*2); + label_pos = ClampLabelPos(label_pos, label_size, gp.BB_Plot.Min, gp.BB_Plot.Max); + DrawList.AddRectFilled(label_pos, label_pos + label_size, col32); + DrawList.AddText(label_pos + ImVec2(pad,pad),CalcTextColor(color),buf); + } + PopPlotClipRect(); + } + } + bool dragging = false; + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { + *value = ImPlot::GetPlotMousePos().y; + *value = ImClamp(*value, gp.Y[yax].Axis->Range.Min, gp.Y[yax].Axis->Range.Max); + dragging = true; + } + return dragging; +} + +bool DragPoint(const char* id, double* x, double* y, bool show_label, const ImVec4& col, float radius) { + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragPoint() needs to be called between BeginPlot() and EndPlot()!"); const float grab_size = ImMax(5.0f, 2*radius); const bool outside = !GetPlotLimits().Contains(*x,*y); if (outside) @@ -2603,13 +2610,10 @@ bool GrabPoint(const char* id, double* x, double* y, const ImVec4& col, float ra ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); ImDrawList& DrawList = *GetPlotDrawList(); - ImVec2 pos = PlotToPixels(*x,*y); - PushPlotClipRect(); DrawList.AddCircleFilled(pos, radius, col32); PopPlotClipRect(); - int yax = GetCurrentYAxis(); ImVec2 old_cursor_pos = ImGui::GetCursorScreenPos(); ImVec2 new_cursor_pos = ImVec2(pos - ImVec2(grab_size,grab_size)*0.5f); @@ -2617,18 +2621,23 @@ bool GrabPoint(const char* id, double* x, double* y, const ImVec4& col, float ra ImGui::InvisibleButton(id, ImVec2(grab_size, grab_size)); ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { + gp.Hov_Plot = false; ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); - // double range_x = gp.XTicks.Size > 1 ? (gp.XTicks.Ticks[1].PlotPos - gp.XTicks.Ticks[0].PlotPos) : gp.CurrentPlot->XAxis.Range.Size(); - // char buf[32]; - // snprintf(buf, 32, "%s = %.*f", id, Precision(range_x), *value); - // ImVec2 size = ImGui::CalcTextSize(buf); - // const int pad = 2; - // PushPlotClipRect(); - // DrawList.AddRectFilled(ImVec2(x - size.x/2 - pad, yb - size.y - 2*pad), ImVec2(x + pad + size.x/2, yb), col32); - // DrawList.AddText(ImVec2(x - size.x/2, yb - size.y - pad), CalcTextColor(color), buf); - // PopPlotClipRect(); + if (show_label) { + PushPlotClipRect(); + double range_x = gp.XTicks.Size > 1 ? (gp.XTicks.Ticks[1].PlotPos - gp.XTicks.Ticks[0].PlotPos) : gp.CurrentPlot->XAxis.Range.Size(); + double range_y = gp.YTicks[yax].Size > 1 ? (gp.YTicks[yax].Ticks[1].PlotPos - gp.YTicks[yax].Ticks[0].PlotPos) : gp.CurrentPlot->YAxis[yax].Range.Size(); + char buf[64]; + const float yb = gp.BB_Plot.Max.y; + snprintf(buf, 64, "%s = %.*f,%.*f", id, Precision(range_x), *x, Precision(range_y), *y); + ImVec2 label_pos = pos + ImVec2(16 * GImGui->Style.MouseCursorScale, 8 * GImGui->Style.MouseCursorScale); + ImVec2 label_size = ImGui::CalcTextSize(buf) + ImVec2(4,4); + label_pos = ClampLabelPos(label_pos, label_size, gp.BB_Plot.Min, gp.BB_Plot.Max); + DrawList.AddRectFilled(label_pos, label_pos + label_size, col32); + DrawList.AddText(label_pos + ImVec2(2,2), CalcTextColor(color), buf); + PopPlotClipRect(); + } } - bool dragging = false; if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { *x = ImPlot::GetPlotMousePos().x; diff --git a/implot.h b/implot.h index 929c604..2d79953 100644 --- a/implot.h +++ b/implot.h @@ -457,12 +457,12 @@ IMPLOT_API ImPlotLimits GetPlotLimits(int y_axis = IMPLOT_AUTO); // Plot Tools //----------------------------------------------------------------------------- -// Shows a draggable horizontal guide line. #col defaults to ImGuiCol_Text. -IMPLOT_API bool GrabLineH(const char* id, double* y_value, const ImVec4& col = IMPLOT_AUTO_COL, float thickness = 1); -// Shows a draggable vertical guide line. #col defaults to ImGuiCol_Text. -IMPLOT_API bool GrabLineV(const char* id, double* x_value, const ImVec4& col = IMPLOT_AUTO_COL, float thickness = 1); -// Shows a draggable anchor point. #col defaults to ImGuiCol_Text. -IMPLOT_API bool GrabPoint(const char* id, double* x, double* y, const ImVec4& col = IMPLOT_AUTO_COL, float radius = 5); +// Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text. +IMPLOT_API bool DragLineX(const char* id, double* x_value, bool show_label = true, const ImVec4& col = IMPLOT_AUTO_COL, float thickness = 1); +// Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text. +IMPLOT_API bool DragLineY(const char* id, double* y_value, bool show_label = true, const ImVec4& col = IMPLOT_AUTO_COL, float thickness = 1); +// Shows a draggable point at x,y. #col defaults to ImGuiCol_Text. +IMPLOT_API bool DragPoint(const char* id, double* x, double* y, bool show_label = true, const ImVec4& col = IMPLOT_AUTO_COL, float radius = 4); // Returns true if the current plot is being queried. Query must be enabled with ImPlotFlags_Query. IMPLOT_API bool IsPlotQueried(); diff --git a/implot_demo.cpp b/implot_demo.cpp index 86c8678..83369a2 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -805,36 +805,35 @@ void ShowDemoWindow(bool* p_open) { } } //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Grab Lines and Points")) { - ImGui::BulletText("Click and drag the horizontal and vertical guide lines."); + if (ImGui::CollapsingHeader("Drag Lines and Points")) { + ImGui::BulletText("Click and drag the horizontal and vertical lines."); static double x1 = 0.2; static double x2 = 0.8; static double y1 = 0.25; static double y2 = 0.75; static double f = 0.1; + static bool show_labels = true; + ImGui::Checkbox("Show Labels##1",&show_labels); if (ImPlot::BeginPlot("##guides",0,0,ImVec2(-1,0),ImPlotFlags_YAxis2)) { - ImPlot::GrabLineV("x1",&x1); - ImPlot::GrabLineV("x2",&x2); - ImPlot::GrabLineH("y1",&y1); - ImPlot::GrabLineH("y2",&y2); - ImPlot::SetPlotYAxis(1); - ImPlot::GrabLineH("f",&f, ImVec4(1,0.5f,1,1)); - - ImPlot::SetPlotYAxis(0); + ImPlot::DragLineX("x1",&x1,show_labels); + ImPlot::DragLineX("x2",&x2,show_labels); + ImPlot::DragLineY("y1",&y1,show_labels); + ImPlot::DragLineY("y2",&y2,show_labels); double xs[1000], ys[1000]; for (int i = 0; i < 1000; ++i) { xs[i] = (x2+x1)/2+abs(x2-x1)*(i/1000.0f - 0.5f); ys[i] = (y1+y2)/2+abs(y2-y1)/2*sin(f*i/10); } - ImPlot::PlotLine("Why Not?", xs, ys, 1000); + ImPlot::PlotLine("Interactive Data", xs, ys, 1000); + ImPlot::SetPlotYAxis(1); + ImPlot::DragLineY("f",&f,show_labels,ImVec4(1,0.5f,1,1)); ImPlot::EndPlot(); } - - ImGui::BulletText("Click and drag the anchor points."); + ImGui::BulletText("Click and drag any point."); + ImGui::Checkbox("Show Labels##2",&show_labels); ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks; - // ImPlot::SetNextPlotLimits(0,1,0,1,ImGuiCond_Always); - if (ImPlot::BeginPlot("##Bezier",0,0,ImVec2(-1,0),ImPlotFlags_CanvasOnly|ImPlotFlags_NoChild,flags,flags)) { - static ImPlotPoint P[] = {ImPlotPoint(0,0), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(1,1)}; + if (ImPlot::BeginPlot("##Bezier",0,0,ImVec2(-1,0),ImPlotFlags_CanvasOnly,flags,flags)) { + static ImPlotPoint P[] = {ImPlotPoint(.05f,.05f), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(.95f,.95f)}; static ImPlotPoint B[100]; for (int i = 0; i < 100; ++i) { double t = i / 99.0; @@ -845,19 +844,16 @@ void ShowDemoWindow(bool* p_open) { double w4 = t*t*t; B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y); } - static ImVec4 gray = ImVec4(0.5f,0.5f,0.5f,1.0f); - ImPlot::SetNextFillStyle(ImVec4(0,1,0,0.25f)); - ImPlot::PlotShaded("##bez",&B[0].x, &B[0].y, 100, 0, 0, sizeof(ImPlotPoint)); - ImPlot::SetNextLineStyle(ImVec4(0,1,0,1), 2); + ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), 2); ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, sizeof(ImPlotPoint)); - ImPlot::SetNextLineStyle(gray, 2); + ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1)); ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, sizeof(ImPlotPoint)); - ImPlot::SetNextLineStyle(gray, 2); + ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1)); ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, sizeof(ImPlotPoint)); - ImPlot::GrabPoint("P0",&P[0].x,&P[0].y,gray); - ImPlot::GrabPoint("P1",&P[1].x,&P[1].y,gray); - ImPlot::GrabPoint("P2",&P[2].x,&P[2].y,gray); - ImPlot::GrabPoint("P3",&P[3].x,&P[3].y,gray); + ImPlot::DragPoint("P0",&P[0].x,&P[0].y, show_labels, ImVec4(0,0.9f,0,1)); + ImPlot::DragPoint("P1",&P[1].x,&P[1].y, show_labels, ImVec4(1,0.5f,1,1)); + ImPlot::DragPoint("P2",&P[2].x,&P[2].y, show_labels, ImVec4(0,0.5f,1,1)); + ImPlot::DragPoint("P3",&P[3].x,&P[3].y, show_labels, ImVec4(0,0.9f,0,1)); ImPlot::EndPlot(); } } diff --git a/implot_internal.h b/implot_internal.h index e6eb290..8dd7e63 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -768,6 +768,15 @@ inline ImVec2 CalcTextSizeVertical(const char *text) { ImVec2 sz = ImGui::CalcTe // Returns white or black text given background color inline ImU32 CalcTextColor(const ImVec4& bg) { return (bg.x * 0.299 + bg.y * 0.587 + bg.z * 0.114) > 0.729 ? IM_COL32_BLACK : IM_COL32_WHITE; } +// Clamps a label position so that it fits a rect defined by Min/Max +inline ImVec2 ClampLabelPos(ImVec2 pos, const ImVec2& size, const ImVec2& Min, const ImVec2& Max) { + if (pos.x < Min.x) pos.x = Min.x; + if (pos.y < Min.y) pos.y = Min.y; + if ((pos.x + size.x) > Max.x) pos.x = Max.x - size.x; + if ((pos.y + size.y) > Max.y) pos.y = Max.y - size.y; + return pos; +} + //----------------------------------------------------------------------------- // [SECTION] Math and Misc Utils //-----------------------------------------------------------------------------