Skip to content

Commit 91fed82

Browse files
authored
Add files via upload
1 parent c4f5cd7 commit 91fed82

File tree

6 files changed

+486
-0
lines changed

6 files changed

+486
-0
lines changed

build_umplot_windows_mingw.bat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
gcc -O3 umplot.c -o umplot.umi -shared -Wl,--dll -static-libgcc -static -lraylib -L%cd% -lkernel32 -luser32 -lgdi32 -lwinmm

set_mingw_paths.bat

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
set mingw_path=C:\Program Files\CodeBlocks\MinGW
2+
set path=%path%;%mingw_path%\bin;%mingw_path%\lib;%mingw_path%\include

umplot.c

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
#include <float.h>
2+
#include <math.h>
3+
4+
#include "raylib.h"
5+
#include "umka_api.h"
6+
7+
8+
enum
9+
{
10+
STYLE_LINE = 1,
11+
STYLE_SCATTER
12+
};
13+
14+
15+
typedef struct
16+
{
17+
double x, y;
18+
} Point;
19+
20+
21+
typedef struct
22+
{
23+
int64_t kind;
24+
uint32_t color;
25+
double width;
26+
} Style;
27+
28+
29+
typedef struct
30+
{
31+
Point *points;
32+
int64_t numPoints;
33+
Style style;
34+
} Series;
35+
36+
37+
typedef struct
38+
{
39+
int64_t xNumLines, yNumLines;
40+
uint32_t color;
41+
bool labelled;
42+
int64_t fontSize;
43+
} Grid;
44+
45+
46+
typedef struct
47+
{
48+
Series *series;
49+
int64_t numSeries;
50+
Grid grid;
51+
} Plot;
52+
53+
54+
typedef struct
55+
{
56+
double dx, dy;
57+
double xScale, yScale;
58+
} ScreenTransform;
59+
60+
61+
static Rectangle getClientRect()
62+
{
63+
const int width = GetScreenWidth(), height = GetScreenHeight();
64+
return (Rectangle){0.15 * width, 0.05 * height, 0.8 * width, 0.8 * height};
65+
}
66+
67+
68+
static Vector2 getScreenPoint(const Point point, const ScreenTransform *transform)
69+
{
70+
return (Vector2){transform->xScale * (point.x - transform->dx), transform->yScale * (point.y - transform->dy)};
71+
}
72+
73+
74+
static Point getGraphPoint(const Vector2 point, const ScreenTransform *transform)
75+
{
76+
return (Point){point.x / transform->xScale + transform->dx, point.y / transform->yScale + transform->dy};
77+
}
78+
79+
80+
static void setTransformToMinMax(ScreenTransform *transform, const Point *minPt, const Point *maxPt)
81+
{
82+
Rectangle rect = getClientRect();
83+
84+
transform->xScale = (maxPt->x > minPt->x) ? (rect.width / (maxPt->x - minPt->x)) : 1.0;
85+
transform->yScale = (maxPt->y > minPt->y) ? -(rect.height / (maxPt->y - minPt->y)) : 1.0;
86+
87+
transform->dx = minPt->x - rect.x / transform->xScale;
88+
transform->dy = maxPt->y - rect.y / transform->yScale;
89+
}
90+
91+
92+
static void resetTransform(ScreenTransform *transform, const Plot *plot)
93+
{
94+
Point minPt = (Point){ DBL_MAX, DBL_MAX};
95+
Point maxPt = (Point){-DBL_MAX, -DBL_MAX};
96+
97+
for (int iSeries = 0; iSeries < plot->numSeries; iSeries++)
98+
{
99+
Series *series = &plot->series[iSeries];
100+
for (int iPt = 0; iPt < series->numPoints; iPt++)
101+
{
102+
const Point *pt = &series->points[iPt];
103+
if (pt->x > maxPt.x) maxPt.x = pt->x;
104+
if (pt->x < minPt.x) minPt.x = pt->x;
105+
if (pt->y > maxPt.y) maxPt.y = pt->y;
106+
if (pt->y < minPt.y) minPt.y = pt->y;
107+
}
108+
}
109+
110+
setTransformToMinMax(transform, &minPt, &maxPt);
111+
}
112+
113+
114+
static void resizeTransform(ScreenTransform *transform, const Rectangle *rect)
115+
{
116+
const Point minPt = getGraphPoint((Vector2){rect->x, rect->y + rect->height}, transform);
117+
const Point maxPt = getGraphPoint((Vector2){rect->x + rect->width, rect->y}, transform);
118+
119+
setTransformToMinMax(transform, &minPt, &maxPt);
120+
}
121+
122+
123+
static void panTransform(ScreenTransform *transform, const Vector2 *delta)
124+
{
125+
transform->dx -= delta->x / transform->xScale;
126+
transform->dy -= delta->y / transform->yScale;
127+
}
128+
129+
130+
static void zoomTransform(ScreenTransform *transform, const Plot *plot, const Rectangle *zoomRect)
131+
{
132+
if (zoomRect->width == 0 && zoomRect->height == 0)
133+
return;
134+
135+
if (zoomRect->width < 0 || zoomRect->height < 0)
136+
{
137+
resetTransform(transform, plot);
138+
return;
139+
}
140+
141+
resizeTransform(transform, zoomRect);
142+
}
143+
144+
145+
static void drawGraph(const Plot *plot, const ScreenTransform *transform)
146+
{
147+
Rectangle clientRect = getClientRect();
148+
BeginScissorMode(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
149+
150+
for (int iSeries = 0; iSeries < plot->numSeries; iSeries++)
151+
{
152+
Series *series = &plot->series[iSeries];
153+
154+
switch (series->style.kind)
155+
{
156+
case STYLE_LINE:
157+
{
158+
if (series->numPoints > 1)
159+
{
160+
Vector2 prevPt = getScreenPoint(series->points[0], transform);
161+
162+
for (int iPt = 1; iPt < series->numPoints; iPt++)
163+
{
164+
Vector2 pt = getScreenPoint(series->points[iPt], transform);
165+
DrawLineEx(prevPt, pt, series->style.width, *(Color *)&series->style.color);
166+
prevPt = pt;
167+
}
168+
}
169+
break;
170+
}
171+
172+
case STYLE_SCATTER:
173+
{
174+
for (int iPt = 0; iPt < series->numPoints; iPt++)
175+
{
176+
Vector2 pt = getScreenPoint(series->points[iPt], transform);
177+
DrawCircleV(pt, series->style.width, *(Color *)&series->style.color);
178+
}
179+
break;
180+
}
181+
182+
default: break;
183+
}
184+
}
185+
186+
EndScissorMode();
187+
}
188+
189+
190+
static void drawGrid(const Plot *plot, const ScreenTransform *transform)
191+
{
192+
if (plot->grid.xNumLines <= 0 || plot->grid.yNumLines <= 0)
193+
return;
194+
195+
const Rectangle clientRect = getClientRect();
196+
197+
const double xSpan = clientRect.width / transform->xScale;
198+
const double ySpan = -clientRect.height / transform->yScale;
199+
200+
double xStep = pow(10.0, floor(log10(xSpan / plot->grid.xNumLines)));
201+
double yStep = pow(10.0, floor(log10(ySpan / plot->grid.yNumLines)));
202+
203+
while (xSpan / xStep > 2.0 * plot->grid.xNumLines)
204+
xStep *= 2.0;
205+
206+
while (ySpan / yStep > 2.0 * plot->grid.yNumLines)
207+
yStep *= 2.0;
208+
209+
const Point minPt = getGraphPoint((Vector2){clientRect.x, clientRect.y + clientRect.height}, transform);
210+
const Point startPt = (Point){ceil(minPt.x / xStep) * xStep, ceil(minPt.y / yStep) * yStep};
211+
212+
Vector2 startPtScreen = getScreenPoint(startPt, transform);
213+
214+
// Vertical grid
215+
for (int i = 0, x = startPtScreen.x; x < clientRect.x + clientRect.width; i++, x = startPtScreen.x + i * xStep * transform->xScale)
216+
{
217+
// Line
218+
DrawLineEx((Vector2){x, clientRect.y}, (Vector2){x, clientRect.y + clientRect.height}, 1, *(Color *)&plot->grid.color);
219+
220+
// Label
221+
if (plot->grid.labelled)
222+
{
223+
const char *label = TextFormat((xStep > 0.01) ? "%.2f" : "%.4f", startPt.x + i * xStep);
224+
const int labelWidth = MeasureText(label, plot->grid.fontSize);
225+
226+
const int labelX = x - labelWidth / 2;
227+
const int labelY = clientRect.y + clientRect.height + plot->grid.fontSize;
228+
229+
DrawText(label, labelX, labelY, plot->grid.fontSize, *(Color *)&plot->grid.color);
230+
}
231+
}
232+
233+
// Horizontal grid
234+
for (int j = 0, y = startPtScreen.y; y > clientRect.y; j++, y = startPtScreen.y + j * yStep * transform->yScale)
235+
{
236+
// Line
237+
DrawLineEx((Vector2){clientRect.x, y}, (Vector2){clientRect.x + clientRect.width, y}, 1, *(Color *)&plot->grid.color);
238+
239+
// Label
240+
if (plot->grid.labelled)
241+
{
242+
const char *label = TextFormat((yStep > 0.01) ? "%.2f" : "%.4f", startPt.y + j * yStep);
243+
const int labelWidth = MeasureText(label, plot->grid.fontSize);
244+
245+
const int labelX = clientRect.x - labelWidth - plot->grid.fontSize;
246+
const int labelY = y - plot->grid.fontSize / 2;
247+
248+
DrawText(label, labelX, labelY, plot->grid.fontSize, *(Color *)&plot->grid.color);
249+
}
250+
}
251+
}
252+
253+
254+
static void drawZoomRect(Rectangle zoomRect, const ScreenTransform *transform)
255+
{
256+
if (zoomRect.width < 0)
257+
{
258+
zoomRect.x += zoomRect.width;
259+
zoomRect.width *= -1;
260+
}
261+
if (zoomRect.height < 0)
262+
{
263+
zoomRect.y += zoomRect.height;
264+
zoomRect.height *= -1;
265+
}
266+
267+
DrawRectangleLinesEx(zoomRect, 1, GRAY);
268+
}
269+
270+
271+
void umplot_plot(UmkaStackSlot *params, UmkaStackSlot *result)
272+
{
273+
Plot *plot = (Plot *) params[0].ptrVal;
274+
275+
SetTraceLogLevel(LOG_ERROR);
276+
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
277+
InitWindow(640, 480, "UmPlot");
278+
SetTargetFPS(60);
279+
280+
Rectangle clientRect = getClientRect();
281+
Rectangle zoomRect = clientRect;
282+
bool showZoomRect = false;
283+
284+
ScreenTransform transform;
285+
resetTransform(&transform, plot);
286+
287+
while (!WindowShouldClose())
288+
{
289+
// Handle input
290+
const Vector2 pos = GetMousePosition();
291+
const Vector2 delta = GetMouseDelta();
292+
293+
// Resizing
294+
if (IsWindowResized())
295+
{
296+
resizeTransform(&transform, &clientRect);
297+
clientRect = zoomRect = getClientRect();
298+
}
299+
300+
// Zooming
301+
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && CheckCollisionPointRec(pos, clientRect))
302+
{
303+
zoomRect = (Rectangle){pos.x, pos.y, 0, 0};
304+
showZoomRect = true;
305+
}
306+
307+
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) && CheckCollisionPointRec(pos, clientRect))
308+
{
309+
zoomRect.width = pos.x - zoomRect.x;
310+
zoomRect.height = pos.y - zoomRect.y;
311+
}
312+
313+
if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
314+
{
315+
zoomTransform(&transform, plot, &zoomRect);
316+
zoomRect = getClientRect();
317+
showZoomRect = false;
318+
}
319+
320+
// Panning
321+
if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT) && CheckCollisionPointRec(pos, clientRect))
322+
{
323+
panTransform(&transform, &delta);
324+
}
325+
326+
// Draw
327+
BeginDrawing();
328+
ClearBackground(WHITE);
329+
330+
// Graph border
331+
DrawRectangleLinesEx(clientRect, 1, BLACK);
332+
333+
// Grid
334+
drawGrid(plot, &transform);
335+
336+
// Graph
337+
drawGraph(plot, &transform);
338+
339+
// Zoom rectangle
340+
if (showZoomRect)
341+
drawZoomRect(zoomRect, &transform);
342+
343+
EndDrawing();
344+
}
345+
346+
CloseWindow();
347+
result->intVal = 1;
348+
}

umplot.png

24.7 KB
Loading

0 commit comments

Comments
 (0)