diff --git a/src/lib/aqdiagram/graph/0BUILD b/src/lib/aqdiagram/graph/0BUILD index b619857..403205f 100644 --- a/src/lib/aqdiagram/graph/0BUILD +++ b/src/lib/aqdiagram/graph/0BUILD @@ -79,6 +79,7 @@ graph.h w_graph.h + w_axis.h w_xaxis.h w_yaxis.h w_viewport.h @@ -88,8 +89,7 @@ graph_p.h w_graph_p.h - w_xaxis_p.h - w_yaxis_p.h + w_axis_p.h w_viewport_p.h @@ -98,6 +98,7 @@ $(local/typefiles) graph.c w_graph.c + w_axis.c w_xaxis.c w_yaxis.c w_viewport.c diff --git a/src/lib/aqdiagram/graph/axis.t2d b/src/lib/aqdiagram/graph/axis.t2d index abf7bf8..cc98872 100644 --- a/src/lib/aqdiagram/graph/axis.t2d +++ b/src/lib/aqdiagram/graph/axis.t2d @@ -87,6 +87,19 @@ } \n } \n vRun+=v; \n + } \n + \n + vRun=startValue; \n + v/=10.0; \n + while(vRun<=(st->maxValue)) { \n + if (vRun>=(st->minValue)) { \n + if (!$(struct_prefix)_HasTickValue(st, vRun)) { \n + GWEN_Buffer_AppendArgs(dbuf, "%.*f", st->precision, vRun); \n + $(struct_prefix)_AddNewTick(st, GWEN_Buffer_GetStart(dbuf), vRun, 1, 0); \n + GWEN_Buffer_Reset(dbuf); \n + } \n + } \n + vRun+=v; \n } \n GWEN_Buffer_free(dbuf); \n } \n diff --git a/src/lib/aqdiagram/graph/w_axis.c b/src/lib/aqdiagram/graph/w_axis.c new file mode 100644 index 0000000..35c0503 --- /dev/null +++ b/src/lib/aqdiagram/graph/w_axis.c @@ -0,0 +1,281 @@ +/**************************************************************************** + * This file is part of the project AqDiagram. + * AqDiagram (c) by 2025 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "./w_axis_p.h" +#include "aqdiagram/draw/w_drawable.h" +#include "aqdiagram/draw/w_label.h" + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static GWENHYWFAR_CB void _freeData(void *bp, void *p); + +static void _setupObjectTree(AQDG_OBJECT *o); +static void _createTickLabelsForLevel(AQDG_OBJECT *o, AQDG_GRAPH *graph, const AQDG_GRAPH_TICK_LIST *tickList, int level); +static int _doesCollideWithChildren(const AQDG_OBJECT *object, const AQDG_OBJECT *newObject); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +GWEN_INHERIT(AQDG_OBJECT, AQDG_WIDGET_AXIS); + + + +AQDG_OBJECT *AQDG_AxisWidget_new(AQDG_OBJECT *parent, uint32_t options, AQDG_OBJECT *graphObject, int axisIndex) +{ + AQDG_OBJECT *o; + AQDG_WIDGET_AXIS *xo; + + o=AQDG_DrawableWidget_new(parent, options, AQDG_DrawableWidget_GetDrawContext(graphObject)); + AQDG_Object_SetName(o, "AxisWidget"); + GWEN_NEW_OBJECT(AQDG_WIDGET_AXIS, xo); + GWEN_INHERIT_SETDATA(AQDG_OBJECT, AQDG_WIDGET_AXIS, o, xo, _freeData); + + xo->graphObject=graphObject; + xo->axisIndex=axisIndex; + + _setupObjectTree(o); + + return o; +} + + + + +GWENHYWFAR_CB void _freeData(void *bp, void *p) +{ + AQDG_WIDGET_AXIS *xo; + + xo=(AQDG_WIDGET_AXIS*) p; + + GWEN_FREE_OBJECT(xo); +} + + + +AQDG_OBJECT *AQDG_AxisWidget_GetGraphObject(const AQDG_OBJECT *o) +{ + if (o) { + AQDG_WIDGET_AXIS *xo; + + xo=GWEN_INHERIT_GETDATA(AQDG_OBJECT, AQDG_WIDGET_AXIS, o); + return xo?xo->graphObject:NULL; + } + return NULL; +} + + + +int AQDG_AxisWidget_GetAxisIndex(const AQDG_OBJECT *o) +{ + if (o) { + AQDG_WIDGET_AXIS *xo; + + xo=GWEN_INHERIT_GETDATA(AQDG_OBJECT, AQDG_WIDGET_AXIS, o); + return xo?xo->axisIndex:-1; + } + return -1; +} + + +double AQDG_AxisWidget_GetMinValue(const AQDG_OBJECT *o) +{ + if (o) { + AQDG_WIDGET_AXIS *xo; + + xo=GWEN_INHERIT_GETDATA(AQDG_OBJECT, AQDG_WIDGET_AXIS, o); + return xo?xo->minValue:0.0; + } + return 0.0; +} + + + +void AQDG_AxisWidget_SetMinValue(AQDG_OBJECT *o, double v) +{ + if (o) { + AQDG_WIDGET_AXIS *xo; + + xo=GWEN_INHERIT_GETDATA(AQDG_OBJECT, AQDG_WIDGET_AXIS, o); + if (xo) + xo->minValue=v; + } +} + + + +double AQDG_AxisWidget_GetMaxValue(const AQDG_OBJECT *o) +{ + if (o) { + AQDG_WIDGET_AXIS *xo; + + xo=GWEN_INHERIT_GETDATA(AQDG_OBJECT, AQDG_WIDGET_AXIS, o); + return xo?xo->maxValue:0.0; + } + return 0.0; +} + + + +void AQDG_AxisWidget_SetMaxValue(AQDG_OBJECT *o, double v) +{ + if (o) { + AQDG_WIDGET_AXIS *xo; + + xo=GWEN_INHERIT_GETDATA(AQDG_OBJECT, AQDG_WIDGET_AXIS, o); + if (xo) + xo->maxValue=v; + } +} + + + + + +void _setupObjectTree(AQDG_OBJECT *o) +{ + AQDG_WIDGET_AXIS *xo; + + xo=GWEN_INHERIT_GETDATA(AQDG_OBJECT, AQDG_WIDGET_AXIS, o); + if (xo) { + AQDG_GRAPH *graph; + AQDG_GRAPH_AXIS *axis; + + graph=AQDG_GraphWidget_GetGraph(xo->graphObject); + axis=AQDG_Graph_GetAxisByIndex(graph, xo->axisIndex); + if (axis) { + const AQDG_GRAPH_TICK_LIST *tickList; + + xo->minValue=AQDG_Graph_Axis_GetMinValue(axis); + xo->maxValue=AQDG_Graph_Axis_GetMaxValue(axis); + + tickList=AQDG_Graph_Axis_GetTickList(axis); + if (tickList) + _createTickLabelsForLevel(o, graph, tickList, 0); + } + } +} + + + +void _createTickLabelsForLevel(AQDG_OBJECT *o, AQDG_GRAPH *graph, const AQDG_GRAPH_TICK_LIST *tickList, int level) +{ + AQDG_WIDGET_AXIS *xo; + const AQDG_GRAPH_TICK *tick; + AQDG_DRAW_CONTEXT *dc; + uint32_t opts=0; + + xo=GWEN_INHERIT_GETDATA(AQDG_OBJECT, AQDG_WIDGET_AXIS, o); + + if (xo->axisIndex==AQDG_GRAPH_AXISPOS_TOP) + opts=AQDG_DRAWABLE_OPTIONS_JUSTIFY_BOTTOM; + else if (xo->axisIndex==AQDG_GRAPH_AXISPOS_LEFT) + opts=AQDG_DRAWABLE_OPTIONS_JUSTIFY_RIGHT; + + dc=AQDG_DrawableWidget_GetDrawContext(o); + + tick=AQDG_Graph_Tick_List_First(tickList); + while(tick) { + if (AQDG_Graph_Tick_GetLevel(tick)==level) { + const char *s; + + s=AQDG_Graph_Tick_GetLabel(tick); + if (s && *s) { + AQDG_OBJECT *oNew; + + oNew=AQDG_LabelWidget_new(o, opts, dc, s); + AQDG_DrawableWidget_SetValue(oNew, AQDG_Graph_Tick_GetValue(tick)); + } + } + + tick=AQDG_Graph_Tick_List_Next(tick); + } +} + + + +/* + * TODO: + * - set positions of all children according to value factor and offset + * - loop: + * - for every child at level 0: _doesCollideWithChildren() positive on any? + * - yes: + * - remove every 2nd child, goto loop + * - no: okay, done + * + * - YAxis: + * - calc: pos= height - ( (value-minValue) * (height / (maxValue-minValue) ) ) + */ + + + +int AQDG_AxisWidget_DoesCollideWithOtherChildren(const AQDG_OBJECT *object, const AQDG_OBJECT *newObject) +{ + AQDG_OBJECT *child; + int xLeft; + int xRight; + int yTop; + int yBottom; + + xLeft=AQDG_Object_GetRelativeX(newObject); + xRight=xLeft+AQDG_Object_GetWidth(newObject); + yTop=AQDG_Object_GetRelativeY(newObject); + yBottom=yTop+AQDG_Object_GetHeight(newObject); + + child=AQDG_Object_Tree2_GetFirstChild(object); + while(child) { + if (child!=newObject) { + if (!(AQDG_Object_GetFlags(child) & AQDG_OBJECT_FLAGS_HIDDEN)) { + int xChildLeft; + int xChildRight; + int yChildTop; + int yChildBottom; + + xChildLeft=AQDG_Object_GetRelativeX(child); + yChildTop=AQDG_Object_GetRelativeY(child); + xChildRight=xChildLeft+AQDG_Object_GetWidth(child); + yChildBottom=yChildTop+AQDG_Object_GetHeight(child); + + if (! + ( + (xRight<=xChildLeft) || /* new object completely is left of child */ + (yBottom<=yChildTop) || /* new object completely is above child */ + (xLeft>=xChildRight) || /* new object is completely right of child */ + (yTop>=yChildBottom) /* new object is completely below child */ + ) + ) + return 1; + } + } + child=AQDG_Object_Tree2_GetNext(child); + } + + return 0; +} + + + + + + + + diff --git a/src/lib/aqdiagram/graph/w_axis.h b/src/lib/aqdiagram/graph/w_axis.h new file mode 100644 index 0000000..8ff6a8a --- /dev/null +++ b/src/lib/aqdiagram/graph/w_axis.h @@ -0,0 +1,33 @@ +/**************************************************************************** + * This file is part of the project AqDiagram. + * AqDiagram (c) by 2025 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQDG_GRAPH_W_AXIS_H +#define AQDG_GRAPH_W_AXIS_H + +#include + + +AQDG_API AQDG_OBJECT *AQDG_AxisWidget_new(AQDG_OBJECT *parent, uint32_t options, AQDG_OBJECT *graphObject, int axisIndex); + +AQDG_API AQDG_OBJECT *AQDG_AxisWidget_GetGraphObject(const AQDG_OBJECT *o); +AQDG_API int AQDG_AxisWidget_GetAxisIndex(const AQDG_OBJECT *o); + + +AQDG_API double AQDG_AxisWidget_GetMinValue(const AQDG_OBJECT *o); +AQDG_API void AQDG_AxisWidget_SetMinValue(AQDG_OBJECT *o, double v); + +AQDG_API double AQDG_AxisWidget_GetMaxValue(const AQDG_OBJECT *o); +AQDG_API void AQDG_AxisWidget_SetMaxValue(AQDG_OBJECT *o, double v); + +AQDG_API int AQDG_AxisWidget_DoesCollideWithOtherChildren(const AQDG_OBJECT *object, const AQDG_OBJECT *newObject); + + + + +#endif + diff --git a/src/lib/aqdiagram/graph/w_xaxis_p.h b/src/lib/aqdiagram/graph/w_axis_p.h similarity index 58% rename from src/lib/aqdiagram/graph/w_xaxis_p.h rename to src/lib/aqdiagram/graph/w_axis_p.h index e904b30..b19011b 100644 --- a/src/lib/aqdiagram/graph/w_xaxis_p.h +++ b/src/lib/aqdiagram/graph/w_axis_p.h @@ -1,21 +1,23 @@ /**************************************************************************** * This file is part of the project AqDiagram. - * AqDiagram (c) by 2024 Martin Preuss, all rights reserved. + * AqDiagram (c) by 2025 Martin Preuss, all rights reserved. * * The license for this file can be found in the file COPYING which you * should have received along with this file. ****************************************************************************/ -#ifndef AQDG_GRAPH_W_XAXIS_P_H -#define AQDG_GRAPH_W_XAXIS_P_H +#ifndef AQDG_GRAPH_W_AXIS_P_H +#define AQDG_GRAPH_W_AXIS_P_H -#include +#include -typedef struct AQDG_WIDGET_XAXIS AQDG_WIDGET_XAXIS; -struct AQDG_WIDGET_XAXIS { +typedef struct AQDG_WIDGET_AXIS AQDG_WIDGET_AXIS; +struct AQDG_WIDGET_AXIS { AQDG_OBJECT *graphObject; int axisIndex; + double minValue; + double maxValue; }; diff --git a/src/lib/aqdiagram/graph/w_xaxis.c b/src/lib/aqdiagram/graph/w_xaxis.c index d970dee..dbded79 100644 --- a/src/lib/aqdiagram/graph/w_xaxis.c +++ b/src/lib/aqdiagram/graph/w_xaxis.c @@ -1,6 +1,6 @@ /**************************************************************************** * This file is part of the project AqDiagram. - * AqDiagram (c) by 2024 Martin Preuss, all rights reserved. + * AqDiagram (c) by 2025 Martin Preuss, all rights reserved. * * The license for this file can be found in the file COPYING which you * should have received along with this file. @@ -10,8 +10,10 @@ # include #endif -#include "./w_xaxis_p.h" +#include "./w_xaxis.h" +#include "./w_axis.h" #include "aqdiagram/draw/w_drawable.h" +#include "aqdiagram/draw/w_label.h" #include @@ -22,9 +24,10 @@ * ------------------------------------------------------------------------------------------------ */ -static GWENHYWFAR_CB void _freeData(void *bp, void *p); static int _calcContentDims(AQDG_OBJECT *object); -static int _draw(AQDG_OBJECT *object); +static int _layout(AQDG_OBJECT *object); +static int _calcHorizontalPos(double value, int contentSize, int borderSize, double minValue, double maxValue); + @@ -33,43 +36,22 @@ static int _draw(AQDG_OBJECT *object); * ------------------------------------------------------------------------------------------------ */ -GWEN_INHERIT(AQDG_OBJECT, AQDG_WIDGET_XAXIS); - - AQDG_OBJECT *AQDG_XAxisWidget_new(AQDG_OBJECT *parent, uint32_t options, AQDG_OBJECT *graphObject, int axisIndex) { AQDG_OBJECT *o; - AQDG_WIDGET_XAXIS *xo; - o=AQDG_DrawableWidget_new(parent, options, AQDG_DrawableWidget_GetDrawContext(graphObject)); + o=AQDG_AxisWidget_new(parent, options, graphObject, axisIndex); AQDG_Object_SetName(o, "XAxisWidget"); - GWEN_NEW_OBJECT(AQDG_WIDGET_XAXIS, xo); - GWEN_INHERIT_SETDATA(AQDG_OBJECT, AQDG_WIDGET_XAXIS, o, xo, _freeData); - - xo->graphObject=graphObject; - xo->axisIndex=axisIndex; + AQDG_Object_SetLayoutFn(o, _layout); AQDG_Object_SetCalcContentDimsFn(o, _calcContentDims); - AQDG_DrawableWidget_SetDrawFn(o, _draw); return o; } - -GWENHYWFAR_CB void _freeData(void *bp, void *p) -{ - AQDG_WIDGET_XAXIS *xo; - - xo=(AQDG_WIDGET_XAXIS*) p; - - GWEN_FREE_OBJECT(xo); -} - - - int _calcContentDims(AQDG_OBJECT *object) { /* TODO @@ -83,7 +65,7 @@ int _calcContentDims(AQDG_OBJECT *object) -int _draw(AQDG_OBJECT *object) +int _layout(AQDG_OBJECT *object) { /* TODO */ return 1; @@ -91,9 +73,75 @@ int _draw(AQDG_OBJECT *object) +void _setChildrenRelXFromValue(AQDG_OBJECT *o) +{ + AQDG_OBJECT *child; + int contentSize; + int borderSize; + double minValue; + double maxValue; + + contentSize=AQDG_Object_GetContentWidth(o); + borderSize=AQDG_Object_GetBorderLeft(o); + minValue=AQDG_AxisWidget_GetMinValue(o); + maxValue=AQDG_AxisWidget_GetMaxValue(o); + + child=AQDG_Object_Tree2_GetFirstChild(o); + while(child) { + int pos; + double value; + + value=AQDG_DrawableWidget_GetValue(child); + pos=_calcHorizontalPos(value, contentSize, borderSize, minValue, maxValue); + AQDG_Object_SetRelativeX(child, pos); /* TODO: take label size into account */ + child=AQDG_Object_Tree2_GetNext(child); + } +} +void _setChildrenRelY(AQDG_OBJECT *o) +{ + int contentSize; + + contentSize=AQDG_Object_GetContentHeight(o); + + if (AQDG_AxisWidget_GetAxisIndex(o)==AQDG_GRAPH_AXISPOS_BOTTOM) { + AQDG_OBJECT *child; + int borderSize; + + borderSize=AQDG_Object_GetBorderTop(o); + /* align left */ + child=AQDG_Object_Tree2_GetFirstChild(o); + while(child) { + AQDG_Object_SetRelativeY(child, borderSize); + child=AQDG_Object_Tree2_GetNext(child); + } + } + else { + AQDG_OBJECT *child; + int borderSize; + + /* align right */ + borderSize=AQDG_Object_GetBorderBottom(o); + child=AQDG_Object_Tree2_GetFirstChild(o); + while(child) { + int pos; + int cheight; + + cheight=AQDG_Object_GetHeight(child); + pos=contentSize-cheight-borderSize; + AQDG_Object_SetRelativeY(child, pos); + child=AQDG_Object_Tree2_GetNext(child); + } + } +} +int _calcHorizontalPos(double value, int contentSize, int borderSize, double minValue, double maxValue) +{ + return borderSize+((value-minValue)*(contentSize/(maxValue-minValue))); +} + + diff --git a/src/lib/aqdiagram/graph/w_yaxis.c b/src/lib/aqdiagram/graph/w_yaxis.c index e55f601..878e217 100644 --- a/src/lib/aqdiagram/graph/w_yaxis.c +++ b/src/lib/aqdiagram/graph/w_yaxis.c @@ -10,7 +10,8 @@ # include #endif -#include "./w_yaxis_p.h" +#include "./w_yaxis.h" +#include "./w_axis.h" #include "aqdiagram/draw/w_drawable.h" #include @@ -22,7 +23,9 @@ * ------------------------------------------------------------------------------------------------ */ -static GWENHYWFAR_CB void _freeData(void *bp, void *p); +static int _calcContentDims(AQDG_OBJECT *object); +static int _layout(AQDG_OBJECT *object); +static int _calcVerticalPos(double value, int contentSize, int borderSize, double minValue, double maxValue); @@ -31,36 +34,114 @@ static GWENHYWFAR_CB void _freeData(void *bp, void *p); * ------------------------------------------------------------------------------------------------ */ -GWEN_INHERIT(AQDG_OBJECT, AQDG_WIDGET_YAXIS); - - - AQDG_OBJECT *AQDG_YAxisWidget_new(AQDG_OBJECT *parent, uint32_t options, AQDG_OBJECT *graphObject, int axisIndex) { AQDG_OBJECT *o; - AQDG_WIDGET_YAXIS *xo; - o=AQDG_DrawableWidget_new(parent, options, AQDG_DrawableWidget_GetDrawContext(graphObject)); - AQDG_Object_SetName(o, "XAxisWidget"); - GWEN_NEW_OBJECT(AQDG_WIDGET_YAXIS, xo); - GWEN_INHERIT_SETDATA(AQDG_OBJECT, AQDG_WIDGET_YAXIS, o, xo, _freeData); + o=AQDG_AxisWidget_new(parent, options, graphObject, axisIndex); + AQDG_Object_SetName(o, "YAxisWidget"); - xo->graphObject=graphObject; - xo->axisIndex=axisIndex; + AQDG_Object_SetLayoutFn(o, _layout); + AQDG_Object_SetCalcContentDimsFn(o, _calcContentDims); return o; } - -GWENHYWFAR_CB void _freeData(void *bp, void *p) +int _calcContentDims(AQDG_OBJECT *object) { - AQDG_WIDGET_YAXIS *xo; + /* TODO + * - get axis + * - get ticks + * - calc max height of tick labels + * - add size of axis line + tick lines + spacing + */ + return 1; +} - xo=(AQDG_WIDGET_YAXIS*) p; - GWEN_FREE_OBJECT(xo); + +int _layout(AQDG_OBJECT *object) +{ + /* TODO */ + return 1; +} + + + +void _setChildrenRelX(AQDG_OBJECT *o) +{ + int contentSize; + + contentSize=AQDG_Object_GetContentWidth(o); + + if (AQDG_AxisWidget_GetAxisIndex(o)==AQDG_GRAPH_AXISPOS_RIGHT) { + AQDG_OBJECT *child; + int borderSize; + + borderSize=AQDG_Object_GetBorderLeft(o); + /* align left */ + child=AQDG_Object_Tree2_GetFirstChild(o); + while(child) { + AQDG_Object_SetRelativeX(child, borderSize); + child=AQDG_Object_Tree2_GetNext(child); + } + } + else { + AQDG_OBJECT *child; + int borderSize; + + /* align right */ + borderSize=AQDG_Object_GetBorderRight(o); + child=AQDG_Object_Tree2_GetFirstChild(o); + while(child) { + int pos; + int cwidth; + + cwidth=AQDG_Object_GetWidth(child); + pos=contentSize-cwidth-borderSize; + AQDG_Object_SetRelativeX(child, pos); /* TODO: take label size into account */ + child=AQDG_Object_Tree2_GetNext(child); + } + } +} + + + +void _setChildrenRelYFromValue(AQDG_OBJECT *o) +{ + AQDG_OBJECT *child; + int contentSize; + int borderSize; + double minValue; + double maxValue; + + contentSize=AQDG_Object_GetContentHeight(o); + borderSize=AQDG_Object_GetBorderTop(o); + minValue=AQDG_AxisWidget_GetMinValue(o); + maxValue=AQDG_AxisWidget_GetMaxValue(o); + + child=AQDG_Object_Tree2_GetFirstChild(o); + while(child) { + int pos; + double value; + + value=AQDG_DrawableWidget_GetValue(child); + pos=_calcVerticalPos(value, contentSize, borderSize, minValue, maxValue); + AQDG_Object_SetRelativeY(child, pos); /* TODO: take label size into account */ + child=AQDG_Object_Tree2_GetNext(child); + } +} + + + +/** + * special case because 0/0 is top left corner, so we need to flip contentSize + */ +int _calcVerticalPos(double value, int contentSize, int borderSize, double minValue, double maxValue) +{ + return borderSize+(contentSize-((value-minValue)*(contentSize/(maxValue-minValue)))); } diff --git a/src/lib/aqdiagram/graph/w_yaxis_p.h b/src/lib/aqdiagram/graph/w_yaxis_p.h deleted file mode 100644 index 7e1da2e..0000000 --- a/src/lib/aqdiagram/graph/w_yaxis_p.h +++ /dev/null @@ -1,24 +0,0 @@ -/**************************************************************************** - * This file is part of the project AqDiagram. - * AqDiagram (c) by 2023 Martin Preuss, all rights reserved. - * - * The license for this file can be found in the file COPYING which you - * should have received along with this file. - ****************************************************************************/ - -#ifndef AQDG_GRAPH_W_YAXIS_P_H -#define AQDG_GRAPH_W_YAXIS_P_H - -#include - - -typedef struct AQDG_WIDGET_YAXIS AQDG_WIDGET_YAXIS; -struct AQDG_WIDGET_YAXIS { - AQDG_OBJECT *graphObject; - int axisIndex; -}; - - - -#endif -