-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild.zig
More file actions
160 lines (148 loc) · 7.39 KB
/
Copy pathbuild.zig
File metadata and controls
160 lines (148 loc) · 7.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// The library. `src/root.zig` is the only entry point consumers see; the
// executable below imports it by name rather than reaching into the source
// files directly, so the module wiring actually carries weight.
const zigimg = b.dependency("zigimg", .{ .target = target, .optimize = optimize });
const mod = b.addModule("implicplot", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "zigimg", .module = zigimg.module("zigimg") },
},
});
const clap = b.dependency("clap", .{ .target = target, .optimize = optimize });
const exe = b.addExecutable(.{
.name = "implicplot",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "implicplot", .module = mod },
.{ .name = "clap", .module = clap.module("clap") },
},
}),
});
b.installArtifact(exe);
const run = b.addRunArtifact(exe);
run.step.dependOn(b.getInstallStep());
if (b.args) |args| run.addArgs(args);
b.step("run", "Render a relation (zig build run -- --help)").dependOn(&run.step);
// One test binary per module: the library's tests live under `root.zig`,
// which re-exports every file, and the executable's cover argument parsing.
const test_step = b.step("test", "Run all tests");
for ([_]*std.Build.Module{ mod, exe.root_module }) |module| {
const tests = b.addTest(.{ .root_module = module });
test_step.dependOn(&b.addRunArtifact(tests).step);
}
addExamples(b, exe);
addTypstPlugin(b);
}
/// `zig build examples`: every image the README embeds - the gallery in
/// example/, and the package cover in typst/thumbnail.png. The invocation
/// picks the algorithm: equalities are traced by `contour` into SVG
/// polylines (`-t`), inequalities plotted by `plot` into rasters - and so is
/// the cover, whose step relation has a solution set with area, no curve.
fn addExamples(b: *std.Build, exe: *std.Build.Step.Compile) void {
const step = b.step("examples", "Draw every image the README embeds (use -Doptimize=ReleaseFast)");
const gallery = [_]struct {
args: []const []const u8,
relation: []const u8,
}{
// rings factors into two ellipse families - 2 cos(u) sin(v) = 0 -
// so its singular crossings are real junctions: join, not avoid.
.{ .args = &.{ "-t", "-s", "400x400", "-u", "join", "-c", "#e5484d", "-o", "rings.svg" }, .relation = "sin(x^2 + y^2) = cos(x*y)" },
.{ .args = &.{ "-t", "-s", "400x400", "-c", "#3e7bd6", "-o", "hyperbolas.svg" }, .relation = "x*y*sin(x*y)*cos(x*y) = x + y" },
// cells' singular points (x or y at 0, the other at sqrt(pi/2 + 2k pi))
// expand to x^2 = 2 y0^2 e^2: two transversal lines - real crossings.
.{ .args = &.{ "-t", "-s", "400x400", "-u", "join", "-c", "#46a758", "-o", "cells.svg" }, .relation = "sin(x^2) + sin(y^2) = 1" },
.{ .args = &.{ "-s", "700x700", "-b", "#ffffff", "-c", "#f76b15", "-o", "halftone.png" }, .relation = "sin(x^2 + y^2) + sin(x*y) > 0.3" },
.{ .args = &.{ "-s", "700x700", "-b", "#ffffff", "-c", "#6e56cf", "-x", "-5.5:5.5", "-y", "-5.5:5.5", "-o", "fingerprint.png" }, .relation = "sin(x^2 + y^2 - 2*atan(y / (sqrt(x^2 + y^2) + x)) + sin(x*y) / 2) < 0" },
.{ .args = &.{ "-s", "700x700", "-b", "#ffffff", "-c", "#000000", "-o", "checker.png" }, .relation = "sin(x^2) * sin(y^2) > 0" },
};
for (gallery) |image| {
const run = b.addRunArtifact(exe);
run.setCwd(b.path("example"));
run.addArgs(image.args);
run.addArg(image.relation);
step.dependOn(&run.step);
}
// The cover: five arms of the discrete logarithmic spiral, one relation
// per colour (typst/thumbnail.typ is its pure-Typst twin).
const cover = b.addRunArtifact(exe);
cover.setCwd(b.path("typst"));
cover.addArgs(&.{ "-s", "3200x3200", "-x", "-6.3:6.3", "-y", "-6.3:6.3", "-d", "4", "-b", "#1e1e26", "-o", "thumbnail.png" });
const arms = [_]struct { k: u8, tint: []const u8 }{
.{ .k = 0, .tint = "#e5484d" },
.{ .k = 3, .tint = "#f0b100" },
.{ .k = 6, .tint = "#46a758" },
.{ .k = 9, .tint = "#3e7bd6" },
.{ .k = 12, .tint = "#9d5bd2" },
};
for (arms) |arm| cover.addArgs(&.{ "-c", arm.tint });
for (arms) |arm| cover.addArg(b.fmt(
"(floor(15/(2*pi) * log(x^2 + y^2) / 2) - floor(15/pi * atan(y / (sqrt(x^2 + y^2) + x))) - {d}) % 15 = 0",
.{arm.k},
));
step.dependOn(&cover.step);
}
/// A Typst plugin, built for `wasm32-freestanding` against the same library.
///
/// It is a second target rather than a second package: a module carries its
/// target, so the library source is instantiated once more here, but nothing
/// else is duplicated - no second `build.zig.zon`, no path dependency, and the
/// wire format lives in `src/wire.zig`, where the host test suite above pins
/// it with golden bytes.
fn addTypstPlugin(b: *std.Build) void {
// Deliberately WITHOUT simd128, although Typst 0.14+ supports it: Typst
// executes plugins in an interpreter (wasmi), where every v128 instruction
// is software-emulated and costs more to dispatch than the scalar ops it
// replaces. Measured on the same wasm and the same call, the SIMD build
// takes 1.37x as long under Typst and the same time under a JIT (V8:
// 70ms either way, at native speed - so the vector code is only ballast
// in the one runtime this artifact is built for).
const target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
.cpu_model = .{ .explicit = &std.Target.wasm.cpu.lime1 },
.cpu_features_add = std.Target.wasm.featureSet(&.{ .bulk_memory, .reference_types }),
});
// The host default (Debug) produces a plugin too slow and too large to be
// useful, and plotting is compute-bound, so this gets its own knob.
const optimize = b.option(
std.builtin.OptimizeMode,
"wasm-optimize",
"Optimize mode for the Typst plugin (default ReleaseFast)",
) orelse .ReleaseFast;
const zigimg = b.dependency("zigimg", .{ .target = target, .optimize = optimize });
const lib = b.createModule(.{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "zigimg", .module = zigimg.module("zigimg") },
},
});
const plugin = b.addExecutable(.{
.name = "implicplot",
.root_module = b.createModule(.{
.root_source_file = b.path("src/typst_plugin.zig"),
.target = target,
.optimize = optimize,
.imports = &.{.{ .name = "implicplot", .module = lib }},
.strip = true,
.omit_frame_pointer = true,
.unwind_tables = .none,
}),
});
plugin.entry = .disabled;
plugin.rdynamic = true;
const install = b.addInstallArtifact(plugin, .{
.dest_dir = .{ .override = .{ .custom = "../typst" } },
});
b.step("wasm", "Build the Typst plugin into typst/").dependOn(&install.step);
}