Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@tsconfig/recommended": "^1.0.8",
"@types/jest": "^29.5.14",
"@types/node": "^22.13.9",
"@types/svg-parser": "^2.0.6",
"@webgpu/types": "^0.1.54",
"eslint": "^9.28.0",
"html-webpack-plugin": "^5.6.3",
Expand Down Expand Up @@ -70,5 +71,8 @@
"homepage": "https://github.com/mateuszJS/magic-render#readme",
"files": [
"lib/**/*"
]
],
"dependencies": {
"svg-parser": "^2.0.4"
}
}
10 changes: 5 additions & 5 deletions src/WebGPU/programs/drawShape/shader.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -294,13 +294,13 @@ fn evaluate_shape(point: vec2f) -> ShapeInfo {
var total_crossings: i32 = 0;

// For each curve, find closest point and count ray crossings
let num_curves = arrayLength(&curves) / 3;
let num_curves = arrayLength(&curves) / 4;
for (var i = 0u; i < num_curves; i++) {
let curve = CubicBezier(
curves[i * 3 + 0],
curves[i * 3 + 1],
curves[i * 3 + 2],
curves[i * 3 + 3]
curves[i * 4 + 0],
curves[i * 4 + 1],
curves[i * 4 + 2],
curves[i * 4 + 3]
);

// Check if this is a straight line (handle points have x >= STRAIGHT_LINE_THRESHOLD)
Expand Down
9 changes: 2 additions & 7 deletions src/logic/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,6 @@ declare module '*.zig' {
export const remove_asset: () => void
export const reset_assets: (assets: ZigAssetInput[], with_snapshot: boolean) => void

export const init_svg_textures: (
texture_max_size: number,
resize_texture: (texture_id: number, width: number, height: number) => void
) => void

export const add_svg_texture: (texture_id: number, width: number, height: number) => void

export const on_update_pick: (id: number) => void
export const on_pointer_down: (x: number, y: number) => void
export const on_pointer_up: () => void
Expand Down Expand Up @@ -86,4 +79,6 @@ declare module '*.zig' {
export const set_tool: (tool: number) => void

export const import_icons: (data: number[]) => void

export const add_shape: (lines: Array<Array<[Point, Point]>>) => void
}
91 changes: 34 additions & 57 deletions src/logic/index.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ const Triangle = @import("triangle.zig");
const TransformUI = @import("./transform_ui.zig");
const zigar = @import("zigar");
const Msdf = @import("./msdf.zig");
const SvgTextures = @import("./svg_textures.zig");
const shapes = @import("./shapes/shapes.zig");
const squares = @import("squares.zig");
const bounding_box = @import("shapes/bounding_box.zig");
const shared = @import("./shared.zig");

const WebGpuPrograms = struct {
draw_texture: *const fn (images.DrawVertex, u32) void,
Expand Down Expand Up @@ -83,7 +83,6 @@ const State = struct {
action: ActionType,
tool: Tool,
last_pointer_coords: Types.Point,
render_scale: f32,
};

var state = State{
Expand All @@ -95,7 +94,6 @@ var state = State{
.action = ActionType.None,
.tool = Tool.None,
.last_pointer_coords = Types.Point{ .x = 0.0, .y = 0.0 },
.render_scale = 1.0,
};

pub fn init_state(allocator: std.mem.Allocator, width: f32, height: f32, texture_max_size: f32) void {
Expand All @@ -106,33 +104,9 @@ pub fn init_state(allocator: std.mem.Allocator, width: f32, height: f32, texture
shapes.max_texture_size = texture_max_size;
}

pub fn init_svg_textures(texture_max_size: f32, resize_texture: *const fn (u32, f32, f32) void) void {
SvgTextures.init(texture_max_size, resize_texture);
}

pub fn add_svg_texture(texture_id: u32, width: f32, height: f32) void {
SvgTextures.add_texture(texture_id, width, height);

// When loading SVG, firstly assets will be added with their texture ID and later texture will load(so we know its svg)
// and now we have to make sure SVG texture is big enough to match quality of each of the assets.
var iterator = state.assets.iterator();
while (iterator.next()) |asset| {
switch (asset.value_ptr.*) {
.img => |img| {
if (img.texture_id == texture_id) {
SvgTextures.ensure_svg_texture_quality(img);
}
},
.shape => {},
}
}
}

pub fn update_render_scale(scale: f32) !void {
state.render_scale = scale;
shapes.render_scale = scale;
shared.render_scale = scale;

SvgTextures.update_render_scale(scale);
var iterator = state.assets.iterator();

var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
Expand Down Expand Up @@ -160,14 +134,11 @@ fn generate_id() u32 {

pub fn add_asset(id_or_zero: u32, points: [4]Types.PointUV, texture_id: u32) !void {
const id = if (id_or_zero == 0) generate_id() else id_or_zero;
const img = images.Image.new(id, points, texture_id);

const asset = Asset{
.img = img,
.img = images.Image.new(id, points, texture_id),
};
try state.assets.put(id, asset);

SvgTextures.ensure_svg_texture_quality(img);
try check_assets_update(true);
}

Expand Down Expand Up @@ -250,19 +221,18 @@ pub fn on_pointer_down(_allocator: std.mem.Allocator, x: f32, y: f32) !void {
const point = Types.Point{ .x = x, .y = y };

if (get_selected_shape()) |shape| {
if (!shape.is_closed) {
shape.setPreviewPoint(point);
const is_completed = try shape.add_point_start();
if (is_completed) {
// Shape is completed, we can finalize it
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();

try shape.completeShape(allocator);
}
return;
}
shape.setPreviewPoint(point);
try shape.addPointStart();
// const is_completed = try shape.addPointStart();
// if (is_completed) {
// // Shape is completed, we can finalize it
// var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
// defer arena.deinit();
// const allocator = arena.allocator();

// try shape.completeShape(allocator);
// }
return;
}

const id = generate_id();
Expand Down Expand Up @@ -336,7 +306,6 @@ pub fn on_pointer_move(x: f32, y: f32) void {
.Transform => {
const points_ptr: *[4]Types.PointUV = &img.points;
TransformUI.tranform_points(state.hovered_asset_id, points_ptr, x, y);
SvgTextures.ensure_svg_texture_quality(img.*);
},
.None => {},
}
Expand All @@ -356,7 +325,7 @@ pub fn commitChanges() !void {
defer arena.deinit();
const allocator = arena.allocator();

try shape.completeShape(allocator);
try shape.complete(allocator);
}
}
}
Expand All @@ -378,9 +347,9 @@ fn get_border(allocator: std.mem.Allocator) struct { []Triangle.DrawInstance, []
buffer[0..2],
point,
next_point,
10.0 * state.render_scale,
10.0 * shared.render_scale,
red,
5.0 * state.render_scale,
5.0 * shared.render_scale,
);

triangle_vertex_data.appendSlice(&buffer) catch unreachable;
Expand All @@ -403,9 +372,9 @@ fn get_border(allocator: std.mem.Allocator) struct { []Triangle.DrawInstance, []
buffer[0..2],
point,
next_point,
10.0 * state.render_scale,
10.0 * shared.render_scale,
green,
5.0 * state.render_scale,
5.0 * shared.render_scale,
);
triangle_vertex_data.appendSlice(&buffer) catch unreachable;
}
Expand All @@ -418,7 +387,6 @@ fn get_border(allocator: std.mem.Allocator) struct { []Triangle.DrawInstance, []
&msdf_buffer,
img,
state.hovered_asset_id,
state.render_scale,
);

triangle_vertex_data.appendSlice(&triangle_buffer) catch unreachable;
Expand Down Expand Up @@ -467,7 +435,7 @@ fn draw_project_boundary() void {
buffer[i * 2 ..][0..2],
point,
next_point,
2.0 * state.render_scale,
2.0 * shared.render_scale,
color,
0.0,
);
Expand All @@ -480,7 +448,7 @@ const point_size: f32 = @floatFromInt(@sizeOf(Types.Point)); // 8 bytes
const triangle_size: f32 = @floatFromInt(@sizeOf(Triangle.DrawInstance)); // 64 bytes
const asset_size: f32 = @floatFromInt(@sizeOf(images.DrawVertex)); // 96 bytes

pub fn render_draw() void {
pub fn render_draw() !void {
// Add some padding for allocator overhead (usually ~16-32 bytes per allocation)
// const allocator_overhead = 64;

Expand Down Expand Up @@ -527,8 +495,10 @@ pub fn render_draw() void {
};
web_gpu_programs.draw_texture(vertex_data, texture_id);
} else {
const vertex_data = shape.get_draw_vertex_data(allocator) catch unreachable;
web_gpu_programs.draw_shape(vertex_data.curves, &vertex_data.bounding_box, vertex_data.uniform);
const option_vertex_data = try shape.get_draw_vertex_data(allocator);
if (option_vertex_data) |vertex_data| {
web_gpu_programs.draw_shape(vertex_data.curves, &vertex_data.bounding_box, vertex_data.uniform);
}
}
},
}
Expand Down Expand Up @@ -601,7 +571,7 @@ pub fn render_pick() void {
switch (asset) {
.img => |img| {
var vertex_buffer: [TransformUI.PICK_TRIANGLE_INSTANCES]Triangle.PickInstance = undefined;
TransformUI.get_pick_vertex_data(vertex_buffer[0..TransformUI.PICK_TRIANGLE_INSTANCES], img, state.render_scale);
TransformUI.get_pick_vertex_data(vertex_buffer[0..TransformUI.PICK_TRIANGLE_INSTANCES], img);
web_gpu_programs.pick_triangle(vertex_buffer[0..TransformUI.PICK_TRIANGLE_INSTANCES]);
},
.shape => {},
Expand Down Expand Up @@ -655,6 +625,13 @@ pub fn stop_drawing_shape() void {
state.selected_asset_id = 0;
}

pub fn add_shape(lines: []const []const [2]Types.Point) !void {
const id = generate_id();
const shape = try shapes.Shape.new_from_points(id, lines, std.heap.page_allocator);
try state.assets.put(id, Asset{ .shape = shape });
state.selected_asset_id = id;
}

test "reset_assets does not call the real update callback" {
// Setup initial state
init_state(100, 100);
Expand Down
10 changes: 5 additions & 5 deletions src/logic/shapes/bounding_box.zig
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,14 @@ pub fn getBoundingBox(curves: []const Point, padding: f32) BoundingBox {
.max_y = -std.math.inf(f32),
};

const num_cubic_curves = curves.len / 3;
const num_cubic_curves = curves.len / 4;

var i: usize = 0;
while (i < num_cubic_curves) : (i += 1) {
const p0 = curves[i * 3 + 0];
const p1 = curves[i * 3 + 1];
const p2 = curves[i * 3 + 2];
const p3 = curves[i * 3 + 3];
const p0 = curves[i * 4 + 0];
const p1 = curves[i * 4 + 1];
const p2 = curves[i * 4 + 2];
const p3 = curves[i * 4 + 3];

// Calculate real bounding box for this cubic Bézier curve
const bounds = calculateCubicBezierRealBounds(p0, p1, p2, p3);
Expand Down
Loading