Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
build/
*.pem
3 changes: 3 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["biomejs.biome", "ziglang.vscode-zig"]
}
13 changes: 13 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[react]": {
"editor.defaultFormatter": "biomejs.biome"
}
}
23 changes: 23 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": false
},
"files": {
"ignoreUnknown": false,
"ignore": ["**/alpine-3.10.4.js", "**/path-data-polyfill.js"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"lineWidth": 120,
"indentWidth": 4
},
"linter": {
"enabled": true,
"rules": { "recommended": true, "complexity": { "useLiteralKeys": "info" } }
},
"javascript": { "formatter": { "quoteStyle": "double" } }
}
4 changes: 2 additions & 2 deletions native/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
zig-cache/
zig-out/
*zig-cache/
*zig-out/
*.o
*.a
*.dylib
Expand Down
26 changes: 21 additions & 5 deletions native/src/gingerbread.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const wasm = @import("wasm.zig");

const a = wasm.allocator;

fn trace(allocator: std.mem.Allocator, layer_name: []const u8, scale_factor: f64, image_pixels: [*]u8, image_width: usize, image_height: usize, writer: anytype) !void {
fn trace(allocator: std.mem.Allocator, layer_name: []const u8, scale_factor: f64, image_pixels: [*]u8, image_width: usize, image_height: usize, writer: anytype, width_mm: f64) !void {
var bitmap = try potrace.Bitmap.from_image(allocator, .{
.pixels = image_pixels,
.w = image_width,
Expand Down Expand Up @@ -39,7 +39,7 @@ fn trace(allocator: std.mem.Allocator, layer_name: []const u8, scale_factor: f64

print("Polylist fractured\n", .{});

try pcb.polylist_to_footprint(polylist, layer_name, scale_factor, writer);
try pcb.polylist_to_footprint(polylist, layer_name, scale_factor, writer, width_mm);
}

test "trace" {
Expand Down Expand Up @@ -70,7 +70,7 @@ export fn conversion_start() void {
pcb.start_pcb(conversion_buffer.?.writer()) catch @panic("memory");
}

export fn conversion_add_raster_layer(layer: u32, scale_factor: f64, image_pixels: [*]u8, image_width: u32, image_height: u32) void {
export fn conversion_add_raster_layer(layer: u32, scale_factor: f64, image_pixels: [*]u8, image_width: u32, image_height: u32, width_mm: f64) void {
const layer_name = switch (layer) {
1 => "F.Cu",
2 => "B.Cu",
Expand All @@ -81,7 +81,7 @@ export fn conversion_add_raster_layer(layer: u32, scale_factor: f64, image_pixel
else => "Unknown",
};

trace(a, layer_name, scale_factor, image_pixels, image_width, image_height, conversion_buffer.?.writer()) catch @panic("memory");
trace(a, layer_name, scale_factor, image_pixels, image_width, image_height, conversion_buffer.?.writer(), width_mm) catch @panic("memory");
}

export fn conversion_start_poly() void {
Expand All @@ -91,9 +91,21 @@ export fn conversion_start_poly() void {
export fn conversion_add_poly_point(
x: f64,
y: f64,
layer_number: u32,
scale_factor: f64,
width_mm: f64,
) void {
pcb.add_xx_poly_point(.{ .x = x, .y = y }, scale_factor, conversion_buffer.?.writer()) catch @panic("memory");
const layer_name = switch (layer_number) {
1 => "F.Cu",
2 => "B.Cu",
3 => "F.SilkS",
4 => "B.SilkS",
5 => "F.Mask",
6 => "B.Mask",
else => "Unknown",
};

pcb.add_xx_poly_point(.{ .x = x, .y = y }, layer_name, scale_factor, conversion_buffer.?.writer(), width_mm) catch @panic("memory");
}

export fn conversion_end_poly(layer: u32, width: f32, fill: bool) void {
Expand All @@ -115,3 +127,7 @@ export fn conversion_finish() wasm.StringResult {
pcb.end_pcb(&conversion_buffer.?.writer()) catch @panic("memory");
return wasm.return_string(conversion_buffer.?.toOwnedSlice() catch @panic("memory"));
}

export fn set_mirror_back_layers(val: bool) void {
pcb.mirror_back_layers = val;
}
34 changes: 24 additions & 10 deletions native/src/pcb.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ const Poly = geometry.Poly;
const PolyList = geometry.PolyList;
const FauxUUID = @import("fauxuuid.zig").FauxUUID;

fn is_back_layer(layer: []const u8) bool {
return std.ascii.startsWithIgnoreCase(layer, "B.");
}

pub fn start_pcb(writer: anytype) !void {
try writer.writeAll("(kicad_pcb (version 20211014) (generator pcbnew)\n");
try writer.writeAll("(layers\n");
Expand All @@ -29,31 +33,41 @@ pub fn start_xx_poly(kind: []const u8, writer: anytype) !void {
try writer.print(" ({s}_poly\n", .{kind});
try writer.writeAll(" (pts\n");
}
pub var mirror_back_layers: bool = true;

pub fn add_xx_poly_point(pt: geometry.Point, layer_name: []const u8, scale_factor: f64, writer: anytype, width_mm: f64) !void {
const scaled_x = pt.x * scale_factor;
const scaled_y = pt.y * scale_factor;

// For back layers, mirror around y=0 then translate back
const final_x = if (is_back_layer(layer_name) and mirror_back_layers)
-scaled_x + width_mm
else
scaled_x;

pub fn add_xx_poly_point(pt: geometry.Point, scale_factor: f64, writer: anytype) !void {
try writer.print(" (xy {d:.3} {d:.3})\n", .{ pt.x * scale_factor, pt.y * scale_factor });
try writer.print(" (xy {d:.3} {d:.3})\n", .{ final_x, scaled_y });
}

pub fn end_xx_poly(layer: []const u8, width: f64, fill: bool, writer: anytype) !void {
pub fn end_xx_poly(layer_name: []const u8, line_width: f64, fill: bool, writer: anytype) !void {
try writer.writeAll(" )\n");
try writer.print(" (layer \"{s}\")\n", .{layer});
try writer.print(" (width {d:.3})\n", .{width});
try writer.print(" (layer \"{s}\")\n", .{layer_name});
try writer.print(" (width {d:.3})\n", .{line_width});
try writer.print(" (fill {s})\n", .{if (fill) "solid" else "none"});
try writer.print(" (tstamp \"{s}\")\n", .{FauxUUID.init()});
try writer.writeAll(" )\n");
}

pub fn points_to_xx_poly(kind: []const u8, pts: []geometry.Point, scale_factor: f64, layer: []const u8, width: f64, fill: bool, writer: anytype) !void {
pub fn points_to_xx_poly(kind: []const u8, pts: []geometry.Point, scale_factor: f64, layer_name: []const u8, line_width: f64, fill: bool, writer: anytype, width_mm: f64) !void {
try start_xx_poly(kind, writer);

for (pts) |pt| {
try add_xx_poly_point(pt, scale_factor, writer);
try add_xx_poly_point(pt, layer_name, scale_factor, writer, width_mm);
}

try end_xx_poly(layer, width, fill, writer);
try end_xx_poly(layer_name, line_width, fill, writer);
}

pub fn polylist_to_footprint(polylist: PolyList, layer: []const u8, scale_factor: f64, writer: anytype) !void {
pub fn polylist_to_footprint(polylist: PolyList, layer: []const u8, scale_factor: f64, writer: anytype, width_mm: f64) !void {
try writer.writeAll("(footprint \"Graphics\"\n");
try writer.print(" (layer \"{s}\")\n", .{layer});
try writer.writeAll(" (at 0 0)\n");
Expand All @@ -62,7 +76,7 @@ pub fn polylist_to_footprint(polylist: PolyList, layer: []const u8, scale_factor
try writer.print(" (tedit \"{s}\")\n", .{FauxUUID.init()});

for (polylist.items) |poly| {
try points_to_xx_poly("fp", poly.outline, scale_factor, layer, 0, true, writer);
try points_to_xx_poly("fp", poly.outline, scale_factor, layer, 0, true, writer, width_mm);
}

try writer.writeAll(")\n");
Expand Down
5 changes: 5 additions & 0 deletions web/help.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ <h2>Drills</h2>
Affinity and KiCAD.
</p>

<h2>Mirror back layers</h2>
<p>Gingerbread allows you to mirror the <code>B.SilkS</code>, <code>B.Mask</code>, and <code>B.Cu</code> layers so that the it is correctly mirrored in the output. This can be switched
off if you don't want this behavior.</p>


<h2>Exporting your design</h2>
<p>When exporting you design to an SVG for Gingerbread, click the <strong>More</strong> button and setup the export parameters as shown below so that "Rasterize" is set to
"Nothing", "Export text as curves" is checked, and "Flatten transforms" is checked.</p>
Expand Down
7 changes: 7 additions & 0 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@
</div>

<label class="label">Layers</label>
<div class="field">
<label class="checkbox">
<input type="checkbox" x-model="design.mirror_back_layers">
Mirror back layers
</label>
</div>

<ul class="layer-list">
<template x-for="layer in layers">
<li>
Expand Down
47 changes: 33 additions & 14 deletions web/scripts/libgingerbread.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,49 @@
import { ZigWASM } from "./zigwasm.js";

export class LibGingerbread {
static wasm_src = "./native/gingerbread.wasm";
static wasm_module;
static #wasm_src = "./native/gingerbread.wasm";
static #wasm_module = null;

constructor(zig) {
this.zig = zig;
}

static async new() {
if (this.wasm_module == null) {
this.wasm_module = await ZigWASM.compile(this.wasm_src);
if (!LibGingerbread.#wasm_module) {
LibGingerbread.#wasm_module = await ZigWASM.compile(LibGingerbread.#wasm_src);
}

return new this(await ZigWASM.new(this.wasm_module));
return new LibGingerbread(await ZigWASM.new(LibGingerbread.#wasm_module));
}

conversion_start() {
this.zig.exports.conversion_start();
}

conversion_add_raster_layer(layer, scale, image) {
conversion_add_raster_layer(layer, scale, imageData, width_mm) {
if (!this.image_array_ptr) {
this.image_array_ptr = this.zig.allocate(image.data.byteLength);
this.image_array_ptr = this.zig.allocate(imageData.data.byteLength);
}

this.image_array_ptr.u8().set(image.data);
this.image_array_ptr.u8().set(imageData.data);

this.zig.exports.conversion_add_raster_layer(layer, scale, this.image_array_ptr.address, image.width, image.height);
try {
this.zig.exports.conversion_add_raster_layer(
layer,
scale,
this.image_array_ptr.address,
imageData.width,
imageData.height,
width_mm,
);
} catch (error) {
console.log("===================conversion_add_raster_layer error============================");
console.log("layer:", layer, "scale:", scale, "width:", imageData.width, "height:", imageData.height);
console.log("imageData:", imageData);
console.log("image_array_ptr:", this.image_array_ptr);
console.error("WASM error in conversion_add_raster_layer:", error);
console.log("================================================");
throw error;
}
}

conversion_finish() {
Expand All @@ -39,16 +55,19 @@ export class LibGingerbread {
this.zig.exports.conversion_start_poly();
}

conversion_add_poly_point(x, y, scale_factor) {
this.zig.exports.conversion_add_poly_point(x, y, scale_factor);
conversion_add_poly_point(x, y, layer_number, scale_factor, width_mm) {
this.zig.exports.conversion_add_poly_point(x, y, layer_number, scale_factor, width_mm);
}

conversion_end_poly(layer, width, fill) {
this.zig.exports.conversion_end_poly(layer, width, fill);
conversion_end_poly(layer, line_width, fill) {
this.zig.exports.conversion_end_poly(layer, line_width, fill);
}

conversion_add_drill(x, y, d, scale_factor) {
this.zig.exports.conversion_add_drill(x, y, d, scale_factor);
}

set_mirror_back_layers(val) {
this.zig.exports.set_mirror_back_layers(val);
}
}
Loading