share code for axis widgets.

This commit is contained in:
Martin Preuss
2025-09-24 23:34:54 +02:00
parent 91b0878ced
commit 86af358cd1
8 changed files with 513 additions and 78 deletions

View File

@@ -79,6 +79,7 @@
<headers dist="true" install="$(pkgincludedir)"> <headers dist="true" install="$(pkgincludedir)">
graph.h graph.h
w_graph.h w_graph.h
w_axis.h
w_xaxis.h w_xaxis.h
w_yaxis.h w_yaxis.h
w_viewport.h w_viewport.h
@@ -88,8 +89,7 @@
<headers dist="true"> <headers dist="true">
graph_p.h graph_p.h
w_graph_p.h w_graph_p.h
w_xaxis_p.h w_axis_p.h
w_yaxis_p.h
w_viewport_p.h w_viewport_p.h
</headers> </headers>
@@ -98,6 +98,7 @@
$(local/typefiles) $(local/typefiles)
graph.c graph.c
w_graph.c w_graph.c
w_axis.c
w_xaxis.c w_xaxis.c
w_yaxis.c w_yaxis.c
w_viewport.c w_viewport.c

View File

@@ -87,6 +87,19 @@
} \n } \n
} \n } \n
vRun+=v; \n vRun+=v; \n
} \n
\n
vRun=startValue; \n
v/=10.0; \n
while(vRun&lt;=(st-&gt;maxValue)) { \n
if (vRun&gt;=(st-&gt;minValue)) { \n
if (!$(struct_prefix)_HasTickValue(st, vRun)) { \n
GWEN_Buffer_AppendArgs(dbuf, "%.*f", st-&gt;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 } \n
GWEN_Buffer_free(dbuf); \n GWEN_Buffer_free(dbuf); \n
} \n } \n

View File

@@ -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 <config.h>
#endif
#include "./w_axis_p.h"
#include "aqdiagram/draw/w_drawable.h"
#include "aqdiagram/draw/w_label.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* 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;
}

View File

@@ -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 <aqdiagram/graph/w_graph.h>
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

View File

@@ -1,21 +1,23 @@
/**************************************************************************** /****************************************************************************
* This file is part of the project AqDiagram. * 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 * The license for this file can be found in the file COPYING which you
* should have received along with this file. * should have received along with this file.
****************************************************************************/ ****************************************************************************/
#ifndef AQDG_GRAPH_W_XAXIS_P_H #ifndef AQDG_GRAPH_W_AXIS_P_H
#define AQDG_GRAPH_W_XAXIS_P_H #define AQDG_GRAPH_W_AXIS_P_H
#include <aqdiagram/graph/w_xaxis.h> #include <aqdiagram/graph/w_axis.h>
typedef struct AQDG_WIDGET_XAXIS AQDG_WIDGET_XAXIS; typedef struct AQDG_WIDGET_AXIS AQDG_WIDGET_AXIS;
struct AQDG_WIDGET_XAXIS { struct AQDG_WIDGET_AXIS {
AQDG_OBJECT *graphObject; AQDG_OBJECT *graphObject;
int axisIndex; int axisIndex;
double minValue;
double maxValue;
}; };

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
* This file is part of the project AqDiagram. * 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 * The license for this file can be found in the file COPYING which you
* should have received along with this file. * should have received along with this file.
@@ -10,8 +10,10 @@
# include <config.h> # include <config.h>
#endif #endif
#include "./w_xaxis_p.h" #include "./w_xaxis.h"
#include "./w_axis.h"
#include "aqdiagram/draw/w_drawable.h" #include "aqdiagram/draw/w_drawable.h"
#include "aqdiagram/draw/w_label.h"
#include <gwenhywfar/debug.h> #include <gwenhywfar/debug.h>
@@ -22,9 +24,10 @@
* ------------------------------------------------------------------------------------------------ * ------------------------------------------------------------------------------------------------
*/ */
static GWENHYWFAR_CB void _freeData(void *bp, void *p);
static int _calcContentDims(AQDG_OBJECT *object); 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 *AQDG_XAxisWidget_new(AQDG_OBJECT *parent, uint32_t options, AQDG_OBJECT *graphObject, int axisIndex)
{ {
AQDG_OBJECT *o; 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"); 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_Object_SetCalcContentDimsFn(o, _calcContentDims);
AQDG_DrawableWidget_SetDrawFn(o, _draw);
return o; 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) int _calcContentDims(AQDG_OBJECT *object)
{ {
/* TODO /* TODO
@@ -83,7 +65,7 @@ int _calcContentDims(AQDG_OBJECT *object)
int _draw(AQDG_OBJECT *object) int _layout(AQDG_OBJECT *object)
{ {
/* TODO */ /* TODO */
return 1; 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)));
}

View File

@@ -10,7 +10,8 @@
# include <config.h> # include <config.h>
#endif #endif
#include "./w_yaxis_p.h" #include "./w_yaxis.h"
#include "./w_axis.h"
#include "aqdiagram/draw/w_drawable.h" #include "aqdiagram/draw/w_drawable.h"
#include <gwenhywfar/debug.h> #include <gwenhywfar/debug.h>
@@ -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 *AQDG_YAxisWidget_new(AQDG_OBJECT *parent, uint32_t options, AQDG_OBJECT *graphObject, int axisIndex)
{ {
AQDG_OBJECT *o; AQDG_OBJECT *o;
AQDG_WIDGET_YAXIS *xo;
o=AQDG_DrawableWidget_new(parent, options, AQDG_DrawableWidget_GetDrawContext(graphObject)); o=AQDG_AxisWidget_new(parent, options, graphObject, axisIndex);
AQDG_Object_SetName(o, "XAxisWidget"); AQDG_Object_SetName(o, "YAxisWidget");
GWEN_NEW_OBJECT(AQDG_WIDGET_YAXIS, xo);
GWEN_INHERIT_SETDATA(AQDG_OBJECT, AQDG_WIDGET_YAXIS, o, xo, _freeData);
xo->graphObject=graphObject; AQDG_Object_SetLayoutFn(o, _layout);
xo->axisIndex=axisIndex; AQDG_Object_SetCalcContentDimsFn(o, _calcContentDims);
return o; return o;
} }
int _calcContentDims(AQDG_OBJECT *object)
GWENHYWFAR_CB void _freeData(void *bp, void *p)
{ {
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))));
} }

View File

@@ -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 <aqdiagram/graph/w_yaxis.h>
typedef struct AQDG_WIDGET_YAXIS AQDG_WIDGET_YAXIS;
struct AQDG_WIDGET_YAXIS {
AQDG_OBJECT *graphObject;
int axisIndex;
};
#endif