Skip to content

Commit

Permalink
Rework transforms and zooming, fix panning with right mouse button
Browse files Browse the repository at this point in the history
  • Loading branch information
rj45 committed May 12, 2024
1 parent c349205 commit e3449d9
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 83 deletions.
6 changes: 3 additions & 3 deletions src/assets.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
limitations under the License.
*/

#ifndef FONT_H
#define FONT_H
#ifndef ASSETS_H
#define ASSETS_H

#include <stdint.h>

extern unsigned int assets_zip_len;
extern const unsigned char assets_zip[];

#endif // FONT_H
#endif // ASSETS_H
4 changes: 4 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ void frame(void *user_data) {
sgp_begin(width, height);
sgp_set_blend_mode(SGP_BLENDMODE_BLEND);

ux_update(&app->circuit);

if (!app->loaded) {
if (nk_begin(
ctx, "Load example file",
Expand All @@ -238,10 +240,12 @@ void frame(void *user_data) {
nk_end(ctx);
}

draw_begin_frame(&app->draw);
app->circuit.input.frameDuration = sapp_frame_duration();
ux_draw(&app->circuit);
app->circuit.input.scroll = HMM_V2(0, 0);
app->circuit.input.mouseDelta = HMM_V2(0, 0);
draw_end_frame(&app->draw);

sg_pass pass = {
.action =
Expand Down
149 changes: 80 additions & 69 deletions src/render/draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@
#include "sokol_gfx.h"
#include "sokol_gp.h"

static inline sgp_vec2 mat2x3_vec2_mul(const sgp_mat2x3 *m, const sgp_vec2 *v) {
sgp_vec2 u = {
m->v[0][0] * v->x + m->v[0][1] * v->y + m->v[0][2],
m->v[1][0] * v->x + m->v[1][1] * v->y + m->v[1][2]};
return u;
static inline HMM_Vec2 mat2x3_vec2_mul(const sgp_mat2x3 *m, HMM_Vec2 v) {
return HMM_V2(
m->v[0][0] * v.X + m->v[0][1] * v.Y + m->v[0][2],
m->v[1][0] * v.X + m->v[1][1] * v.Y + m->v[1][2]);
}

// static inline sgp_vec2
Expand Down Expand Up @@ -71,29 +70,60 @@ void draw_init(DrawContext *draw, FONScontext *fontstash) {
.pan = HMM_V2(0.0f, 0.0f),
.zoom = 1.0f,
.fontstash = fontstash,
.polyliner = pl_create()};
.polyliner = pl_create(),
.transform =
(sgp_mat2x3){
.v =
{
{1.0f, 0.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
},
},
};
}

void draw_free(DrawContext *draw) { pl_free(draw->polyliner); }

HMM_Vec2 draw_screen_to_world(HMM_Vec2 screenPos) {
sgp_mat2x3 xform = sgp_query_state()->transform;
HMM_Vec2 draw_screen_to_world(DrawContext *draw, HMM_Vec2 screenPos) {
sgp_mat2x3 xform = draw->transform;
sgp_mat2x3 inv = mat2x3_invert(&xform);
sgp_vec2 screen = {screenPos.X, screenPos.Y};
sgp_vec2 world = mat2x3_vec2_mul(&inv, &screen);
return HMM_V2(world.x, world.y);
return mat2x3_vec2_mul(&inv, screenPos);
}

HMM_Vec2 draw_world_to_screen(DrawContext *draw, HMM_Vec2 worldPos) {
sgp_mat2x3 xform = draw->transform;
return mat2x3_vec2_mul(&xform, worldPos);
}

void draw_set_zoom(DrawContext *draw, float zoom) { draw->zoom = zoom; }
void draw_set_zoom(DrawContext *draw, float zoom) {
draw->zoom = zoom;

draw_begin_frame(draw);
draw->transform = sgp_query_state()->transform;
draw_end_frame(draw);
}

void draw_add_pan(DrawContext *draw, HMM_Vec2 pan) {
draw->pan = HMM_AddV2(draw->pan, pan);

draw_begin_frame(draw);
draw->transform = sgp_query_state()->transform;
draw_end_frame(draw);
}

HMM_Vec2 draw_get_pan(DrawContext *draw) { return draw->pan; }

float draw_get_zoom(DrawContext *draw) { return draw->zoom; }

void draw_begin_frame(DrawContext *draw) {
sgp_push_transform();
sgp_reset_transform();
sgp_translate(draw->pan.X, draw->pan.Y);
sgp_scale(draw->zoom, draw->zoom);
}

void draw_end_frame(DrawContext *draw) { sgp_pop_transform(); }

void draw_filled_rect(
DrawContext *draw, HMM_Vec2 position, HMM_Vec2 size, float radius,
HMM_Vec4 color) {
Expand Down Expand Up @@ -145,13 +175,19 @@ void draw_text(
FONScontext *fsctx = f->fsctx;

// top left corner of rect
HMM_Vec2 dot = box_top_left(rect);
HMM_Vec2 dot = draw_world_to_screen(draw, box_top_left(rect));

// position dot in bottom left corner of rect
dot.Y += rect.halfSize.Y * 2;
dot.Y += rect.halfSize.Y * draw->zoom * 2;

// already transformed, so reset the current transform
// this is done so that the text is scaled by font size rather than
// getting blurry
sgp_push_transform();
sgp_reset_transform();

fonsPushState(fsctx);
fonsSetSize(fsctx, fontSize);
fonsSetSize(fsctx, fontSize * draw->zoom);
fonsSetColor(
fsctx, fsgp_rgba(
(uint8_t)(fgColor.R * 255.0f), (uint8_t)(fgColor.G * 255.0f),
Expand All @@ -165,48 +201,32 @@ void draw_text(

fonsDrawText(fsctx, dot.X, dot.Y, text, text + len);

fonsPopState(fsctx);
}

static HMM_Vec2 panZoom(DrawContext *draw, HMM_Vec2 position) {
return HMM_AddV2(HMM_MulV2F(position, draw->zoom), draw->pan);
}
sgp_pop_transform();

static HMM_Vec2 zoom(DrawContext *draw, HMM_Vec2 size) {
return HMM_MulV2F(size, draw->zoom);
}

static Box transformBox(DrawContext *draw, Box box) {
HMM_Vec2 center = panZoom(draw, box.center);
HMM_Vec2 halfSize = zoom(draw, box.halfSize);
return (Box){.center = center, .halfSize = halfSize};
fonsPopState(fsctx);
}

void draw_chip(DrawContext *draw, Theme *theme, Box box, DrawFlags flags) {
HMM_Vec2 center = box.center;
HMM_Vec2 pos = panZoom(draw, HMM_SubV2(center, box.halfSize));
HMM_Vec2 size = zoom(draw, HMM_MulV2F(box.halfSize, 2.0f));
HMM_Vec2 pos = HMM_SubV2(center, box.halfSize);
HMM_Vec2 size = HMM_MulV2F(box.halfSize, 2.0f);

if (flags & DRAW_HOVERED) {
draw_filled_rect(
draw,
HMM_SubV2(
pos, HMM_V2(
theme->borderWidth * draw->zoom * 2.0f,
theme->borderWidth * draw->zoom * 2.0f)),
pos, HMM_V2(theme->borderWidth * 2.0f, theme->borderWidth * 2.0f)),
HMM_AddV2(
size, HMM_V2(
theme->borderWidth * draw->zoom * 4.0f,
theme->borderWidth * draw->zoom * 4.0f)),
draw->zoom * theme->componentRadius, theme->color.hovered);
size, HMM_V2(theme->borderWidth * 4.0f, theme->borderWidth * 4.0f)),
theme->componentRadius, theme->color.hovered);
}

draw_filled_rect(
draw, pos, size, draw->zoom * theme->componentRadius,
draw, pos, size, theme->componentRadius,
(flags & DRAW_SELECTED) ? theme->color.selected : theme->color.component);
draw_stroked_rect(
draw, pos, size, draw->zoom * theme->componentRadius,
draw->zoom * theme->borderWidth, theme->color.componentBorder);
draw, pos, size, theme->componentRadius, theme->borderWidth,
theme->color.componentBorder);
}

typedef struct Symbol {
Expand Down Expand Up @@ -237,8 +257,8 @@ static void draw_symbol(

const Symbol symbol = outline ? symbolOutline[shape] : symbolSolid[shape];

HMM_Vec2 center = panZoom(draw, HMM_AddV2(box.center, symbol.offset));
HMM_Vec2 hs = zoom(draw, HMM_MulV2F(box.halfSize, symbol.scale));
HMM_Vec2 center = HMM_AddV2(box.center, symbol.offset);
HMM_Vec2 hs = HMM_MulV2F(box.halfSize, symbol.scale);

Box bounds = draw_text_bounds(
draw, center, symbol.text, 1, ALIGN_CENTER, ALIGN_MIDDLE, hs.Height * 2.0f,
Expand Down Expand Up @@ -279,34 +299,30 @@ void draw_component_shape(
void draw_port(
DrawContext *draw, Theme *theme, HMM_Vec2 center, DrawFlags flags) {
float portWidth = theme->portWidth;
HMM_Vec2 portPosition = panZoom(
draw, HMM_SubV2(center, HMM_V2(portWidth / 2.0f, portWidth / 2.0f)));
HMM_Vec2 portSize = zoom(draw, HMM_V2(portWidth, portWidth));
HMM_Vec2 portPosition =
HMM_SubV2(center, HMM_V2(portWidth / 2.0f, portWidth / 2.0f));
HMM_Vec2 portSize = HMM_V2(portWidth, portWidth);

if (flags & DRAW_HOVERED) {
draw_filled_circle(
draw,
HMM_SubV2(
portPosition, HMM_V2(
theme->borderWidth * draw->zoom * 2.0f,
theme->borderWidth * draw->zoom * 2.0f)),
portPosition,
HMM_V2(theme->borderWidth * 2.0f, theme->borderWidth * 2.0f)),
HMM_AddV2(
portSize, HMM_V2(
theme->borderWidth * draw->zoom * 4.0f,
theme->borderWidth * draw->zoom * 4.0f)),
portSize, HMM_V2(theme->borderWidth * 4.0f, theme->borderWidth * 4.0f)),
theme->color.hovered);
}

draw_filled_circle(draw, portPosition, portSize, theme->color.port);
draw_stroked_circle(
draw, portPosition, portSize, draw->zoom * theme->borderWidth,
theme->color.portBorder);
draw, portPosition, portSize, theme->borderWidth, theme->color.portBorder);
}

void draw_selection_box(
DrawContext *draw, Theme *theme, Box box, DrawFlags flags) {
HMM_Vec2 pos = panZoom(draw, HMM_SubV2(box.center, box.halfSize));
HMM_Vec2 size = zoom(draw, HMM_MulV2F(box.halfSize, 2.0f));
HMM_Vec2 pos = HMM_SubV2(box.center, box.halfSize);
HMM_Vec2 size = HMM_MulV2F(box.halfSize, 2.0f);

draw_filled_rect(draw, pos, size, 0, theme->color.selectFill);
}
Expand All @@ -318,12 +334,12 @@ void draw_wire(
return;
}

HMM_Vec2 pos = panZoom(draw, verts[0]);
HMM_Vec2 pos = verts[0];

for (int i = 1; i < numVerts; i++) {
HMM_Vec2 vertex = panZoom(draw, verts[i]);
HMM_Vec2 vertex = verts[i];
draw_stroked_line(
draw, pos, vertex, draw->zoom * theme->wireThickness, theme->color.wire);
draw, pos, vertex, theme->wireThickness, theme->color.wire);
pos = vertex;
}
}
Expand All @@ -332,26 +348,21 @@ void draw_junction(
DrawContext *draw, Theme *theme, HMM_Vec2 pos, DrawFlags flags) {
float factor = flags ? 3.0f : 1.5f;

Box box = transformBox(
draw, (Box){
.center = pos,
.halfSize = HMM_V2(
theme->wireThickness * factor, theme->wireThickness * factor)});
HMM_Vec2 halfSize =
HMM_V2(theme->wireThickness * factor, theme->wireThickness * factor);

draw_filled_circle(
draw, HMM_SubV2(box.center, box.halfSize), HMM_MulV2F(box.halfSize, 2.0f),
draw, HMM_SubV2(pos, halfSize), HMM_MulV2F(halfSize, 2.0f),
(flags & DRAW_SELECTED) ? theme->color.selected : theme->color.wire);
}

void draw_label(
DrawContext *draw, Theme *theme, Box box, const char *text,
DrawLabelType type, DrawFlags flags) {

Box typeLabelBounds = transformBox(draw, box);
draw_text(
draw, typeLabelBounds, text, strlen(text),
theme->labelFontSize * draw->zoom, theme->font, theme->color.labelColor,
HMM_V4(0, 0, 0, 0));
draw, box, text, strlen(text), theme->labelFontSize, theme->font,
theme->color.labelColor, HMM_V4(0, 0, 0, 0));
}

Box draw_text_bounds(
Expand Down
2 changes: 2 additions & 0 deletions src/render/draw.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ void draw_set_zoom(DrawContext *draw, float zoom);
void draw_add_pan(DrawContext *draw, HMM_Vec2 pan);
HMM_Vec2 draw_get_pan(DrawContext *draw);
float draw_get_zoom(DrawContext *draw);
HMM_Vec2 draw_screen_to_world(DrawContext *draw, HMM_Vec2 screenPos);
HMM_Vec2 draw_world_to_screen(DrawContext *draw, HMM_Vec2 worldPos);

void draw_component_shape(
DrawContext *draw, Theme *theme, Box box, ShapeType shape, DrawFlags flags);
Expand Down
10 changes: 7 additions & 3 deletions src/render/render.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@

#include "render/fons_sgp.h"
#include "render/polyline.h"
#include "view/view.h"

#include "sokol_gfx.h"
#include "sokol_gp.h"

#include "render/draw.h"

Expand All @@ -35,12 +37,14 @@ typedef struct DrawContext {

HMM_Vec2 pan;
float zoom;

sgp_mat2x3 transform;
} DrawContext;

void draw_init(DrawContext *draw, FONScontext *fontstash);
void draw_free(DrawContext *draw);

HMM_Vec2 draw_screen_to_world(HMM_Vec2 screenPos);
void draw_begin_frame(DrawContext *draw);
void draw_end_frame(DrawContext *draw);

void draw_text(
DrawContext *draw, Box rect, const char *text, int len, float fontSize,
Expand Down
18 changes: 10 additions & 8 deletions src/ux/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ static void ux_mouse_down_state_machine(CircuitUX *ux, HMM_Vec2 worldMousePos) {
}
case STATE_PAN: {
HMM_Vec2 delta = HMM_SubV2(worldMousePos, ux->downStart);
ux->downStart = worldMousePos;
ux->downStart = HMM_SubV2(worldMousePos, delta);
draw_add_pan(ux->view.drawCtx, delta);
break;
}
Expand Down Expand Up @@ -343,16 +343,16 @@ static void ux_zoom(CircuitUX *ux) {
ux->zoomExp = MAX_ZOOM;
}
float newZoom = powf(1.1f, ux->zoomExp);
float oldZoom = draw_get_zoom(ux->view.drawCtx);
draw_set_zoom(ux->view.drawCtx, newZoom);

// figure out where the mouse was in "world coords" with the old zoom
HMM_Vec2 originalMousePos = HMM_DivV2F(
HMM_SubV2(ux->input.mousePos, draw_get_pan(ux->view.drawCtx)), oldZoom);
HMM_Vec2 originalMousePos =
draw_screen_to_world(ux->view.drawCtx, ux->input.mousePos);

draw_set_zoom(ux->view.drawCtx, newZoom);

// figure out where the mouse is in "world coords" with the new zoom
HMM_Vec2 newMousePos = HMM_DivV2F(
HMM_SubV2(ux->input.mousePos, draw_get_pan(ux->view.drawCtx)), newZoom);
HMM_Vec2 newMousePos =
draw_screen_to_world(ux->view.drawCtx, ux->input.mousePos);

// figure out the correction to the pan so that the zoom is centred on the
// mouse position
Expand All @@ -361,7 +361,7 @@ static void ux_zoom(CircuitUX *ux) {
draw_add_pan(ux->view.drawCtx, correction);
}

void ux_draw(CircuitUX *ux) {
void ux_update(CircuitUX *ux) {
float dt = (float)ux->input.frameDuration;
HMM_Vec2 panDelta = HMM_V2(0, 0);
if (bv_is_set(ux->input.keysDown, KEYCODE_W)) {
Expand Down Expand Up @@ -421,7 +421,9 @@ void ux_draw(CircuitUX *ux) {
}

ux_handle_mouse(ux);
}

void ux_draw(CircuitUX *ux) {
view_draw(&ux->view);

if (ux->debugLines) {
Expand Down
Loading

0 comments on commit e3449d9

Please sign in to comment.