Compare commits

...

10 Commits

Author SHA1 Message Date
27764ef23a push 2024-08-23 01:12:29 +08:00
e8f3b2bc47 pusher 2024-08-22 18:40:13 +08:00
f25ed801f5 pusher 2024-08-21 22:00:45 +08:00
480888e8fc pusher 2024-08-21 00:03:35 +08:00
4204f444d6 pusher 2024-08-16 21:36:48 +08:00
9aab1b5304 ppi module 2024-08-16 16:30:06 +08:00
91f67b395f rebuild factor 2024-08-14 17:05:06 +08:00
5a28820b5e rebuild 2024-07-25 11:58:15 +08:00
2638243856 sync 2024-07-22 23:37:14 +08:00
57bbbfe97f check 2024-07-22 21:55:59 +08:00
77 changed files with 9300 additions and 1004 deletions

19
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug",
"program": "${workspaceFolder}/target/debug/radar-g",
"args": [],
"cwd": "${workspaceFolder}",
"env": {
"RUST_LOG": "info"
}
}
]
}

1154
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,37 +1,69 @@
[package]
edition = "2021"
name = "radar-g"
version = "0.1.0"
edition = "2021"
build = "build.rs"
[[bin]]
name = "radar-gi"
path = "src/main.rs"
windows = true
[dependencies]
imgui = "0.12.0"
aligned-vec = "0.6.0"
anyhow = "1.0.86"
bytemuck = {version = "1.16.1", features = ["derive"]}
byteorder = "1.5.0"
cgmath = "0.18.0"
chrono = "0.4.38"
copypasta = "0.10.1"
dirs = "5.0.1"
env_logger = "0.11.3"
flate2 = "1.0.30"
# font-kit = {version = "0.14.1"}
freetype-rs = {version="0.37.0",features = ["bundled"]}
geo = "0.28.0"
glow = "0.13.1"
imgui-glow-renderer = "0.12.0"
imgui-winit-support = "0.12.0"
glutin = "0.31.3"
glutin-winit = "0.4.2"
copypasta = "0.10.1"
raw-window-handle = "0.5.2"
winit = "0.29.15"
cgmath = "0.18.0"
nalgebra-glm = "0.18.0"
regex = "1.10.5"
once_cell = "1.19.0"
image = "0.25.2"
imgui = {version="0.12.0",features = ["tables-api", "freetype"]}
imgui-glow-renderer = "0.12.0"
imgui-winit-support = "0.12.0"
include_dir = "0.7.4"
nom = "7.1.3"
thiserror = "1.0.61"
log = "0.4.22"
env_logger = "0.11.3"
bytemuck = { version = "1.16.1", features = ["derive"] }
lru = "0.12.4"
nalgebra = "0.33.0"
nom-derive = "0.10.1"
serde = { version = "1.0.204", features = ["derive"] }
byteorder = "1.5.0"
chrono = "0.4.38"
flate2 = "1.0.30"
anyhow = "1.0.86"
serde_json = "1.0.120"
geo = "0.28.0"
nalgebra-glm = "0.18.0"
ndarray = "0.15.6"
aligned-vec = "0.6.0"
nom = "7.1.3"
nom-derive = "0.10.1"
once_cell = "1.19.0"
pathfinder_geometry = "0.5.1"
raw-window-handle = "0.5.2"
regex = "1.10.5"
serde = {version = "1.0.204", features = ["derive"]}
serde_json = "1.0.120"
thiserror = "1.0.61"
winit = "0.29.3"
tinyfiledialogs = "3.0"
tracker = "0.2.2"
glsl = "7.0.0"
glsl-quasiquote = "7.0.0"
toml = "0.8.19"
femtovg = "0.9.2"
rust-embed = "8.5.0"
tempfile = "3.12.0"
[target.'cfg(windows)'.build-dependencies]
windres = "0.2.2"
[features]
default = ["sdf_font"]
sdfer_use_f64_instead_of_f32 = []
normal_font = []
sdf_font = []
inparser = []
femtovg = []

1
app-icon.rc Normal file
View File

@ -0,0 +1 @@
IDI_ICON1 ICON "resources/radar-gi.ico"

16
build.rs Normal file
View File

@ -0,0 +1,16 @@
extern crate windres;
#[cfg(windows)]
use windres::Build;
fn main() {
#[cfg(windows)]
{
println!("cargo:rustc-link-arg=/SUBSYSTEM:WINDOWS");
println!("cargo:rustc-link-arg=/ENTRY:mainCRTStartup");
Build::new().compile("app-icon.rc").unwrap();
}
}

BIN
resources/radar-gi.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

30
resources/radar.toml Normal file
View File

@ -0,0 +1,30 @@
[[cmap]]
type="DBZ"
levels = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75]
colors = ["#aaaaaa", "#0022ff", "#01a0f6", "#00ecec", "#00d800", "#019000", "#ffff00", "#e7c000", "#ff9000", "#ff0000", "#d60000",
"#c00000", "#ff00f0", "#9600b4", "#ad90f0"]
[[cmap]]
type="VEL"
levels = [-90,-45, -35, -27, -20, -15, -10, -5, -1, 0, 1, 5, 10, 15, 20, 27, 1000]
colors = ["#9fffff", "#00e0ff", "#0080ff", "#320096", "#00fb90", "#00bb90", "#008f00", "#cdc09f","#000000", "#f88700", "#ffcf00", "#ffff00", "#ae0000", "#d07000", "#dd0000", "#ff0000"]
[[cmap]]
type="SW"
colors=["#E0E0E0", "#7CE0E0", "#00E0E0", "#00B0B0", "#00FEFE", "#00C400", "#008000", "#FEFE00", "#FED200", "#FE7C00", "#FEB0B0", "#FE5858", "#FE0000", "#FEFEFE"]
levels=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
[[cmap]]
type="CC"
colors=["#003CFF", "#00EFEF", "#00BABF", "#00837D", "#008938", "#00B729", "#00DA0D", "#00FF00", "#FFFF3B", "#FFF000", "#FFC600", "#FFA500", "#FF7200", "#FF1F00", "#C10000", "#D400AA"]
levels=[0, 0.1, 0.3, 0.5, 0.6, 0.7, 0.8, 0.85, 0.9, 0.92, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99]
[[cmap]]
type="KDP"
colors=["#00FFFF", "#00EFEF", "#00A8AC", "#B4B4B4", "#B4B4B4", "#00C027", "#00E80A", "#24FF24", "#FFFF1E", "#FFE600", "#FFBC00", "#FF9800", "#FF5E00", "#F20F00", "#BB003A", "#DD009C", "#FF00FF"]
levels=[-0.8, -0.4, -0.2, -0.1, 0.1, 0.15, 0.22, 0.33, 0.5, 0.75, 1.1, 1.7, 2.4, 3.1, 7, 20]
[[cmap]]
type="ZDR"
colors=["#464646", "#505050", "#5A5A5A", "#646464", "#6E6E6E", "#787878", "#828282", "#8C8C8C", "#969696", "#AFAFAF", "#C8C8C8", "#DCF0DC", "#00C027", "#00E80A", "#24FF24", "#FFFF1E", "#FFF20F", "#FFE600", "#FFBC00", "#FF9800", "#FF5E00", "#FFFF00", "#F20F00", "#BB003A", "#DD009C", "#FF00FF"]
levels=[-5, -4.5, -4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7]

View File

@ -21,4 +21,5 @@ void main()
if (v_color.a == 0) { discard; }
FragColor = stroke(v_distance, v_linewidth, v_antialias, v_color);
// FragColor = v_color;
}

View File

@ -33,47 +33,67 @@ void main ()
// This function is externally generated
// fetch_uniforms();
float id = gl_VertexID;
v_linewidth = linewidth;
v_antialias = antialias;
v_color = color;
// transform prev/curr/next
vec4 prev_ = <transform(prev)>;
vec4 curr_ = <transform(curr)>;
vec4 next_ = <transform(next)>;
// vec4 prev_ = <transform(prev)>;
// vec4 curr_ = <transform(curr)>;
// vec4 next_ = <transform(next)>;
// prev/curr/next in viewport coordinates
vec2 _prev = NDC_to_viewport(prev_, <viewport.viewport_global>.zw);
vec2 _curr = NDC_to_viewport(curr_, <viewport.viewport_global>.zw);
vec2 _next = NDC_to_viewport(next_, <viewport.viewport_global>.zw);
// vec2 _prev = NDC_to_viewport(prev_, <viewport.viewport_global>.zw);
// vec2 _curr = NDC_to_viewport(curr_, <viewport.viewport_global>.zw);
// vec2 _next = NDC_to_viewport(next_, <viewport.viewport_global>.zw);
// Compute vertex final position (in viewport coordinates)
float w = linewidth/2.0 + 1.5*antialias;
float z;
vec2 P;
// if( curr == prev) {
// vec2 v = normalize(_next.xy - _curr.xy);
// vec2 normal = normalize(vec2(-v.y,v.x));
// P = _curr.xy + normal*w*id;
// } else if (curr == next) {
// vec2 v = normalize(_curr.xy - _prev.xy);
// vec2 normal = normalize(vec2(-v.y,v.x));
// P = _curr.xy + normal*w*id;
// } else {
// vec2 v0 = normalize(_curr.xy - _prev.xy);
// vec2 v1 = normalize(_next.xy - _curr.xy);
// vec2 normal = normalize(vec2(-v0.y,v0.x));
// vec2 tangent = normalize(v0+v1);
// vec2 miter = vec2(-tangent.y, tangent.x);
// float l = abs(w / dot(miter,normal));
// P = _curr.xy + miter*l*sign(id);
// }
if( curr == prev) {
vec2 v = normalize(_next.xy - _curr.xy);
vec2 v = normalize(next.xy - curr.xy);
vec2 normal = normalize(vec2(-v.y,v.x));
P = _curr.xy + normal*w*id;
P = curr.xy + normal*w*id;
} else if (curr == next) {
vec2 v = normalize(_curr.xy - _prev.xy);
vec2 v = normalize(curr.xy - prev.xy);
vec2 normal = normalize(vec2(-v.y,v.x));
P = _curr.xy + normal*w*id;
P = curr.xy + normal*w*id;
} else {
vec2 v0 = normalize(_curr.xy - _prev.xy);
vec2 v1 = normalize(_next.xy - _curr.xy);
vec2 v0 = normalize(curr.xy - prev.xy);
vec2 v1 = normalize(next.xy - curr.xy);
vec2 normal = normalize(vec2(-v0.y,v0.x));
vec2 tangent = normalize(v0+v1);
vec2 miter = vec2(-tangent.y, tangent.x);
float l = abs(w / dot(miter,normal));
P = _curr.xy + miter*l*sign(id);
P = curr.xy + miter*l*sign(id);
}
if( abs(id) > 1.5 ) v_color.a = 0.0;
v_distance = w*id;
gl_Position = viewport_to_NDC(P, <viewport.viewport_global>.zw, curr_.z / curr_.w);
// gl_Position = viewport_to_NDC(P, <viewport.viewport_global>.zw, curr_.z / curr_.w);
// gl_Position = curr.xy + miter * l * sign(id);
<viewport.transform()>;
gl_Position = vec4(P , 0.0, 1.0);
// <viewport.transform()>;
}

65
shaders/font.frag Normal file
View File

@ -0,0 +1,65 @@
uniform sampler2D atlas_data;
in vec2 v_texCoord;
uniform vec4 uClipUV;
// uniform vec2 uSdfUV;
uniform vec4 uSdfConfig;
uniform int uMode;
uniform vec4 uBorder;
uniform vec4 uStroke;
uniform vec4 uFill;
float getUVScale(vec2 sdfUV) {
float dx = dFdx(sdfUV.x);
float dy = dFdy(sdfUV.y);
return (sqrt(dx * dx + dy * dy) + sqrt(dy * dy + dx * dx)) * 0.5;
}
struct SDF {
float outer;
float inner;
};
vec4 getTexture(vec2 uv) {
return texture(atlas_data, uv);
}
vec4 getMask(vec4 color, vec2 uv, vec2 st) {
return color; // 默认不修改颜色
}
out vec4 fragColor;
void main() {
vec4 fillColor = uFill;
vec4 strokeColor = uStroke;
float scale = getUVScale(v_texCoord);
vec4 texture = getTexture(v_texCoord);
float sdfRaw = 0.0;
float mark = 0.0;
float sdf;
float sdfRadius = uSdfConfig.x;
float expand = uBorder.x;
float bleed = uBorder.y;
float d = (texture.r - 0.75) * sdfRadius;
float s = (d + expand / uSdfConfig.y) / scale + 0.5 + bleed;
sdf = s; // Assuming SDF returns a single float, adjust as necessary
if (uMode == -2) {
fillColor = vec4(texture.rgb, fillColor.a);
}
if (gl_FragCoord.x < uClipUV.x || gl_FragCoord.y < uClipUV.y || gl_FragCoord.x > uClipUV.z || gl_FragCoord.y > uClipUV.w) {
discard;
}
// Compute mask based on SDF
float mask = clamp(sdf, 0.0, 1.0);
// Final color blending logic here
fragColor = vec4(fillColor.rgb + mark, fillColor.a * mask + mark);
}

28
shaders/font.vert Normal file
View File

@ -0,0 +1,28 @@
layout(location = 0) in vec3 input_position;
layout(location = 1) in vec2 texcoord;
uniform vec2 atlas_shape;
out float v_scale;
out vec2 v_texCoord;
out vec4 v_color;
const vec2 verts[4] = vec2[4](
vec2(-0.5f, 0.5f),
vec2(0.5f, 0.5f),
vec2(0.5f,-0.5f),
vec2(-0.5, -0.5f)
);
void main() {
// vec4 _position = <transform(input_position)>;
gl_Position = vec4(verts[gl_VertexID],0.0,1.0);
v_texCoord = texcoord / atlas_shape;
v_scale = 1.0;
v_color = vec4(1.0, 1.0, 1.0, 1.0);
}

View File

@ -1,12 +1,7 @@
layout(location = 0) in vec3 vertex;
uniform mat4 projection;
uniform mat4 view;
const vec2 verts[3] = vec2[3](
vec2(0.5f, 1.0f),
vec2(0.0f, 0.0f),
vec2(1.0f, 0.0f)
);
out vec2 vert;
out vec4 color;
@ -23,7 +18,7 @@ vec4 srgb_to_linear(vec4 srgb_color) {
}
void main() {
vert = verts[gl_VertexID];
vert = vec2(vertex.xy);
color = srgb_to_linear(vec4(vert, 0.5, 1.0));
gl_Position = vec4(vert - 0.5, 0.0, 1.0);
gl_Position = vec4(vert, 0.0, 1.0);
}

View File

@ -0,0 +1,289 @@
const float kernel_bias = -0.234377;
const float kernel_scale = 1.241974;
uniform sampler2D u_kernel;
vec4 filter1D_radius1(sampler2D kernel, float index, float x, vec4 c0, vec4 c1) {
float w;
float w_sum = 0.0;
vec4 r = vec4(0.0,0.0,0.0,0.0);
w = texture(kernel, vec2(0.000000+(x/1.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c0 * w;
w = texture(kernel, vec2(1.000000-(x/1.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r + c1 * w;
return r;
}
vec4 filter2D_radius1(sampler2D _texture, sampler2D kernel, float index, vec2 uv, vec2 pixel) {
vec2 texel = uv/pixel - vec2(0.5,0.5) ;
vec2 f = fract(texel);
texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel;
vec4 t0 = filter1D_radius1(kernel, index, f.x,
texture(_texture, texel + vec2(0,0)*pixel),
texture(_texture, texel + vec2(1,0)*pixel));
vec4 t1 = filter1D_radius1(kernel, index, f.x,
texture(_texture, texel + vec2(0,1)*pixel),
texture(_texture, texel + vec2(1,1)*pixel));
return filter1D_radius1(kernel, index, f.y, t0, t1);
}
vec4 filter1D_radius2(sampler2D kernel, float index, float x, vec4 c0, vec4 c1, vec4 c2, vec4 c3) {
float w;
float w_sum = 0.0;
vec4 r = vec4(0.0,0.0,0.0,0.0);
w = texture(kernel, vec2(0.500000+(x/2.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c0 * w;
w = texture(kernel, vec2(0.500000-(x/2.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c2 * w;
w = texture(kernel, vec2(0.000000+(x/2.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c1 * w;
w = texture(kernel, vec2(1.000000-(x/2.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c3 * w;
return r;
}
vec4 filter2D_radius2(sampler2D _texture, sampler2D kernel, float index, vec2 uv, vec2 pixel) {
vec2 texel = uv/pixel - vec2(0.5,0.5) ;
vec2 f = fract(texel);
texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel;
vec4 t0 = filter1D_radius2(kernel, index, f.x,
texture(_texture, texel + vec2(-1,-1)*pixel),
texture(_texture, texel + vec2(0,-1)*pixel),
texture(_texture, texel + vec2(1,-1)*pixel),
texture(_texture, texel + vec2(2,-1)*pixel));
vec4 t1 = filter1D_radius2(kernel, index, f.x,
texture(_texture, texel + vec2(-1,0)*pixel),
texture(_texture, texel + vec2(0,0)*pixel),
texture(_texture, texel + vec2(1,0)*pixel),
texture(_texture, texel + vec2(2,0)*pixel));
vec4 t2 = filter1D_radius2(kernel, index, f.x,
texture(_texture, texel + vec2(-1,1)*pixel),
texture(_texture, texel + vec2(0,1)*pixel),
texture(_texture, texel + vec2(1,1)*pixel),
texture(_texture, texel + vec2(2,1)*pixel));
vec4 t3 = filter1D_radius2(kernel, index, f.x,
texture(_texture, texel + vec2(-1,2)*pixel),
texture(_texture, texel + vec2(0,2)*pixel),
texture(_texture, texel + vec2(1,2)*pixel),
texture(_texture, texel + vec2(2,2)*pixel));
return filter1D_radius2(kernel, index, f.y, t0, t1, t2, t3);
}
vec4 filter1D_radius3(sampler2D kernel, float index, float x, vec4 c0, vec4 c1, vec4 c2, vec4 c3, vec4 c4, vec4 c5) {
float w;
float w_sum = 0.0;
vec4 r = vec4(0.0,0.0,0.0,0.0);
w = texture(kernel, vec2(0.666667+(x/3.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c0 * w;
w = texture(kernel, vec2(0.333333-(x/3.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c3 * w;
w = texture(kernel, vec2(0.333333+(x/3.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c1 * w;
w = texture(kernel, vec2(0.666667-(x/3.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c4 * w;
w = texture(kernel, vec2(0.000000+(x/3.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c2 * w;
w = texture(kernel, vec2(1.000000-(x/3.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c5 * w;
return r;
}
vec4 filter2D_radius3(sampler2D _texture, sampler2D kernel, float index, vec2 uv, vec2 pixel) {
vec2 texel = uv/pixel - vec2(0.5,0.5) ;
vec2 f = fract(texel);
texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel;
vec4 t0 = filter1D_radius3(kernel, index, f.x,
texture(_texture, texel + vec2(-2,-2)*pixel),
texture(_texture, texel + vec2(-1,-2)*pixel),
texture(_texture, texel + vec2(0,-2)*pixel),
texture(_texture, texel + vec2(1,-2)*pixel),
texture(_texture, texel + vec2(2,-2)*pixel),
texture(_texture, texel + vec2(3,-2)*pixel));
vec4 t1 = filter1D_radius3(kernel, index, f.x,
texture(_texture, texel + vec2(-2,-1)*pixel),
texture(_texture, texel + vec2(-1,-1)*pixel),
texture(_texture, texel + vec2(0,-1)*pixel),
texture(_texture, texel + vec2(1,-1)*pixel),
texture(_texture, texel + vec2(2,-1)*pixel),
texture(_texture, texel + vec2(3,-1)*pixel));
vec4 t2 = filter1D_radius3(kernel, index, f.x,
texture(_texture, texel + vec2(-2,0)*pixel),
texture(_texture, texel + vec2(-1,0)*pixel),
texture(_texture, texel + vec2(0,0)*pixel),
texture(_texture, texel + vec2(1,0)*pixel),
texture(_texture, texel + vec2(2,0)*pixel),
texture(_texture, texel + vec2(3,0)*pixel));
vec4 t3 = filter1D_radius3(kernel, index, f.x,
texture(_texture, texel + vec2(-2,1)*pixel),
texture(_texture, texel + vec2(-1,1)*pixel),
texture(_texture, texel + vec2(0,1)*pixel),
texture(_texture, texel + vec2(1,1)*pixel),
texture(_texture, texel + vec2(2,1)*pixel),
texture(_texture, texel + vec2(3,1)*pixel));
vec4 t4 = filter1D_radius3(kernel, index, f.x,
texture(_texture, texel + vec2(-2,2)*pixel),
texture(_texture, texel + vec2(-1,2)*pixel),
texture(_texture, texel + vec2(0,2)*pixel),
texture(_texture, texel + vec2(1,2)*pixel),
texture(_texture, texel + vec2(2,2)*pixel),
texture(_texture, texel + vec2(3,2)*pixel));
vec4 t5 = filter1D_radius3(kernel, index, f.x,
texture(_texture, texel + vec2(-2,3)*pixel),
texture(_texture, texel + vec2(-1,3)*pixel),
texture(_texture, texel + vec2(0,3)*pixel),
texture(_texture, texel + vec2(1,3)*pixel),
texture(_texture, texel + vec2(2,3)*pixel),
texture(_texture, texel + vec2(3,3)*pixel));
return filter1D_radius3(kernel, index, f.y, t0, t1, t2, t3, t4, t5);
}
vec4 filter1D_radius4(sampler2D kernel, float index, float x, vec4 c0, vec4 c1, vec4 c2, vec4 c3, vec4 c4, vec4 c5, vec4 c6, vec4 c7) {
float w;
float w_sum = 0.0;
vec4 r = vec4(0.0,0.0,0.0,0.0);
w = texture(kernel, vec2(0.750000+(x/4.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c0 * w;
w = texture(kernel, vec2(0.250000-(x/4.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c4 * w;
w = texture(kernel, vec2(0.500000+(x/4.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c1 * w;
w = texture(kernel, vec2(0.500000-(x/4.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c5 * w;
w = texture(kernel, vec2(0.250000+(x/4.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c2 * w;
w = texture(kernel, vec2(0.750000-(x/4.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c6 * w;
w = texture(kernel, vec2(0.000000+(x/4.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c3 * w;
w = texture(kernel, vec2(1.000000-(x/4.0),index) ).r;
w = w*kernel_scale + kernel_bias;
r = r+ c7 * w;
return r;
}
vec4 filter2D_radius4(sampler2D _texture, sampler2D kernel, float index, vec2 uv, vec2 pixel) {
vec2 texel = uv/pixel - vec2(0.5,0.5) ;
vec2 f = fract(texel);
texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel;
vec4 t0 = filter1D_radius4(kernel, index, f.x,
texture(_texture, texel + vec2(-3,-3)*pixel),
texture(_texture, texel + vec2(-2,-3)*pixel),
texture(_texture, texel + vec2(-1,-3)*pixel),
texture(_texture, texel + vec2(0,-3)*pixel),
texture(_texture, texel + vec2(1,-3)*pixel),
texture(_texture, texel + vec2(2,-3)*pixel),
texture(_texture, texel + vec2(3,-3)*pixel),
texture(_texture, texel + vec2(4,-3)*pixel));
vec4 t1 = filter1D_radius4(kernel, index, f.x,
texture(_texture, texel + vec2(-3,-2)*pixel),
texture(_texture, texel + vec2(-2,-2)*pixel),
texture(_texture, texel + vec2(-1,-2)*pixel),
texture(_texture, texel + vec2(0,-2)*pixel),
texture(_texture, texel + vec2(1,-2)*pixel),
texture(_texture, texel + vec2(2,-2)*pixel),
texture(_texture, texel + vec2(3,-2)*pixel),
texture(_texture, texel + vec2(4,-2)*pixel));
vec4 t2 = filter1D_radius4(kernel, index, f.x,
texture(_texture, texel + vec2(-3,-1)*pixel),
texture(_texture, texel + vec2(-2,-1)*pixel),
texture(_texture, texel + vec2(-1,-1)*pixel),
texture(_texture, texel + vec2(0,-1)*pixel),
texture(_texture, texel + vec2(1,-1)*pixel),
texture(_texture, texel + vec2(2,-1)*pixel),
texture(_texture, texel + vec2(3,-1)*pixel),
texture(_texture, texel + vec2(4,-1)*pixel));
vec4 t3 = filter1D_radius4(kernel, index, f.x,
texture(_texture, texel + vec2(-3,0)*pixel),
texture(_texture, texel + vec2(-2,0)*pixel),
texture(_texture, texel + vec2(-1,0)*pixel),
texture(_texture, texel + vec2(0,0)*pixel),
texture(_texture, texel + vec2(1,0)*pixel),
texture(_texture, texel + vec2(2,0)*pixel),
texture(_texture, texel + vec2(3,0)*pixel),
texture(_texture, texel + vec2(4,0)*pixel));
vec4 t4 = filter1D_radius4(kernel, index, f.x,
texture(_texture, texel + vec2(-3,1)*pixel),
texture(_texture, texel + vec2(-2,1)*pixel),
texture(_texture, texel + vec2(-1,1)*pixel),
texture(_texture, texel + vec2(0,1)*pixel),
texture(_texture, texel + vec2(1,1)*pixel),
texture(_texture, texel + vec2(2,1)*pixel),
texture(_texture, texel + vec2(3,1)*pixel),
texture(_texture, texel + vec2(4,1)*pixel));
vec4 t5 = filter1D_radius4(kernel, index, f.x,
texture(_texture, texel + vec2(-3,2)*pixel),
texture(_texture, texel + vec2(-2,2)*pixel),
texture(_texture, texel + vec2(-1,2)*pixel),
texture(_texture, texel + vec2(0,2)*pixel),
texture(_texture, texel + vec2(1,2)*pixel),
texture(_texture, texel + vec2(2,2)*pixel),
texture(_texture, texel + vec2(3,2)*pixel),
texture(_texture, texel + vec2(4,2)*pixel));
vec4 t6 = filter1D_radius4(kernel, index, f.x,
texture(_texture, texel + vec2(-3,3)*pixel),
texture(_texture, texel + vec2(-2,3)*pixel),
texture(_texture, texel + vec2(-1,3)*pixel),
texture(_texture, texel + vec2(0,3)*pixel),
texture(_texture, texel + vec2(1,3)*pixel),
texture(_texture, texel + vec2(2,3)*pixel),
texture(_texture, texel + vec2(3,3)*pixel),
texture(_texture, texel + vec2(4,3)*pixel));
vec4 t7 = filter1D_radius4(kernel, index, f.x,
texture(_texture, texel + vec2(-3,4)*pixel),
texture(_texture, texel + vec2(-2,4)*pixel),
texture(_texture, texel + vec2(-1,4)*pixel),
texture(_texture, texel + vec2(0,4)*pixel),
texture(_texture, texel + vec2(1,4)*pixel),
texture(_texture, texel + vec2(2,4)*pixel),
texture(_texture, texel + vec2(3,4)*pixel),
texture(_texture, texel + vec2(4,4)*pixel));
return filter1D_radius4(kernel, index, f.y, t0, t1, t2, t3, t4, t5, t6, t7);
}
vec4 Nearest(sampler2D _texture, vec2 shape, vec2 uv) { return texture(_texture,uv); }
vec4 Bilinear(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius1(_texture, u_kernel, 0.031250, uv, 1.0/shape); }
vec4 Hanning(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius1(_texture, u_kernel, 0.093750, uv, 1.0/shape); }
vec4 Hamming(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius1(_texture, u_kernel, 0.156250, uv, 1.0/shape); }
vec4 Hermite(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius1(_texture, u_kernel, 0.218750, uv, 1.0/shape); }
vec4 Kaiser(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius1(_texture, u_kernel, 0.281250, uv, 1.0/shape); }
vec4 Quadric(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.343750, uv, 1.0/shape); }
vec4 Bicubic(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.406250, uv, 1.0/shape); }
vec4 CatRom(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.468750, uv, 1.0/shape); }
vec4 Mitchell(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.531250, uv, 1.0/shape); }
vec4 Spline16(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.593750, uv, 1.0/shape); }
vec4 Spline36(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius3(_texture, u_kernel, 0.656250, uv, 1.0/shape); }
vec4 Gaussian(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.718750, uv, 1.0/shape); }
vec4 Bessel(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius4(_texture, u_kernel, 0.781250, uv, 1.0/shape); }
vec4 Sinc(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius4(_texture, u_kernel, 0.843750, uv, 1.0/shape); }
vec4 Lanczos(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius4(_texture, u_kernel, 0.906250, uv, 1.0/shape); }
vec4 Blackman(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius4(_texture, u_kernel, 0.968750, uv, 1.0/shape); }

View File

@ -3,6 +3,10 @@
layout(points) in;
layout(triangle_strip, max_vertices = 4) out;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
// conf: Range, Elevation, Resolution, 0.0
uniform vec4 conf;
in float in_value[];

View File

@ -1,8 +1,10 @@
// Layout
layout(location = 0) in vec3 position;
out float in_value;
void main() {
gl_Position = vec4(position.x, position.y, 0.0, 1.0);
gl_Position = vec4(position.x, position.y, 0.5, 1.0);
in_value = position.z;
}

745
src/_pg.rs Normal file
View File

@ -0,0 +1,745 @@
use crate::components::Program;
use crate::font_manager::FontManager;
use crate::graphics::colormap::linear::LinearColormap;
use crate::graphics::font::Text;
use crate::graphics::ppi::PPIConfig;
use crate::graphics::threed::ThreeD;
use crate::graphics::transforms::viewport::Viewport;
use crate::graphics::{ppi::PPI, Graphics};
use crate::graphics::{AttaWithProgram, AttachWithMouse, Config, MouseState};
use crate::utils::resources::{
RcGlFramebuffer, RcGlRcResource, RcGlResource, RcGlTexture, Resource,
};
// use crate::{
// errors::*,
// ui::{State, GUI},
// };
use glow::{HasContext, NativeBuffer, NativeFramebuffer, NativeTexture, NativeVertexArray};
use imgui::{ImString, Ui};
use log::info;
use std::marker::PhantomPinned;
use std::pin::Pin;
use std::ptr::NonNull;
use std::{cell::RefCell, collections::HashMap, rc::Rc};
type RcGraphic<T> = Rc<RefCell<T>>;
type ProgramId = &'static str;
// pub struct App<'a> {
// gui: Option<GUI>,
// gl: GL,
// viewport: Viewport,
// programs: Option<Programs<'a>>,
// }
// impl<'a> App<'a> {
// pub fn new(gl: GL) -> Result<Self> {
// let viewport = Viewport::new()?;
// Ok(Self {
// gui: None,
// viewport,
// programs: None,
// gl,
// })
// }
// fn render_for_program<P: Graphics>(
// gl: &'a glow::Context,
// program_with_window: &HashMap<ProgramId, Vec<ImString>>,
// _windows: &mut HashMap<ImString, WindowData<'a>>,
// p: &mut P,
// ) where
// P::Config: From<Config>,
// {
// // let mut p = program.borrow_mut();
// if program_with_window.len() == 0 {
// return;
// }
// p.mount(&gl).unwrap();
// program_with_window.get(&P::id).map(|windows| {
// for window in windows.iter() {
// let window_info = &mut *(_windows.get_mut(window).unwrap());
// // Skip the window if it doesn't need to be redrawn
// if !window_info.need_redraw {
// continue;
// }
// // window_info.re_init = false;
// // If the window needs to be reinitialized, set the config
// for conf in window_info.confs.iter() {
// p.set_config(&gl, &(conf.1.clone()).into()).unwrap();
// }
// // Attach the modifer to the program
// if let Some(motion) = window_info.modifer.as_ref() {
// motion.attach_with_program(&gl, p.program_ref()).unwrap();
// }
// // Attach the data to the program
// unsafe {
// if window_info.gl_fb_resources.is_none() {
// continue;
// }
// let framebuffer = window_info.gl_fb_resources.as_ref().unwrap().native();
// gl.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer));
// let attach = window_info.attach.get(P::id);
// if attach.is_some() {
// let vao = attach.unwrap().vao.native();
// gl.bind_vertex_array(Some(vao));
// }
// if attach.is_some() {
// let window_size = window_info.size;
// gl.viewport(0, 0, window_size[0] as i32, window_size[1] as i32);
// p.draw(&gl, attach.as_ref().unwrap().len).unwrap();
// }
// if attach.is_some() {
// gl.bind_vertex_array(None);
// }
// gl.bind_framebuffer(glow::FRAMEBUFFER, None);
// window_info.need_redraw = false;
// }
// }
// });
// // Unmount the program
// p.unmount(&gl).unwrap();
// }
// pub fn render(&mut self) {
// // Self::render_for_program(
// // &self.gl,
// // &self.windows_manager.program_with_window,
// // &mut self.windows_manager.windows,
// // &mut self.programs.ppi_module,
// // );
// // Self::render_for_program(
// // &self.gl,
// // &self.windows_manager.program_with_window,
// // &mut self.windows_manager.windows,
// // &mut self.programs.text_module,
// // );
// }
// // pub fn render_ui(&mut self, ui: &Ui, window: &winit::window::Window, run: &mut bool) {
// // self.gui.render(ui);
// // self.windows_manager.show_window(ui);
// // // self.windows_manager.destroy_window();
// // }
// pub fn programs(&mut self) -> &mut Programs<'a> {
// self.programs.as_mut().unwrap()
// }
// pub fn prepare(&mut self) {
// self.programs = Some(Programs::new(self.gl.gl(), &self.viewport).unwrap());
// self.programs().prepare();
// unsafe {
// // self.gl.enable(glow::DEPTH_TEST);
// }
// }
// pub fn destroy(&mut self) {
// self.programs().destroy();
// }
// }
// pub struct WindowsManager<'a> {
// gl: &'a glow::Context,
// gl_resource_dispatcher: GlResourceDispatcher,
// windows: HashMap<ImString, WindowData<'a>>,
// program_with_window: HashMap<ProgramId, Vec<ImString>>,
// }
// impl<'a> WindowsManager<'a> {
// fn new(gl: &'a glow::Context, gl_resource_dispatcher: GlResourceDispatcher) -> Self {
// Self {
// gl,
// gl_resource_dispatcher,
// windows: HashMap::with_capacity(30),
// program_with_window: HashMap::with_capacity(30),
// }
// }
// fn destroy_window(&mut self) {
// for (id, window) in self.windows.iter() {
// if !window.open {
// self.program_with_window.iter_mut().for_each(|(_, v)| {
// v.retain(|v| v != id);
// });
// }
// }
// self.windows.retain(|_, v| v.open == true);
// }
// pub fn create_framebuffer(
// &mut self,
// id: &str,
// size: [i32; 2],
// ) -> Result<(RcGlFramebuffer<'a>, RcGlTexture<'a>)> {
// let id = &ImString::new(id);
// if self.windows.contains_key(id) {
// let info = self.windows.get(id).unwrap();
// if info.gl_fb_resources.is_some() && info.gl_tex_resources.is_some() {
// return Ok((
// info.gl_fb_resources.as_ref().unwrap().clone(),
// info.gl_tex_resources.as_ref().unwrap().clone(),
// ));
// }
// }
// let gl = self.gl;
// let tex = unsafe {
// let framebuffer = self
// .gl_resource_dispatcher
// .create_new::<NativeFramebuffer>();
// // let framebuffer = gl.create_framebuffer().unwrap();
// gl.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer.native()));
// let texture = self.gl_resource_dispatcher.create_new::<NativeTexture>();
// gl.bind_texture(glow::TEXTURE_2D, Some(texture.native()));
// gl.tex_image_2d(
// glow::TEXTURE_2D,
// 0,
// glow::RGB8 as i32,
// size[0],
// size[1],
// 0,
// glow::RGB,
// glow::UNSIGNED_BYTE,
// None,
// );
// gl.tex_parameter_i32(
// glow::TEXTURE_2D,
// glow::TEXTURE_MIN_FILTER,
// glow::LINEAR as i32,
// );
// gl.tex_parameter_i32(
// glow::TEXTURE_2D,
// glow::TEXTURE_MAG_FILTER,
// glow::LINEAR as i32,
// );
// gl.framebuffer_texture_2d(
// glow::FRAMEBUFFER,
// glow::COLOR_ATTACHMENT0,
// glow::TEXTURE_2D,
// Some(texture.native()),
// 0,
// );
// assert_eq!(
// gl.check_framebuffer_status(glow::FRAMEBUFFER),
// glow::FRAMEBUFFER_COMPLETE
// );
// gl.bind_framebuffer(glow::FRAMEBUFFER, None);
// gl.bind_texture(glow::TEXTURE_2D, None);
// (framebuffer, texture)
// };
// Ok(tex)
// }
// pub fn create_render_window(
// &mut self,
// title: &str,
// size: [f32; 2],
// ) -> Result<&mut WindowData<'a>> {
// // Insert the window data into the windows hashmap
// let id = ImString::new(title);
// let mut data = WindowData::new(&self.gl, id.clone(), size, None);
// let (fb, tex) =
// self.create_framebuffer(title, [size[0].floor() as i32, size[1].floor() as i32])?;
// data.gl_fb_resources = Some(fb);
// data.gl_tex_resources = Some(tex);
// self.windows.insert(id.clone(), data);
// let window = self.windows.get_mut(&id).unwrap();
// Ok(window)
// }
// fn show_window(&mut self, ui: &Ui) {
// let mut need_resize = vec![];
// for (id, window) in self.windows.iter_mut() {
// let mut window = window;
// ui.window(&window.title)
// .size(window.size, imgui::Condition::FirstUseEver)
// .opened(&mut window.open)
// .flags(imgui::WindowFlags::NO_SCROLLBAR)
// .build(|| {
// if ui.is_mouse_clicked(imgui::MouseButton::Left) {
// let io = ui.io();
// let pos = io.mouse_pos;
// let window_pos = ui.window_pos();
// window.last_mouse_position =
// [pos[0] - window_pos[0], pos[1] - window_pos[1]];
// }
// if ui.is_mouse_dragging(imgui::MouseButton::Left) {
// if ui.is_window_hovered() {
// let delta = ui.mouse_drag_delta();
// window.last_mouse_delta = delta;
// window.accmulate_mouse_delta = [
// window.accmulate_mouse_delta[0] + delta[0],
// window.accmulate_mouse_delta[1] + delta[1],
// ];
// window.motion = Some(MouseState::Drag {
// from: window.last_mouse_position,
// delta: delta,
// });
// println!(
// "Dragging: {:?} {:?}",
// window.last_mouse_position, window.accmulate_mouse_delta
// );
// window.modifer.as_mut().map(|v| {
// v.exec(window.motion.as_ref().unwrap());
// });
// window.need_redraw = true;
// }
// }
// if ui.is_mouse_released(imgui::MouseButton::Left) {
// if window.size != ui.window_size() {
// window.size = ui.window_size();
// info!("resized: {:?}", window.size);
// need_resize.push((window.title.clone(), ui.window_size()));
// }
// }
// if let Some(texture) = window.gl_tex_resources.as_ref() {
// let cursor = ui.cursor_pos();
// imgui::Image::new(texture.native2imguiid(), ui.window_size()).build(ui);
// ui.set_cursor_pos(cursor);
// if ui.invisible_button(&window.title, ui.window_size()) {
// let io = ui.io();
// let pos = io.mouse_pos;
// let window_pos = ui.window_pos();
// let related_pos = [pos[0] - window_pos[0], pos[1] - window_pos[1]];
// }
// }
// });
// }
// for (id, size) in need_resize.iter() {
// self.reset_window_size(id, *size);
// }
// }
// fn reset_window_size(&mut self, id: &ImString, size: [f32; 2]) {
// let window_info = self.windows.get_mut(id).unwrap();
// window_info.need_redraw = true;
// info!("resize: {:?}", size);
// let tex = unsafe {
// self.gl.bind_framebuffer(
// glow::FRAMEBUFFER,
// window_info.gl_fb_resources.as_ref().map(|v| v.native()),
// );
// let texture = self.gl.create_texture().unwrap();
// self.gl.bind_texture(glow::TEXTURE_2D, Some(texture));
// self.gl.tex_image_2d(
// glow::TEXTURE_2D,
// 0,
// glow::RGB8 as i32,
// size[0].floor() as i32,
// size[1].floor() as i32,
// 0,
// glow::RGB,
// glow::UNSIGNED_BYTE,
// None,
// );
// self.gl.tex_parameter_i32(
// glow::TEXTURE_2D,
// glow::TEXTURE_MIN_FILTER,
// glow::LINEAR as i32,
// );
// self.gl.tex_parameter_i32(
// glow::TEXTURE_2D,
// glow::TEXTURE_MAG_FILTER,
// glow::LINEAR as i32,
// );
// self.gl.framebuffer_texture_2d(
// glow::FRAMEBUFFER,
// glow::COLOR_ATTACHMENT0,
// glow::TEXTURE_2D,
// Some(texture),
// 0,
// );
// assert_eq!(
// self.gl.check_framebuffer_status(glow::FRAMEBUFFER),
// glow::FRAMEBUFFER_COMPLETE
// );
// self.gl.bind_framebuffer(glow::FRAMEBUFFER, None);
// self.gl.bind_texture(glow::TEXTURE_2D, None);
// texture
// };
// window_info.gl_tex_resources = Some(RcGlResource::new(self.gl, tex));
// }
// pub fn set_window_program<P: Graphics>(&mut self, window: ImString) {
// self.program_with_window
// .entry(P::id)
// .and_modify(|v| v.push(window.clone()))
// .or_insert(vec![window]);
// }
// }
// pub struct Programs<'a> {
// gl: &'a glow::Context,
// pub text_module: NonNull<Text<'a>>,
// pub ppi_module: PPI,
// }
impl<'a> Programs<'a> {
fn new(gl: &'a glow::Context, viewport: &Viewport) -> Result<Self> {
let font_manager = FontManager::new()?;
let mut cmap = LinearColormap::new()?;
cmap.set_colors(vec![
[170, 170, 170, 255],
[0, 34, 255, 255],
[1, 160, 246, 255],
[0, 236, 236, 255],
[0, 216, 0, 255],
[1, 144, 0, 255],
[255, 255, 0, 255],
[231, 192, 0, 255],
[255, 144, 0, 255],
[255, 0, 0, 255],
[214, 0, 0, 255],
[192, 0, 0, 255],
[255, 0, 240, 255],
[150, 0, 180, 255],
]);
cmap.set_range(0.0, 70.0);
let cmap = Box::new(cmap);
let mut ppi = PPI::new()?;
ppi.set_viewport(&viewport);
ppi.set_colormap(cmap);
let mut text_module = Text::new(gl, font_manager)?;
text_module.set_viewport(&viewport);
Ok(Self {
gl,
ppi_module: ppi,
text_module,
})
}
fn prepare(&mut self) {
self.ppi_module.compile(&self.gl).unwrap();
self.text_module.compile(&self.gl).unwrap();
}
fn destroy(&mut self) {
self.ppi_module.destroy(&self.gl).unwrap();
self.text_module.destroy(&self.gl).unwrap();
}
pub fn create_ppi_render(&self, config: Option<PPIConfig>) -> Attach<'a> {
self.create_render(&self.ppi_module)
}
pub fn create_text_render(&self) -> Attach<'a> {
self.create_render(&self.text_module)
}
fn create_render<P: Graphics>(&self, p: &P) -> Attach<'a> {
let (vao, vbo, ebo) = p.init(&self.gl);
Attach {
vao: RcGlResource::new(&self.gl, vao),
vbo: RcGlResource::new(&self.gl, vbo),
ebo: ebo.map(|ebo| RcGlResource::new(&self.gl, ebo)),
len: 0,
}
}
}
// pub struct WindowProgram<'a, 'sl, 'pro, P: Graphics> {
// window: &'sl mut WindowData<'a>,
// program: &'pro P,
// }
// impl<'a, 's, 'b, P: Graphics> WindowProgram<'a, 's, 'b, P> {
// pub fn bind_data(&mut self, data: &P::Data) -> Result {
// use bytemuck::cast_slice;
// let data = self.program.bake(data)?;
// let attach = self.window.attach.get_mut(P::id).unwrap();
// attach.len = data.2;
// let gl = self.window.gl;
// unsafe {
// gl.bind_vertex_array(Some(attach.vao.native()));
// // gl.bind_buffer(glow::VERTEX_ARRAY, Some(attach.vbo.native()));
// gl.buffer_data_u8_slice(
// glow::ARRAY_BUFFER,
// cast_slice(data.0.as_slice()),
// glow::STATIC_DRAW,
// );
// if let Some(_) = attach.ebo.as_ref() {
// // gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ebo.native()));
// gl.buffer_data_u8_slice(
// glow::ELEMENT_ARRAY_BUFFER,
// cast_slice(&data.1.as_ref().unwrap()),
// glow::STATIC_DRAW,
// );
// // gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
// }
// // gl.bind_buffer(glow::VERTEX_ARRAY, None);
// gl.bind_vertex_array(None);
// }
// Ok(())
// }
// pub fn set_config(&mut self, config: P::Config)
// where
// Config: From<P::Config>,
// {
// let config = Config::from(config);
// self.window
// .confs
// .entry(P::id)
// .and_modify(|v| *v = config.clone())
// .or_insert(config);
// }
// }
// pub struct WindowData<'a> {
// gl: &'a glow::Context,
// pub title: ImString,
// pub open: bool,
// pub copy_from: Option<ImString>,
// pub size: [f32; 2],
// gl_fb_resources: Option<RcGlResource<'a, NativeFramebuffer>>,
// gl_tex_resources: Option<RcGlResource<'a, NativeTexture>>,
// need_redraw: bool,
// attach: HashMap<ProgramId, Attach<'a>>,
// confs: HashMap<ProgramId, Config>,
// re_init: bool,
// last_mouse_position: [f32; 2],
// last_mouse_delta: [f32; 2],
// accmulate_mouse_delta: [f32; 2],
// mouse_position: [f32; 2],
// motion: Option<MouseState>,
// modifer: Option<ModiferType>,
// }
// impl<'a> WindowData<'a> {
// fn new(
// gl: &'a glow::Context,
// title: ImString,
// size: [f32; 2],
// modifer: Option<ModiferType>,
// ) -> Self {
// Self {
// gl,
// title,
// open: true,
// copy_from: None,
// size,
// last_mouse_position: [0.0, 0.0],
// last_mouse_delta: [0.0, 0.0],
// accmulate_mouse_delta: [0.0, 0.0],
// mouse_position: [0.0, 0.0],
// motion: None,
// gl_fb_resources: None,
// gl_tex_resources: None,
// need_redraw: true,
// attach: HashMap::with_capacity(10),
// re_init: true,
// confs: HashMap::with_capacity(5),
// modifer,
// }
// }
// fn set<F>(&mut self, f: F)
// where
// F: FnOnce(&mut Self),
// {
// f(self);
// self.need_redraw = true;
// // let v = self.program_with_window.entry(PPI::id).or_insert(vec![]);
// // v.push(id.clone());
// }
// pub fn set_attach<P: Graphics>(&mut self, attach: Attach<'a>) {
// self.set(move |w| {
// w.attach.insert(P::id, attach);
// });
// }
// pub fn with_program<'b, 's, P: Graphics>(
// &'b mut self,
// program: &'s P,
// ) -> WindowProgram<'a, 'b, 's, P>
// where
// 's: 'b,
// 'a: 's,
// {
// WindowProgram {
// program,
// window: self,
// }
// }
// pub fn set_window_modifer(&mut self, modifier: Option<ModiferType>) {
// self.set(|w| w.modifer = modifier);
// }
// pub fn id(&self) -> &ImString {
// &self.title
// }
// fn set_current_mouse_delta(&mut self, delta: [f32; 2]) {
// self.last_mouse_delta = delta;
// self.accmulate_mouse_delta = [
// self.accmulate_mouse_delta[0] + delta[0],
// self.accmulate_mouse_delta[1] + delta[1],
// ];
// }
// fn set_mouse_postion(&mut self, pos: [f32; 2]) {
// self.mouse_position = pos;
// }
// fn set_motion(&mut self, motion: MouseState) {
// self.motion = Some(motion);
// }
// fn set_need_redraw(&mut self) {
// self.need_redraw = true;
// }
// fn set_re_init(&mut self) {
// self.re_init = true;
// }
// fn on_mouse_drag(&mut self) {
// let state = MouseState::Drag {
// from: self.last_mouse_position,
// delta: self.last_mouse_delta,
// };
// self.set_motion(state.clone());
// self.modifer.as_mut().map(|m| {});
// self.set_need_redraw();
// }
// }
// macro_rules! modifer_exec {
// ($(($t:ty => $b:tt),)+) => {
// impl ModiferType {
// pub fn exec(&mut self, motion: &MouseState) {
// match self {
// $(
// ModiferType::$b(b) => {
// b.attach_with_mouse(motion);
// }
// )+
// _ => {}
// }
// }
// }
// impl AttaWithProgram for ModiferType {
// fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result {
// match self {
// $(
// ModiferType::$b(b) => {
// b.attach_with_program(gl, program)?;
// }
// )+
// _ => {
// }
// }
// Ok(())
// }
// }
// $(
// impl From<$t> for ModiferType {
// fn from(t: $t) -> Self {
// ModiferType::$b(t)
// }
// }
// )+
// }
// }
// pub enum ModiferType {
// ThreeD(ThreeD),
// TwoD,
// }
// modifer_exec!((ThreeD => ThreeD),);
// #[derive(Debug, Clone)]
// pub struct GlResourceDispatcher {
// gl: Rc<glow::Context>,
// }
// impl GlResourceDispatcher {
// pub fn new(gl: Rc<glow::Context>) -> Self {
// Self { gl }
// }
// pub fn create_new<'a, T: Resource>(&'a self) -> RcGlResource<'a, T> {
// RcGlResource::new(&self.gl, T::create(&self.gl))
// }
// pub fn create_new_rc<T: Resource>(&self) -> RcGlRcResource<T> {
// RcGlRcResource::new(self.gl.clone(), T::create(&self.gl))
// }
// pub fn gl(&self) -> &Rc<glow::Context> {
// &self.gl
// }
// }
// pub struct Manager {
// pub gl_resource_dispatcher: GlResourceDispatcher,
// }
// pub struct GL {
// gl: Rc<glow::Context>,
// }
// impl GL {
// pub fn new(gl: Rc<glow::Context>) -> Self {
// Self { gl }
// }
// fn gl(&self) -> &glow::Context {
// &self.gl
// }
// fn rc_gl(&self) -> Rc<glow::Context> {
// self.gl.clone()
// }
// }

View File

@ -1,25 +1,23 @@
use std::num::NonZeroU32;
use cgmath::Euler;
use glow::NativeProgram;
use nalgebra_glm::{
Vec3, Mat4x4, look_at
};
use nalgebra_glm::{look_at, Mat4x4, Vec3};
use std::num::NonZeroU32;
#[derive(Clone)]
pub(crate) struct Camera {
pos: Vec3,
upward: Vec3,
front: Vec3
center: Vec3,
}
pub type CameraPositon = Vec3;
impl Camera {
pub(crate) fn new(world_loc: CameraPositon, upward: Vec3, front: Vec3) -> Self {
pub(crate) fn new(world_loc: CameraPositon, upward: Vec3, center: Vec3) -> Self {
Self {
pos: world_loc,
upward,
front
center,
}
}
@ -31,25 +29,38 @@ impl Camera {
self.pos = pos;
}
pub fn get_front(&self) -> Vec3 {
self.front
}
pub fn set_front(&mut self, front: Vec3) {
self.front = front;
}
pub fn get_upward(&self) -> Vec3 {
self.upward
}
pub fn get_center(&self) -> Vec3 {
self.center
}
pub fn set_upward(&mut self, upward: Vec3) {
self.upward = upward;
}
pub fn set_center(&mut self, center: Vec3) {
self.center = center;
}
pub fn get_view_matrix(&self) -> Mat4x4 {
let l = self.pos + self.front;
let l = self.center;
look_at(&self.pos, &l, &self.upward)
}
pub fn front(&self) -> Vec3 {
self.center - self.pos
}
}
impl Default for Camera {
fn default() -> Self {
Self {
pos: Vec3::new(0.0, 0.0, 0.0),
upward: Vec3::new(0.0, 1.0, 0.0),
center: Vec3::new(0.0, 0.0, -1.0),
}
}
}

View File

@ -3,7 +3,7 @@ use glow::{HasContext, NativeUniformLocation};
use super::shader::Shader;
use super::snippets::{CodeType, Snippet};
use crate::components::CodeComponent;
use crate::graphics::transforms::{viewport::Viewport, Transform};
use crate::graphics::transforms::viewport::Viewport;
#[derive(Debug)]
pub struct Program {
@ -41,16 +41,23 @@ impl Program {
&self.fragment
}
pub fn geometry(&self) -> Option<&Shader> {
self.geometry.as_ref()
}
#[cfg(feature = "inparser")]
pub fn set_hook(&mut self, hook: &str, code: &Snippet) {
self.vertex.set_hook(hook, code);
self.fragment.set_hook(hook, code);
self.geometry.as_mut().map(|g| g.set_hook(hook, code));
}
#[cfg(feature = "inparser")]
pub fn set_transform<T: Transform>(&mut self, value: &T) {
self.set_hook("transform", value.snippet());
}
#[cfg(feature = "inparser")]
pub fn set_viewport(&mut self, viewport: &Viewport) {
self.set_hook("viewport", viewport.snippet());
}
@ -58,8 +65,11 @@ impl Program {
pub fn compile(&mut self, gl: &glow::Context) -> crate::errors::Result<()> {
use crate::errors::Error;
#[cfg(feature = "inparser")]
{
self.vertex.define("_GLUMPY__VERTEX_SHADER__");
self.fragment.define("_GLUMPY__FRAGMENT_SHADER__");
}
unsafe {
let program = gl.create_program().map_err(|e| Error::InvalidProgram(e))?;
@ -72,6 +82,7 @@ impl Program {
// Geom Shader
let geom_shader = if let Some(geometry) = self.geometry.as_mut() {
#[cfg(feature = "inparser")]
geometry.define("_GLUMPY__GEOMETRY_SHADER__");
let geometry_shader = geometry.compile(&self.version, gl);
@ -134,13 +145,13 @@ mod test {
fn test_program() {
let vertex = Shader::new(
glow::VERTEX_SHADER,
CodeType::<&'static str>::Path("agg-fast-path.vert".into()),
CodeType::from_path("agg-fast-path.vert"),
)
.unwrap();
let fragment = Shader::new(
glow::FRAGMENT_SHADER,
CodeType::<&'static str>::Path("agg-fast-path.frag".into()),
CodeType::from_path("agg-fast-path.frag"),
)
.unwrap();

View File

@ -15,7 +15,10 @@ pub type ShaderType = u32;
#[derive(Debug)]
pub struct Shader {
target: u32,
#[cfg(feature = "inparser")]
parsed: CodeBlock,
parsed: String,
#[cfg(feature = "inparser")]
pub hooks: Vec<String>,
}
@ -26,25 +29,34 @@ impl std::fmt::Display for Shader {
}
impl Shader {
pub fn new<S: Borrow<str>>(target: ShaderType, code: CodeType<S>) -> Result<Self> {
pub fn new(target: ShaderType, code: CodeType) -> Result<Self> {
let code = match code {
CodeType::Code(code) => code.borrow().to_string(),
CodeType::Code(code) => code,
CodeType::Path(path) => {
let code = find_file(path).expect("Failed to find file");
let code = find_file(&path).expect(&format!(
"Failed to find file {}",
&path.as_os_str().to_str().unwrap()
));
code
}
};
#[cfg(feature = "inparser")]
{
let code = merge_includes(code).map_err(|e| Error::InvalidSnippet(e.to_string()))?;
let parsed = CodeBlock::new(&code)?;
let hooks = parsed.all_hooks().iter().map(|h| h.name.clone()).collect();
info!("Shader:{} Hooks: {:?}", target, hooks);
Ok(Self {
return Ok(Self {
target,
parsed,
hooks,
});
}
Ok(Self {
target,
parsed: code,
})
}
@ -68,11 +80,13 @@ impl Shader {
shader
}
#[cfg(feature = "inparser")]
pub fn define(&mut self, s: &str) {
self.parsed
.insert(Code::new(&format!("#define {}\n", s)), 0);
}
#[cfg(feature = "inparser")]
pub fn set_hook(&mut self, hook: &str, code: &Snippet) {
self.parsed.set_hook(hook, code);
}
@ -217,7 +231,8 @@ mod utils {
body.push_str("}\n\n");
let input = header + &body;
let result = Snippet::new("fetch_uniform", CodeType::Code(input), false, None).unwrap();
let result =
Snippet::new("fetch_uniform", CodeType::from_code(input), false, None).unwrap();
result
}
@ -231,7 +246,7 @@ mod test {
fn test_shader() {
let shader = Shader::new(
glow::VERTEX_SHADER,
CodeType::<&'static str>::Path("agg-fast-path.vert".into()),
CodeType::from_path("agg-fast-path.vert"),
)
.unwrap();

View File

@ -3,6 +3,7 @@ use std::{
cell::{Ref, RefCell},
collections::{HashMap, HashSet},
ops::Add,
path::{Path, PathBuf},
rc::Rc,
sync::atomic::{AtomicUsize, Ordering},
};
@ -10,6 +11,7 @@ pub use util::Variable;
use crate::{
errors::{Error, Result},
shaders::{CodePiece, EmptyPiece},
utils::{find_file, Code, CodeBlock, Function, Hook, ShareVariable, SnippetCode},
};
mod util;
@ -26,9 +28,33 @@ pub enum InputType {
Other(Variable),
}
pub enum CodeType<S: std::borrow::Borrow<str>> {
Code(S),
Path(std::path::PathBuf),
// pub enum CodeType<
// CP: CodePiece = EmptyPiece,
// S: std::borrow::Borrow<str> = &'static str,
// P: AsRef<Path> = &'static str,
// > {
// Code(S),
// Path(P),
// CodePiece(CP),
// }
pub enum CodeType {
Code(String),
Path(PathBuf),
}
impl CodeType {
pub fn from_path<P: AsRef<Path>>(path: P) -> Self {
Self::Path(path.as_ref().to_path_buf())
}
pub fn from_code<S: std::borrow::Borrow<str>>(code: S) -> Self {
Self::Code(code.borrow().to_string())
}
pub fn from_piece<CP: CodePiece>(piece: CP) -> Self {
Self::Code(piece.to_string())
}
}
#[derive(Debug, Clone)]
@ -43,14 +69,14 @@ pub struct Snippet {
}
impl Snippet {
pub fn new<S: std::borrow::Borrow<str>>(
pub fn new(
name: &'static str,
code: CodeType<S>,
code: CodeType,
mangling: bool,
main: Option<String>,
) -> Result<Self> {
let code = match code {
CodeType::Code(code) => code.borrow().to_string(),
CodeType::Code(code) => code,
CodeType::Path(path) => {
let code = find_file(path).expect("Failed to find file");
code
@ -120,7 +146,11 @@ impl Snippet {
pub fn call(&self, paras: &Vec<String>) -> Option<String> {
(self.main.as_ref()).map(|name| {
if let Some(link) = &self.link {
let call_name = self.alias.as_ref().map(|a| a.get(name).unwrap()).unwrap_or(name);
let call_name = self
.alias
.as_ref()
.map(|a| a.get(name).unwrap())
.unwrap_or(name);
let c = link.call(paras);
if let Some(c) = c {
return format!("{}({})", call_name, c);
@ -128,7 +158,11 @@ impl Snippet {
return format!("{}()", call_name);
}
} else {
let call_name = self.alias.as_ref().map(|a| a.get(name).unwrap()).unwrap_or(name);
let call_name = self
.alias
.as_ref()
.map(|a| a.get(name).unwrap())
.unwrap_or(name);
// let call_name = self.alias.get(name).unwrap();
format!("{}({})", call_name, paras.join(", "))
}
@ -164,7 +198,7 @@ impl Add for Snippet {
let code = rhs.parsed.to_string();
raw_code.push_str(&code);
Snippet::new(self.name, CodeType::Code(raw_code),false, None).unwrap()
Snippet::new(self.name, CodeType::from_code(raw_code), false, None).unwrap()
}
}
@ -192,9 +226,9 @@ mod tests {
}
"#;
let snippet = Snippet::new("polar", CodeType::Code(code), true, None).unwrap();
let snippet = Snippet::new("polar", CodeType::from_code(code), true, None).unwrap();
let snippet2 = Snippet::new("polar2", CodeType::Code(code),true, None).unwrap();
let snippet2 = Snippet::new("polar2", CodeType::from_code(code), true, None).unwrap();
let snippet3 = snippet.clone() + snippet2.clone();

View File

@ -170,7 +170,7 @@ impl Data {
};
use DataType::*;
let valuetype = info.value_type.as_str();
let valuetype = info.value_name.as_str();
let data_type = block_data_type_prepare!(valuetype,
"ET" => ET,

View File

@ -1,6 +1,6 @@
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
pub type Result<T = ()> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
@ -21,4 +21,13 @@ pub enum Error {
#[error("Invalid CoordType")]
InvalidDataType,
#[error("Init Error, cause of {0}")]
InitError(anyhow::Error),
#[error("Invalid Font {0}")]
FontError(String),
#[error("Invalid Setting {0}")]
SettingError(String),
}

View File

@ -1,68 +0,0 @@
use glow::HasContext;
use crate::{
components::{CodeType, Program, Shader},
graphics::transforms::{
polar::Polar, position::Position, trackball::Trackball, viewport::Viewport, Transform,
},
};
// pub struct PPI {
// transform: Trackball,
// program: Program,
// }
// impl PPI {
// pub fn new(version: &'static str) -> Self {
// let trackball = Trackball::new().unwrap();
// let projection = Polar::new().unwrap();
// let position = Position::new().unwrap();
// // let transform = trackball.chain(projection.chain(position));
// let vertex = Shader::new(
// glow::VERTEX_SHADER,
// CodeType::<&'static str>::Path("agg-fast-path.vert".into()),
// )
// .unwrap();
// let fragment = Shader::new(
// glow::FRAGMENT_SHADER,
// CodeType::<&'static str>::Path("agg-fast-path.frag".into()),
// )
// .unwrap();
// let mut program = Program::new(vertex, fragment, None, version);
// let viewport = Viewport::new().unwrap();
// // program.set_transform(&transform);
// program.set_viewport(&viewport);
// Self { transform, program }
// }
// pub fn compile(&mut self, gl: &glow::Context) {
// self.program.compile(gl);
// }
// pub fn program(&self) -> &Program {
// &self.program
// }
// pub fn native_program(&self) -> Option<<glow::Context as HasContext>::Program> {
// self.program.native_program
// }
// }
mod test {
use super::*;
#[test]
fn test_ppi() {
// let ppi = PPI::new("450");
// println!("{}", ppi.program.vertex());
}
}

235
src/font_manager/mod.rs Normal file
View File

@ -0,0 +1,235 @@
use crate::{errors::*, Asset};
use dirs::font_dir;
use freetype::face::LoadFlag;
use freetype::{Face, GlyphMetrics, Library};
use image::GrayImage;
use image::ImageBuffer;
use log::*;
use std::collections::HashMap;
use std::io::Write;
use std::path::PathBuf;
use std::{env, fs};
pub struct FontManager {
library: Library,
fonts: HashMap<String, Font>,
}
use tempfile::NamedTempFile;
impl FontManager {
pub fn new() -> Result<Self> {
// let source = fk::source::SystemSource::new();
let library = Library::init().map_err(|e| Error::InitError(e.into()))?;
let root_path = font_dir().unwrap_or_else(|| {
let default_font = Asset::get("Roboto-Regular.ttf").unwrap();
let exe_path = env::current_exe().unwrap();
let mut folder_path = exe_path.parent().unwrap().to_path_buf();
folder_path.push("resources");
if folder_path.exists() {
info!("Resources Folder exists");
} else {
fs::create_dir_all(&folder_path).unwrap();
}
folder_path
});
// let root_path = font_dir().map_or_else(
// || {
// error!("Font dir not found");
// Err(Error::InitError(anyhow::anyhow!("Font dir not found")))
// },
// |font_dir| {
// info!("Font dir: {:?}", font_dir);
// Ok(font_dir)
// },
// )?;
Ok(Self {
library,
fonts: HashMap::new(),
})
}
pub fn get_font_or_insert(&mut self, path: &str) -> Option<&Font> {
if self.fonts.contains_key(path) {
return self.fonts.get(path);
}
let font = self.library.new_face(path, 0).ok()?;
let font = Font::new(font).ok()?;
self.fonts.insert(path.to_string(), font);
self.fonts.get(path)
}
}
pub struct Font {
font: Face,
}
pub struct CharImg {
img: Vec<u8>,
width: usize,
height: usize,
}
impl CharImg {
pub fn to_image(&self) -> GrayImage {
ImageBuffer::from_raw(self.width as u32, self.height as u32, self.img.clone())
.map(|img| img.into())
.unwrap()
}
pub fn pixels(&self) -> &[u8] {
&self.img
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
}
impl Font {
fn new(font: Face) -> Result<Self> {
Ok(Self { font })
}
pub fn get_char(&self, c: char, size: isize) -> Result<CharImg> {
self.font.set_char_size(0, size * 64, 0, 96).unwrap();
if let Err(_) = self.font.load_char(c as usize, LoadFlag::RENDER) {
self.font.load_char(0, LoadFlag::RENDER).unwrap();
}
let glyph = self.font.glyph();
let bitmap = glyph.bitmap();
if bitmap.width() == 0 || bitmap.rows() == 0 {
return Err(Error::FontError("bitmap is empty".to_string()));
}
Ok(CharImg {
img: bitmap.buffer().to_vec(),
width: bitmap.width() as usize,
height: bitmap.rows() as usize,
})
}
pub fn set_char_size(&self, size: isize) {
self.font.set_char_size(0, size * 64, 0, 96).unwrap();
}
pub fn set_char(&self, c: char) {
self.font.load_char(c as usize, LoadFlag::RENDER).unwrap();
}
pub fn glyph(&self) -> &freetype::GlyphSlot {
self.font.glyph()
}
pub fn get_advance(&self) -> (f32, f32) {
let advance = self.font.glyph().advance();
(advance.x as f32 / 64.0, advance.y as f32 / 64.0)
}
pub fn get_kerning(&self, prev: char, c: char) -> (f32, f32) {
let prev = self.font.get_char_index(prev as usize).unwrap();
let curr = self.font.get_char_index(c as usize).unwrap();
let kerning = self
.font
.get_kerning(prev, curr, freetype::face::KerningMode::KerningDefault)
.unwrap();
(kerning.x as f32 / 64.0, kerning.y as f32 / 64.0)
}
pub fn get_line_height(&self) -> i16 {
self.font.height()
}
pub fn get_metrics(&self, c: char) -> GlyphMetrics {
self.font.glyph().metrics()
}
}
#[derive(Debug, Clone)]
pub enum FontSize {
Absolute(f32),
WindowScale(f32),
DistanceScale(f32),
}
#[derive(Debug, Clone)]
pub struct FontStyle {
pub postscript_name: String,
pub size: FontSize,
pub color: [f32; 4],
pub style: f32,
}
impl Default for FontStyle {
fn default() -> Self {
Self {
postscript_name: "resources/Roboto-Regular.ttf".to_string(),
size: FontSize::Absolute(12.0),
color: [1.0, 1.0, 1.0, 1.0],
style: 0.0,
}
}
}
#[derive(Debug, Default)]
pub struct FontStyleBuilder {
style: FontStyle,
}
impl FontStyleBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn build(self) -> FontStyle {
self.style
}
pub fn postscript_name(mut self, name: &str) -> Self {
self.style.postscript_name = name.to_string();
self
}
pub fn size(mut self, size: FontSize) -> Self {
self.style.size = size;
self
}
pub fn color(mut self, color: [f32; 4]) -> Self {
self.style.color = color;
self
}
}
impl FontStyle {
pub fn buider() -> FontStyleBuilder {
FontStyleBuilder::default()
}
}
mod test {
use super::{FontManager, FontStyle};
#[test]
fn test() {
let mut fon = FontManager::new().unwrap();
let font =
fon.get_font_or_insert("/Users/tsuki/projects/radar-gi/resources/Dokdo-Regular.ttf");
if let Some(font) = font {
font.get_char('g', 64).unwrap();
}
}
}

View File

@ -6,74 +6,44 @@ use glow::{HasContext, NativeBuffer, NativeVertexArray};
use crate::components::{fetchcode, CodeComponent, CodeType, Program, Shader, Snippet};
use crate::errors::*;
use crate::graphics::transforms::viewport::Viewport;
use crate::graphics::transforms::Transform;
use crate::graphics::ty::Ty;
use crate::graphics::Graphics;
use crate::graphics::{AttaWithBuffer, Config, Graphics};
use crate::shaders::agg_path::{AggPathFragment, AggPathVertex};
use super::Colletion;
pub struct AggFastPath {
program: Program,
buffer: Vec<Point>,
}
vao: Option<NativeVertexArray>,
vbo: Option<NativeBuffer>,
pub struct AggFastPathConfig {
pub color: [f32; 4],
pub linewidth: f32,
pub antialias: f32,
}
impl AggFastPath {
pub fn new() -> Result<Self> {
let vertex = Shader::new(
glow::VERTEX_SHADER,
CodeType::<String>::Path("agg-fast-path.vert".into()),
CodeType::from_piece(AggPathVertex::new()),
)?;
let fragment = Shader::new(
glow::FRAGMENT_SHADER,
CodeType::<String>::Path("agg-fast-path.frag".into()),
CodeType::from_piece(AggPathFragment::new()),
)?;
let input_snippet = Snippet::new(
"input",
CodeType::Code(
"
layout(location = 0) in vec3 prev;
layout(location = 1) in vec3 curr;
layout(location = 2) in vec3 next;
layout(location = 3) in float id;
uniform float linewidth;
uniform float antialias;
uniform vec4 color;
",
),
false,
None,
)?;
let vertex = vertex.add_snippet_before(input_snippet);
let program = Program::new(vertex, fragment, None, "330 core");
// program.set_transform(transform);
// program.set_viewport(viewport);
Ok(Self {
program,
buffer: Vec::with_capacity(128),
vao: None,
vbo: None,
})
Ok(Self { program })
}
pub fn set_transform<T: Transform>(&mut self, transform: &T) {
self.program.set_transform(transform);
pub fn set_viewport(&mut self, gl: &glow::Context, viewport: [f32; 2]) {
let loc = self.program.get_uniform_location(gl, "viewport");
unsafe {
gl.uniform_2_f32(loc.as_ref(), viewport[0], viewport[1]);
}
pub fn set_viewport(&mut self, viewport: &Viewport) {
self.program.set_viewport(viewport);
}
pub fn set_linecolor(&mut self, gl: &glow::Context, color: [f32; 4]) {
@ -84,7 +54,7 @@ impl AggFastPath {
}
pub fn set_linewidth(&mut self, gl: &glow::Context, linewidth: f32) {
let loc = self.program.get_uniform_location(gl, "linewidth");
let loc = self.program.get_uniform_location(gl, "thickness");
unsafe {
gl.uniform_1_f32(loc.as_ref(), linewidth);
}
@ -97,6 +67,12 @@ impl AggFastPath {
}
}
pub fn set_config(&mut self, gl: &glow::Context, config: &AggFastPathConfig) {
self.set_linecolor(gl, config.color);
self.set_linewidth(gl, config.linewidth);
self.set_anatialias(gl, config.antialias);
}
pub fn program(&mut self) -> &mut Program {
&mut self.program
}
@ -106,30 +82,96 @@ impl AggFastPath {
}
}
impl Colletion for AggFastPath {
type Item = Path;
fn append(&mut self, item: Self::Item) {
self.buffer.extend(item.points);
}
}
impl Graphics for AggFastPath {
const id: &'static str = "AggPath";
type Config = AggFastPathConfig;
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
use bytemuck::cast_slice;
self.program.compile(gl)?;
self.program.compile(gl)
}
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()> {
unsafe {
gl.draw_elements(glow::TRIANGLES, count, glow::UNSIGNED_INT, 0);
}
Ok(())
}
fn destroy(&mut self, gl: &glow::Context) -> Result<()> {
self.program.destroy(gl);
Ok(())
}
fn program_ref(&self) -> &Program {
&self.program
}
fn program_mut(&mut self) -> &mut Program {
&mut self.program
}
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()> {
Ok(())
}
fn mount(&mut self, gl: &glow::Context) -> Result<()> {
unsafe {
gl.use_program(self.program.native_program);
}
Ok(())
}
fn unmount(&mut self, gl: &glow::Context) -> Result<()> {
unsafe {
gl.use_program(None);
}
Ok(())
}
}
impl AttaWithBuffer for AggFastPath {
type Data = Vec<Path>;
fn bake(
&self,
data: &Self::Data,
config: &<Self as Graphics>::Config,
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
let points = data.iter().map(|v| &v.points).flatten().collect::<Vec<_>>();
let mut lens = Vec::with_capacity(data.len());
let mut sum = 0;
for num in data.iter().map(|v| v.len()) {
lens.push(sum);
sum += num;
}
let mut vbo = Vec::with_capacity(points.len() * 10);
for p in points {
vbo.extend_from_slice(&[
p.prev[0], p.prev[1], p.prev[2], p.curr[0], p.curr[1], p.curr[2], p.next[0],
p.next[1], p.next[2], p.id,
]);
}
let mut ebo = vec![];
for (idx, e) in data.iter().map(|v| &v.ebo).enumerate() {
let len = lens[idx] as u32;
for e in e.iter() {
ebo.extend_from_slice(&[e[0] + len, e[1] + len, e[2] + len]);
}
}
let len = ebo.len() as i32;
Ok((vbo, Some(ebo), len))
}
fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option<NativeBuffer>) {
unsafe {
let vao = gl.create_vertex_array().unwrap();
gl.bind_vertex_array(Some(vao));
let vbo = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
cast_slice(&self.buffer),
glow::STATIC_DRAW,
);
gl.enable_vertex_attrib_array(0);
gl.vertex_attrib_pointer_f32(0, 3, glow::FLOAT, false, 40, 0);
@ -142,41 +184,15 @@ impl Graphics for AggFastPath {
gl.enable_vertex_attrib_array(3);
gl.vertex_attrib_pointer_f32(3, 1, glow::FLOAT, false, 40, 36);
self.vao = Some(vao);
self.vbo = Some(vbo);
let ebo = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ebo));
gl.bind_vertex_array(None);
}
Ok(())
}
fn draw(&self, gl: &glow::Context) -> Result<()> {
unsafe {
gl.clear(glow::COLOR_BUFFER_BIT);
gl.use_program(Some(self.program.native_program.unwrap()));
gl.bind_vertex_array(Some(self.vao.unwrap()));
gl.draw_arrays(glow::TRIANGLE_STRIP, 0, self.buffer.len() as i32);
gl.bind_vertex_array(None);
}
Ok(())
}
fn destroy(&mut self, gl: &glow::Context) -> Result<()> {
self.program.destroy(gl);
unsafe {
self.vao.map(|vao| gl.delete_vertex_array(vao));
self.vbo.map(|vbo| gl.delete_buffer(vbo));
}
Ok(())
gl.bind_buffer(glow::ARRAY_BUFFER, None);
(vao, vbo, Some(ebo))
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
pub struct Point {
@ -186,11 +202,28 @@ pub struct Point {
id: f32,
}
impl std::fmt::Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Point {{ prev: {:?}, curr: {:?}, next: {:?}, id: {} }}",
self.prev, self.curr, self.next, self.id
)
}
}
impl Point {
fn f32_slice(&self) -> &[f32] {
unsafe { std::slice::from_raw_parts(self as *const Self as *const f32, 13) }
}
}
impl Ty for Point {}
#[derive(Debug)]
pub struct Path {
points: Vec<Point>,
ebo: Vec<[u32; 3]>,
is_closed: bool,
is_empty: bool,
}
@ -199,6 +232,8 @@ impl Default for Path {
fn default() -> Self {
Self {
points: Vec::with_capacity(500),
ebo: Vec::with_capacity(500),
is_closed: false,
is_empty: true,
}
@ -225,6 +260,8 @@ impl PathBuilder {
Path {
points: Vec::with_capacity(500),
is_closed: self.is_closed,
ebo: Vec::with_capacity(500),
is_empty: true,
}
}
@ -234,6 +271,7 @@ impl Path {
pub fn new(is_closed: bool) -> Self {
Self {
points: Vec::with_capacity(500),
ebo: Vec::with_capacity(500),
is_closed,
is_empty: true,
}
@ -304,6 +342,11 @@ impl Path {
self.points[len - 1].next = curr;
self.points[len - 2].next = curr;
}
for s in 0..(self.points.len() / 2) - 1 {
let s = s as u32;
self.ebo.push([s * 2, s * 2 + 1, (s + 1) * 2 + 1]);
self.ebo.push([s * 2, (s + 1) * 2 + 1, (s + 1) * 2]);
}
}
pub fn len(&self) -> usize {
@ -329,8 +372,6 @@ impl DerefMut for Path {
mod test {
use crate::graphics::transforms::{polar::Polar, position::Position, trackball::Trackball};
use super::*;
#[test]

View File

@ -1,40 +1,37 @@
use glow::HasContext;
use super::ColorMap;
use crate::components::{CodeType, Program, Snippet};
use crate::errors::*;
use super::ColorMap;
use crate::shaders::colormap::ColorMap as CmapPiece;
use glow::HasContext;
pub struct LinearColormap {
snippet: Snippet,
colors: Vec<[u8; 4]>,
color_changed: bool,
texture: Option<glow::NativeTexture>,
pub unvalid: f32,
pub min: f32,
pub max: f32,
}
impl LinearColormap {
pub fn new() -> Result<Self> {
let snippet = Snippet::new(
"linear",
CodeType::<&'static str>::Path("colormap.glsl".into()),
true,
None,
)?;
Ok(Self {
snippet,
colors: vec![],
texture: None,
color_changed: true,
min: 0.0,
max: 1.0,
unvalid: 0.0,
})
}
pub fn set_unvalid_value(&mut self, value: f32) {
self.unvalid = value;
self.color_changed = true;
}
pub fn set_colors(&mut self, colors: Vec<[u8; 4]>) {
self.colors = colors;
self.color_changed = true;
@ -79,18 +76,14 @@ impl ColorMap for LinearColormap {
glow::NEAREST as i32,
);
let name = self.snippet.find_symbol("conf").unwrap();
let location = program.get_uniform_location(gl, &name);
// gl.uniform_2_f32(location.as_ref(), 0);
let location = program.get_uniform_location(gl, "colormap_conf");
gl.uniform_4_f32(
location.as_ref(),
self.min,
self.max,
self.colors.len() as f32,
125.0,
self.unvalid,
);
self.texture = Some(texture);
}
}
@ -110,17 +103,12 @@ impl ColorMap for LinearColormap {
unsafe {
gl.active_texture(glow::TEXTURE0);
gl.bind_texture(glow::TEXTURE_1D, self.texture);
let name = self.snippet.find_symbol("colormap").unwrap();
let location = program.get_uniform_location(gl, &name);
let location = program.get_uniform_location(gl, "colormap");
gl.uniform_1_i32(location.as_ref(), 0);
}
Ok(())
}
fn snippet_ref(&self) -> &crate::components::Snippet {
&self.snippet
}
}
mod test {

View File

@ -11,7 +11,5 @@ pub trait ColorMap {
fn bind_texture(&self, gl: &glow::Context, program: &Program) -> crate::errors::Result<()>;
fn snippet_ref(&self) -> &crate::components::Snippet;
fn destroy(&mut self, gl: &glow::Context);
}

View File

@ -0,0 +1,740 @@
//! Rust port of the ESDT ("Euclidean Subpixel Distance Transform") algorithm.
//!
//!
//! This algorithm was originally published as the [`@use-gpu/glyph`](https://www.npmjs.com/package/@use-gpu/glyph)
//! `npm` package, and was described in <https://acko.net/blog/subpixel-distance-transform/>.
use super::img::{Bitmap, Image2d, NDCursor, NDCursorExt as _, Unorm8};
// HACK(eddyb) only exists to allow toggling precision for testing purposes.
#[cfg(sdfer_use_f64_instead_of_f32)]
type f32 = f64;
#[derive(Copy, Clone, Debug)]
pub struct Params {
pub pad: usize,
pub radius: f32,
pub cutoff: f32,
pub solidify: bool,
pub preprocess: bool,
// FIXME(eddyb) implement.
// pub postprocess: bool,
}
impl Default for Params {
fn default() -> Self {
Self {
pad: 4,
radius: 3.0,
cutoff: 0.25,
solidify: true,
preprocess: false,
// FIXME(eddyb) implement.
// postprocess: false,
}
}
}
/// Opaque `struct` allowing buffer reuse between SDF computations, instead of
/// reallocating all the buffers every time.
#[derive(Default)]
pub struct ReusableBuffers(ReusableBuffers2d, ReusableBuffers1d);
// Convert grayscale glyph to SDF
pub fn glyph_to_sdf(
glyph: &mut Image2d<Unorm8, impl AsMut<[Unorm8]> + AsRef<[Unorm8]>>,
params: Params,
reuse_bufs: Option<ReusableBuffers>,
) -> (Image2d<Unorm8>, ReusableBuffers) {
if params.solidify {
solidify_alpha(glyph.reborrow_mut());
}
glyph_to_esdt(glyph.reborrow_mut(), params, reuse_bufs)
}
// Solidify semi-transparent areas
fn solidify_alpha(mut glyph: Image2d<Unorm8, &mut [Unorm8]>) {
let (w, h) = (glyph.width(), glyph.height());
let mut mask: Image2d<u8> = Image2d::new(w, h);
let get_data = |x: isize, y: isize| {
if x >= 0 && (x as usize) < w && y >= 0 && (y as usize) < h {
glyph[(x as usize, y as usize)]
} else {
Unorm8::MIN
}
};
let mut masked = 0;
// Mask pixels whose alpha matches their 4 adjacent neighbors (within 16 steps)
// and who don't have black or white neighbors.
for y in 0..(h as isize) {
for x in 0..(w as isize) {
let a = get_data(x, y);
// FIXME(eddyb) audit all comparisons with `254` and try removing them.
if a == Unorm8::MIN || a >= Unorm8::from_bits(254) {
continue;
}
let l = get_data(x - 1, y);
let r = get_data(x + 1, y);
let t = get_data(x, y - 1);
let b = get_data(x, y + 1);
let (min, max) = [a, l, r, t, b]
.into_iter()
.map(|x| (x, x))
.reduce(|(a_min, a_max), (b_min, b_max)| (a_min.min(b_min), a_max.max(b_max)))
.unwrap();
let [a, min, max] = [a, min, max].map(Unorm8::to_bits);
// FIXME(eddyb) audit all comparisons with `254` and try removing them.
if (max - min) < 16 && min > 0 && max < 254 {
// NOTE(eddyb) `min > 0` guarantees all neighbors are in-bounds.
let (x, y) = (x as usize, y as usize);
// Spread to 4 neighbors with max
mask[(x - 1, y)] = mask[(x - 1, y)].max(a);
mask[(x, y - 1)] = mask[(x, y - 1)].max(a);
mask[(x, y)] = a;
mask[(x + 1, y)] = mask[(x + 1, y)].max(a);
mask[(x, y + 1)] = mask[(x, y + 1)].max(a);
masked += 1;
}
}
}
if masked == 0 {
return;
}
let get_mask = |x: isize, y: isize| {
if x >= 0 && (x as usize) < w && y >= 0 && (y as usize) < h {
mask[(x as usize, y as usize)]
} else {
0
}
};
// Sample 3x3 area for alpha normalization factor
for y in 0..(h as isize) {
for x in 0..(w as isize) {
let a = &mut glyph[(x as usize, y as usize)];
// FIXME(eddyb) audit all comparisons with `254` and try removing them.
if *a == Unorm8::MIN || *a >= Unorm8::from_bits(254) {
continue;
}
let c = get_mask(x, y);
let l = get_mask(x - 1, y);
let r = get_mask(x + 1, y);
let t = get_mask(x, y - 1);
let b = get_mask(x, y + 1);
let tl = get_mask(x - 1, y - 1);
let tr = get_mask(x + 1, y - 1);
let bl = get_mask(x - 1, y + 1);
let br = get_mask(x + 1, y + 1);
if let Some(m) = [c, l, r, t, b, tl, tr, bl, br]
.into_iter()
.find(|&x| x != 0)
{
*a = Unorm8::from_bits((a.to_bits() as f32 / m as f32 * 255.0) as u8);
}
}
}
}
// Convert grayscale or color glyph to SDF using subpixel distance transform
fn glyph_to_esdt(
mut glyph: Image2d<Unorm8, &mut [Unorm8]>,
params: Params,
reuse_bufs: Option<ReusableBuffers>,
) -> (Image2d<Unorm8>, ReusableBuffers) {
// FIXME(eddyb) use `Params` itself directly in more places.
let Params {
pad,
radius,
cutoff,
solidify: _,
preprocess,
} = params;
let wp = glyph.width() + pad * 2;
let hp = glyph.height() + pad * 2;
let mut state = State::from_glyph(glyph.reborrow_mut(), params, reuse_bufs);
state.esdt_outer_and_inner(wp, hp);
// FIXME(eddyb) implement.
// if postprocess { state.relax_subpixel_offsets(glyph, pad); }
let mut sdf = Image2d::from_fn(wp, hp, |x, y| {
let i = y * wp + x;
let ReusableBuffers2d { xo, yo, xi, yi, .. } = &state.bufs_2d;
let outer = ((xo[i].powi(2) + yo[i].powi(2)).sqrt() - 0.5).max(0.0);
let inner = ((xi[i].powi(2) + yi[i].powi(2)).sqrt() - 0.5).max(0.0);
let d = if outer >= inner { outer } else { -inner };
Unorm8::encode(1.0 - (d / radius + cutoff))
});
if !preprocess {
paint_into_distance_field(&mut sdf, glyph.reborrow(), params);
}
(sdf, ReusableBuffers(state.bufs_2d, state.reuse_bufs_1d))
}
// Helpers
fn is_black(x: f32) -> bool {
x == 0.0
}
fn is_white(x: f32) -> bool {
x == 1.0
}
fn is_solid(x: f32) -> bool {
x == 0.0 || x == 1.0
}
// Paint original alpha channel into final SDF when gray
fn paint_into_distance_field(
sdf: &mut Image2d<Unorm8>,
glyph: Image2d<Unorm8, &[Unorm8]>,
params: Params,
) {
let Params {
pad,
radius,
cutoff,
..
} = params;
for y in 0..glyph.height() {
for x in 0..glyph.width() {
let a = glyph[(x, y)].decode();
if !is_solid(a) {
let d = 0.5 - a;
sdf[(x + pad, y + pad)] = Unorm8::encode(1.0 - (d / radius + cutoff));
}
}
}
}
/// 2D buffers, which get reused (see also `ReusableBuffers` itself).
#[derive(Default)]
struct ReusableBuffers2d {
// FIXME(eddyb) group `outer` with `{x,y}o`.
outer: Bitmap,
// FIXME(eddyb) group `inner` with `{x,y}i``.
inner: Bitmap,
xo: Vec<f32>,
yo: Vec<f32>,
xi: Vec<f32>,
yi: Vec<f32>,
}
struct State {
// FIXME(eddyb) do the grouping suggested in `ReusableBuffers2d`, to have
// `outer` and `inner` fields in here, to use instead of `ReusableBuffers2d`.
bufs_2d: ReusableBuffers2d,
reuse_bufs_1d: ReusableBuffers1d,
}
impl State {
fn from_glyph(
mut glyph: Image2d<Unorm8, &mut [Unorm8]>,
params: Params,
reuse_bufs: Option<ReusableBuffers>,
) -> Self {
let Params {
pad,
// FIXME(eddyb) should this still be taken as a separate `bool`?
preprocess: relax,
..
} = params;
let wp = glyph.width() + pad * 2;
let hp = glyph.height() + pad * 2;
let np = wp * hp;
let ReusableBuffers(bufs_2d, reuse_bufs_1d) = reuse_bufs.unwrap_or_default();
let mut state = Self {
bufs_2d,
reuse_bufs_1d,
};
let ReusableBuffers2d {
outer,
inner,
xo,
yo,
xi,
yi,
} = &mut state.bufs_2d;
outer.resize_and_fill_with(wp, hp, true);
inner.resize_and_fill_with(wp, hp, false);
for buf2d in [&mut *xo, yo, xi, yi] {
buf2d.clear();
buf2d.resize(np, 0.0);
}
for y in 0..glyph.height() {
for x in 0..glyph.width() {
let a = &mut glyph[(x, y)];
if *a == Unorm8::MIN {
continue;
}
// FIXME(eddyb) audit all comparisons with `254` and try removing them,
// especially this step that modifies the `glyph` itself.
if *a >= Unorm8::from_bits(254) {
// Fix for bad rasterizer rounding
*a = Unorm8::MAX;
outer.at(x + pad, y + pad).set(false);
inner.at(x + pad, y + pad).set(true);
} else {
outer.at(x + pad, y + pad).set(false);
inner.at(x + pad, y + pad).set(false);
}
}
}
//
// Generate subpixel offsets for all border pixels
//
let get_data = |x: isize, y: isize| {
if x >= 0 && (x as usize) < glyph.width() && y >= 0 && (y as usize) < glyph.height() {
glyph[(x as usize, y as usize)].decode()
} else {
0.0
}
};
// Make vector from pixel center to nearest boundary
for y in 0..(glyph.height() as isize) {
for x in 0..(glyph.width() as isize) {
let c = get_data(x, y);
// NOTE(eddyb) `j - 1` (X-) / `j - wp` (Y-) positive (`pad >= 1`).
let j = ((y as usize) + pad) * wp + (x as usize) + pad;
if !is_solid(c) {
let dc = c - 0.5;
// NOTE(eddyb) l(eft) r(ight) t(op) b(ottom)
let l = get_data(x - 1, y);
let r = get_data(x + 1, y);
let t = get_data(x, y - 1);
let b = get_data(x, y + 1);
let tl = get_data(x - 1, y - 1);
let tr = get_data(x + 1, y - 1);
let bl = get_data(x - 1, y + 1);
let br = get_data(x + 1, y + 1);
let ll = (tl + l * 2.0 + bl) / 4.0;
let rr = (tr + r * 2.0 + br) / 4.0;
let tt = (tl + t * 2.0 + tr) / 4.0;
let bb = (bl + b * 2.0 + br) / 4.0;
let (min, max) = [l, r, t, b, tl, tr, bl, br]
.into_iter()
.map(|x| (x, x))
.reduce(|(a_min, a_max), (b_min, b_max)| {
(a_min.min(b_min), a_max.max(b_max))
})
.unwrap();
if min > 0.0 {
// Interior creases
inner.at(x as usize + pad, y as usize + pad).set(true);
continue;
}
if max < 1.0 {
// Exterior creases
outer.at(x as usize + pad, y as usize + pad).set(true);
continue;
}
let mut dx = rr - ll;
let mut dy = bb - tt;
let dl = 1.0 / (dx.powi(2) + dy.powi(2)).sqrt();
dx *= dl;
dy *= dl;
xo[j] = -dc * dx;
yo[j] = -dc * dy;
} else if is_white(c) {
// NOTE(eddyb) l(eft) r(ight) t(op) b(ottom)
let l = get_data(x - 1, y);
let r = get_data(x + 1, y);
let t = get_data(x, y - 1);
let b = get_data(x, y + 1);
if is_black(l) {
xo[j - 1] = 0.4999;
outer.at(x as usize + pad - 1, y as usize + pad).set(false);
inner.at(x as usize + pad - 1, y as usize + pad).set(false);
}
if is_black(r) {
xo[j + 1] = -0.4999;
outer.at(x as usize + pad + 1, y as usize + pad).set(false);
inner.at(x as usize + pad + 1, y as usize + pad).set(false);
}
if is_black(t) {
yo[j - wp] = 0.4999;
outer.at(x as usize + pad, y as usize + pad - 1).set(false);
inner.at(x as usize + pad, y as usize + pad - 1).set(false);
}
if is_black(b) {
yo[j + wp] = -0.4999;
outer.at(x as usize + pad, y as usize + pad + 1).set(false);
inner.at(x as usize + pad, y as usize + pad + 1).set(false);
}
}
}
}
// Blend neighboring offsets but preserve normal direction
// Uses xo as input, xi as output
// Improves quality slightly, but slows things down.
if relax {
let check_cross = |nx, ny, dc, dl, dr, dxl, dyl, dxr, dyr| {
((dxl * nx + dyl * ny) * (dc * dl) > 0.0)
&& ((dxr * nx + dyr * ny) * (dc * dr) > 0.0)
&& ((dxl * dxr + dyl * dyr) * (dl * dr) > 0.0)
};
for y in 0..(glyph.height() as isize) {
for x in 0..(glyph.width() as isize) {
// NOTE(eddyb) `j - 1` (X-) / `j - wp` (Y-) positive (`pad >= 1`).
let j = ((y as usize) + pad) * wp + (x as usize) + pad;
let nx = xo[j];
let ny = yo[j];
if nx == 0.0 && ny == 0.0 {
continue;
}
// NOTE(eddyb) c(enter) l(eft) r(ight) t(op) b(ottom)
let c = get_data(x, y);
let l = get_data(x - 1, y);
let r = get_data(x + 1, y);
let t = get_data(x, y - 1);
let b = get_data(x, y + 1);
let dxl = xo[j - 1];
let dxr = xo[j + 1];
let dxt = xo[j - wp];
let dxb = xo[j + wp];
let dyl = yo[j - 1];
let dyr = yo[j + 1];
let dyt = yo[j - wp];
let dyb = yo[j + wp];
let mut dx = nx;
let mut dy = ny;
let mut dw = 1.0;
let dc = c - 0.5;
let dl = l - 0.5;
let dr = r - 0.5;
let dt = t - 0.5;
let db = b - 0.5;
if !is_solid(l) && !is_solid(r) {
if check_cross(nx, ny, dc, dl, dr, dxl, dyl, dxr, dyr) {
dx += (dxl + dxr) / 2.0;
dy += (dyl + dyr) / 2.0;
dw += 1.0;
}
}
if !is_solid(t) && !is_solid(b) {
if check_cross(nx, ny, dc, dt, db, dxt, dyt, dxb, dyb) {
dx += (dxt + dxb) / 2.0;
dy += (dyt + dyb) / 2.0;
dw += 1.0;
}
}
if !is_solid(l) && !is_solid(t) {
if check_cross(nx, ny, dc, dl, dt, dxl, dyl, dxt, dyt) {
dx += (dxl + dxt - 1.0) / 2.0;
dy += (dyl + dyt - 1.0) / 2.0;
dw += 1.0;
}
}
if !is_solid(r) && !is_solid(t) {
if check_cross(nx, ny, dc, dr, dt, dxr, dyr, dxt, dyt) {
dx += (dxr + dxt + 1.0) / 2.0;
dy += (dyr + dyt - 1.0) / 2.0;
dw += 1.0;
}
}
if !is_solid(l) && !is_solid(b) {
if check_cross(nx, ny, dc, dl, db, dxl, dyl, dxb, dyb) {
dx += (dxl + dxb - 1.0) / 2.0;
dy += (dyl + dyb + 1.0) / 2.0;
dw += 1.0;
}
}
if !is_solid(r) && !is_solid(b) {
if check_cross(nx, ny, dc, dr, db, dxr, dyr, dxb, dyb) {
dx += (dxr + dxb + 1.0) / 2.0;
dy += (dyr + dyb + 1.0) / 2.0;
dw += 1.0;
}
}
let nn = (nx * nx + ny * ny).sqrt();
let ll = (dx * nx + dy * ny) / nn;
dx = nx * ll / dw / nn;
dy = ny * ll / dw / nn;
xi[j] = dx;
yi[j] = dy;
}
}
}
// Produce zero points for positive and negative DF, at +0.5 / -0.5.
// Splits xs into xo/xi
for y in 0..(glyph.height() as isize) {
for x in 0..(glyph.width() as isize) {
// NOTE(eddyb) `j - 1` (X-) / `j - wp` (Y-) positive (`pad >= 1`).
let j = ((y as usize) + pad) * wp + (x as usize) + pad;
// NOTE(eddyb) `if relax` above changed `xs`/`ys` in the original.
let (nx, ny) = if relax {
(xi[j], yi[j])
} else {
(xo[j], yo[j])
};
if nx == 0.0 && ny == 0.0 {
continue;
}
let nn = (nx.powi(2) + ny.powi(2)).sqrt();
let sx = if ((nx / nn).abs() - 0.5) > 0.0 {
nx.signum() as isize
} else {
0
};
let sy = if ((ny / nn).abs() - 0.5) > 0.0 {
ny.signum() as isize
} else {
0
};
let c = get_data(x, y);
let d = get_data(x + sx, y + sy);
// FIXME(eddyb) is this inefficient? (was `Math.sign(d - c)`)
let s = (d - c).total_cmp(&0.0) as i8 as f32;
let dlo = (nn + 0.4999 * s) / nn;
let dli = (nn - 0.4999 * s) / nn;
xo[j] = nx * dlo;
yo[j] = ny * dlo;
xi[j] = nx * dli;
yi[j] = ny * dli;
}
}
state
}
fn esdt_outer_and_inner(&mut self, w: usize, h: usize) {
{
let Self {
bufs_2d:
ReusableBuffers2d {
outer,
inner,
xo,
yo,
xi,
yi,
},
reuse_bufs_1d,
} = self;
esdt(outer, xo, yo, w, h, reuse_bufs_1d);
esdt(inner, xi, yi, w, h, reuse_bufs_1d);
}
}
}
// 2D subpixel distance transform by unconed
// extended from Felzenszwalb & Huttenlocher https://cs.brown.edu/~pff/papers/dt-final.pdf
fn esdt(
mask: &mut Bitmap,
xs: &mut [f32],
ys: &mut [f32],
w: usize,
h: usize,
reuse_bufs_1d: &mut ReusableBuffers1d,
) {
reuse_bufs_1d.critical_minima.clear();
reuse_bufs_1d.critical_minima.reserve(w.max(h));
let mut xs = Image2d::from_storage(w, h, xs);
let mut ys = Image2d::from_storage(w, h, ys);
for x in 0..w {
let mut mask_xy_cursor = mask
.cursor_at(0, 0)
.zip(
// FIXME(eddyb) combine `xs` and `ys` into the same `Image2d`.
ys.cursor_at(0, 0).zip(xs.cursor_at(0, 0)),
)
.map_abs_and_rel(move |y| (x, y), |dy| (0, dy));
mask_xy_cursor.reset(0);
esdt1d(mask_xy_cursor, h, reuse_bufs_1d)
}
for y in 0..h {
let mut mask_xy_cursor = mask
.cursor_at(0, 0)
.zip(
// FIXME(eddyb) combine `xs` and `ys` into the same `Image2d`.
xs.cursor_at(0, 0).zip(ys.cursor_at(0, 0)),
)
.map_abs_and_rel(move |x| (x, y), |dx| (dx, 0));
mask_xy_cursor.reset(0);
esdt1d(mask_xy_cursor, w, reuse_bufs_1d)
}
}
/// 1D buffers (for `esdt1d`), which get reused between calls.
//
// FIXME(eddyb) the name is outdated now that there's only one buffer.
#[derive(Default)]
struct ReusableBuffers1d {
critical_minima: Vec<CriticalMinimum>,
}
// FIXME(eddyb) clean up the names after all the refactors.
struct CriticalMinimum {
// FIXME(eddyb) this is really just a position, since it's not used to
// index anything indirectly anymore, but rather indicates the original `q`,
// and is used to compare against it in the second iteration of `esdt1d`.
v: usize, // Array index
z: f32, // Voronoi threshold
f: f32, // Squared distance
b: f32, // Subpixel offset parallel
t: f32, // Subpixel offset perpendicular
}
// 1D subpixel distance transform
fn esdt1d(
mut mask_xy_cursor: impl for<'a> NDCursor<
'a,
usize,
RefMut = (super::img::BitmapEntry<'a>, (&'a mut f32, &'a mut f32)),
>,
// FIXME(eddyb) provide this through the cursor, maybe?
length: usize,
reuse_bufs_1d: &mut ReusableBuffers1d,
) {
// FIXME(eddyb) this is a pretty misleading name.
const INF: f32 = 1e10;
let cm = &mut reuse_bufs_1d.critical_minima;
cm.clear();
{
let (mask, (&mut dx, &mut dy)) = mask_xy_cursor.get_mut();
cm.push(CriticalMinimum {
v: 0,
z: -INF,
f: if mask.get() { INF } else { dy.powi(2) },
b: dx,
t: dy,
});
mask_xy_cursor.advance(1);
}
// Scan along array and build list of critical minima
for q in 1..length {
// Perpendicular
let (mask, (&mut dx, &mut dy)) = mask_xy_cursor.get_mut();
let fq = if mask.get() { INF } else { dy.powi(2) };
mask_xy_cursor.advance(1);
// Parallel
let qs = q as f32 + dx;
let q2 = qs.powi(2);
// Remove any minima eclipsed by this one
let mut s;
loop {
let r = &cm[cm.len() - 1];
s = (fq - r.f + q2 - r.b.powi(2)) / (qs - r.b) / 2.0;
if !(s <= r.z) {
break;
}
cm.pop();
if cm.len() == 0 {
break;
}
}
// Add to minima list
cm.push(CriticalMinimum {
v: q,
z: s,
f: fq,
b: qs,
t: dy,
});
}
mask_xy_cursor.reset(0);
// Resample array based on critical minima
{
let mut k = 0;
for q in 0..length {
// Skip eclipsed minima
while k + 1 < cm.len() && cm[k + 1].z < q as f32 {
k += 1;
}
let r = &cm[k];
// Distance from integer index to subpixel location of minimum
let rq = r.b - q as f32;
let (mut mask, (dx, dy)) = mask_xy_cursor.get_mut();
*dx = rq;
*dy = r.t;
// Mark cell as having propagated
if r.v != q {
mask.set(false);
}
mask_xy_cursor.advance(1);
}
}
}

View File

@ -0,0 +1,411 @@
// NOTE(eddyb) this is a separate module so that privacy affects sibling modules.
// FIXME(eddyb) deduplicate with `image` crate?
use std::marker::PhantomData;
use std::ops::{Index, IndexMut};
// HACK(eddyb) only exists to allow toggling precision for testing purposes.
#[cfg(sdfer_use_f64_instead_of_f32)]
type f32 = f64;
/// `[0, 1]` represented by uniformly spaced `u8` values (`0..=255`),
/// i.e. `Unorm8(byte)` corresponds to the `f32` value `byte as f32 / 255.0`.
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Unorm8(u8);
impl Unorm8 {
pub const MIN: Self = Self::from_bits(0);
pub const MAX: Self = Self::from_bits(u8::MAX);
#[inline(always)]
pub fn encode(x: f32) -> Self {
// NOTE(eddyb) manual `clamp` not needed, `(_: f32) as u8` will saturate:
// https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast
Self((x * 255.0).round() as u8)
}
#[inline(always)]
pub fn decode(self) -> f32 {
self.0 as f32 / 255.0
}
#[inline(always)]
pub const fn from_bits(bits: u8) -> Self {
Self(bits)
}
#[inline(always)]
pub const fn to_bits(self) -> u8 {
self.0
}
}
#[derive(Default, Copy, Clone)]
pub struct Image2d<T, Storage: AsRef<[T]> = Vec<T>> {
width: usize,
height: usize,
data: Storage,
_marker: PhantomData<T>,
}
impl<T, Storage: AsRef<[T]>> Image2d<T, Storage> {
pub fn new(width: usize, height: usize) -> Self
where
T: Default,
Storage: FromIterator<T>,
{
Self::from_fn(width, height, |_, _| T::default())
}
pub fn from_fn(width: usize, height: usize, mut f: impl FnMut(usize, usize) -> T) -> Self
where
Storage: FromIterator<T>,
{
Self::from_storage(
width,
height,
(0..height)
.flat_map(|y| (0..width).map(move |x| (x, y)))
.map(|(x, y)| f(x, y))
.collect(),
)
}
pub fn from_storage(width: usize, height: usize, storage: Storage) -> Self {
assert_eq!(storage.as_ref().len(), width * height);
Self {
width,
height,
data: storage,
_marker: PhantomData,
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn reborrow(&self) -> Image2d<T, &[T]> {
Image2d {
width: self.width,
height: self.height,
data: self.data.as_ref(),
_marker: PhantomData,
}
}
pub fn reborrow_mut(&mut self) -> Image2d<T, &mut [T]>
where
Storage: AsMut<[T]>,
{
Image2d {
width: self.width,
height: self.height,
data: self.data.as_mut(),
_marker: PhantomData,
}
}
pub fn cursor_at(&mut self, x: usize, y: usize) -> Image2dCursor<'_, T>
where
Storage: AsMut<[T]>,
{
let mut cursor = Image2dCursor {
image: self.reborrow_mut(),
xy_offset: 0,
};
cursor.reset((x, y));
cursor
}
}
impl<T, Storage: AsRef<[T]>> Index<(usize, usize)> for Image2d<T, Storage> {
type Output = T;
fn index(&self, (x, y): (usize, usize)) -> &T {
&self.data.as_ref()[y * self.width..][..self.width][x]
}
}
impl<T, Storage: AsMut<[T]> + AsRef<[T]>> IndexMut<(usize, usize)> for Image2d<T, Storage> {
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut T {
&mut self.data.as_mut()[y * self.width..][..self.width][x]
}
}
impl From<image::GrayImage> for Image2d<Unorm8> {
fn from(img: image::GrayImage) -> Self {
Self {
width: img.width().try_into().unwrap(),
height: img.height().try_into().unwrap(),
// HACK(eddyb) this should be a noop if the right specializations
// all kick in, and LLVM optimizes out the in-place transformation.
data: img.into_vec().into_iter().map(Unorm8::from_bits).collect(),
_marker: PhantomData,
}
}
}
impl From<Image2d<Unorm8>> for image::GrayImage {
fn from(img: Image2d<Unorm8>) -> Self {
image::GrayImage::from_vec(
img.width().try_into().unwrap(),
img.height().try_into().unwrap(),
// HACK(eddyb) this should be a noop if the right specializations
// all kick in, and LLVM optimizes out the in-place transformation.
img.data.into_iter().map(Unorm8::to_bits).collect(),
)
.unwrap()
}
}
impl From<Image2d<Unorm8>> for ndarray::Array2<u8> {
fn from(value: Image2d<Unorm8>) -> Self {
ndarray::Array2::from_shape_vec(
[value.height(), value.width()],
value.data.into_iter().map(Unorm8::to_bits).collect(),
)
.unwrap()
}
}
impl<T: Copy> Image2d<T> {
fn resize_and_fill_with(&mut self, width: usize, height: usize, initial: T) {
self.width = width;
self.height = height;
self.data.clear();
self.data.resize(width * height, initial);
}
}
#[derive(Default)]
pub struct Bitmap {
width: usize,
height: usize,
bit_8x8_blocks: Image2d<u64>,
}
pub struct BitmapEntry<'a> {
bit_8x8_block: &'a mut u64,
mask: u64,
}
impl Bitmap {
#[inline(always)]
pub fn new(width: usize, height: usize) -> Self {
let mut r = Self::default();
r.resize_and_fill_with(width, height, false);
r
}
#[inline(always)]
pub(crate) fn resize_and_fill_with(&mut self, width: usize, height: usize, initial: bool) {
self.width = width;
self.height = height;
self.bit_8x8_blocks.resize_and_fill_with(
width.div_ceil(8),
height.div_ceil(8),
if initial { !0 } else { 0 },
);
}
#[inline(always)]
pub fn width(&self) -> usize {
self.width
}
#[inline(always)]
pub fn height(&self) -> usize {
self.height
}
const BW: usize = 8;
const BH: usize = 8;
#[inline(always)]
const fn bit_8x8_block_xy_and_mask(x: usize, y: usize) -> ((usize, usize), u64) {
(
(x / Self::BW, y / Self::BH),
1 << ((y % Self::BH) * Self::BW + x % Self::BW),
)
}
#[inline(always)]
pub fn get(&self, x: usize, y: usize) -> bool {
let (block_xy, mask) = Self::bit_8x8_block_xy_and_mask(x, y);
(self.bit_8x8_blocks[block_xy] & mask) != 0
}
#[inline(always)]
pub fn at(&mut self, x: usize, y: usize) -> BitmapEntry<'_> {
let (block_xy, mask) = Self::bit_8x8_block_xy_and_mask(x, y);
BitmapEntry {
bit_8x8_block: &mut self.bit_8x8_blocks[block_xy],
mask,
}
}
#[inline(always)]
pub fn cursor_at(&mut self, x: usize, y: usize) -> BitmapCursor<'_> {
let mut cursor = BitmapCursor {
bit_8x8_blocks: self.bit_8x8_blocks.cursor_at(0, 0),
intra_block_xy: (0, 0),
};
cursor.reset((x, y));
cursor
}
}
impl BitmapEntry<'_> {
#[inline(always)]
pub fn get(&self) -> bool {
(*self.bit_8x8_block & self.mask) != 0
}
#[inline(always)]
pub fn set(&mut self, value: bool) {
if value {
*self.bit_8x8_block |= self.mask;
} else {
*self.bit_8x8_block &= !self.mask;
}
}
}
// FIXME(eddyb) this doesn't really belong here, and should use GATs.
pub trait NDCursor<'a, P> {
type RefMut;
fn reset(&'a mut self, position: P);
fn get_mut(&'a mut self) -> Self::RefMut;
fn advance(&'a mut self, delta: P);
}
pub trait NDCursorExt<P>: for<'a> NDCursor<'a, P> {
fn zip<C2: NDCursorExt<P>>(self, other: C2) -> NDCursorZip<Self, C2>
where
Self: Sized,
{
NDCursorZip(self, other)
}
// FIXME(eddyb) this is a really bad API but a whole coordinate system would be overkill.
fn map_abs_and_rel<P2, FA: Fn(P2) -> P, FR: Fn(P2) -> P>(
self,
fa: FA,
fr: FR,
) -> NDCursorMapPos<Self, FA, FR>
where
Self: Sized,
{
NDCursorMapPos(self, fa, fr)
}
}
impl<P, C: for<'a> NDCursor<'a, P>> NDCursorExt<P> for C {}
pub struct NDCursorZip<C1, C2>(C1, C2);
impl<'a, P: Copy, C1: NDCursor<'a, P>, C2: NDCursor<'a, P>> NDCursor<'a, P>
for NDCursorZip<C1, C2>
{
type RefMut = (C1::RefMut, C2::RefMut);
#[inline(always)]
fn reset(&'a mut self, position: P) {
self.0.reset(position);
self.1.reset(position);
}
#[inline(always)]
fn get_mut(&'a mut self) -> Self::RefMut {
(self.0.get_mut(), self.1.get_mut())
}
#[inline(always)]
fn advance(&'a mut self, delta: P) {
self.0.advance(delta);
self.1.advance(delta);
}
}
pub struct NDCursorMapPos<C, FA, FR>(C, FA, FR);
impl<'a, C: NDCursor<'a, P>, P, P2, FA: Fn(P2) -> P, FR: Fn(P2) -> P> NDCursor<'a, P2>
for NDCursorMapPos<C, FA, FR>
{
type RefMut = C::RefMut;
#[inline(always)]
fn reset(&'a mut self, position: P2) {
self.0.reset((self.1)(position));
}
#[inline(always)]
fn get_mut(&'a mut self) -> Self::RefMut {
self.0.get_mut()
}
#[inline(always)]
fn advance(&'a mut self, delta: P2) {
self.0.advance((self.2)(delta));
}
}
pub struct Image2dCursor<'a, T> {
// FIXME(eddyb) find a way to use something closer to `slice::IterMut` here.
image: Image2d<T, &'a mut [T]>,
xy_offset: usize,
}
impl<'a, T: 'a> NDCursor<'a, (usize, usize)> for Image2dCursor<'_, T> {
type RefMut = &'a mut T;
#[inline(always)]
fn reset(&'a mut self, (x, y): (usize, usize)) {
self.xy_offset = y * self.image.width + x;
}
#[inline(always)]
fn get_mut(&'a mut self) -> Self::RefMut {
&mut self.image.data[self.xy_offset]
}
#[inline(always)]
fn advance(&'a mut self, (dx, dy): (usize, usize)) {
// FIXME(eddyb) check for edge conditions? (should be more like an iterator)
self.xy_offset += dy * self.image.width + dx;
}
}
pub struct BitmapCursor<'a> {
bit_8x8_blocks: Image2dCursor<'a, u64>,
// FIXME(eddyb) because of this we can't just use `bit_8x8_block_xy_and_mask`.
intra_block_xy: (u8, u8),
}
impl<'a> NDCursor<'a, (usize, usize)> for BitmapCursor<'_> {
type RefMut = BitmapEntry<'a>;
#[inline(always)]
fn reset(&'a mut self, (x, y): (usize, usize)) {
self.bit_8x8_blocks.reset((x / Bitmap::BW, y / Bitmap::BH));
self.intra_block_xy = ((x % Bitmap::BW) as u8, (y % Bitmap::BH) as u8);
}
#[inline(always)]
fn get_mut(&'a mut self) -> Self::RefMut {
let bxy = self.intra_block_xy;
let (_, mask) = Bitmap::bit_8x8_block_xy_and_mask(bxy.0 as usize, bxy.1 as usize);
BitmapEntry {
bit_8x8_block: self.bit_8x8_blocks.get_mut(),
mask,
}
}
#[inline(always)]
fn advance(&'a mut self, (dx, dy): (usize, usize)) {
// FIXME(eddyb) check for edge conditions? (should be more like an iterator)
let bxy = self.intra_block_xy;
let new_bxy = (bxy.0 as usize + dx, bxy.1 as usize + dy);
let whole_block_dxy = (new_bxy.0 / Bitmap::BW, new_bxy.1 / Bitmap::BH);
if whole_block_dxy != (0, 0) {
self.bit_8x8_blocks.advance(whole_block_dxy);
}
self.intra_block_xy = (
(new_bxy.0 % Bitmap::BW) as u8,
(new_bxy.1 % Bitmap::BH) as u8,
);
}
}

View File

@ -0,0 +1,5 @@
mod esdt;
mod img;
pub use esdt::*;
pub use img::{Image2d, Unorm8};

364
src/graphics/font/mod.rs Normal file
View File

@ -0,0 +1,364 @@
mod esdt;
use esdt::{Image2d, Unorm8};
use glow::{HasContext, TEXTURE_2D};
use std::{cell::RefCell, collections::HashMap};
use text_items::Text as TextTrait;
mod text_items;
pub use text_items::{Anchor, LineStyle, PositionText, TextLine};
use crate::{
components::{CodeType, Program, Shader},
errors::*,
font_manager::{FontManager, FontStyle},
shaders::font::{FontFragment, FontGeometry, FontVertex},
utils::resources::RcGlTexture,
};
use super::{transforms::viewport::Viewport, AttaWithBuffer, Config, Graphics};
pub struct Text<'a> {
gl: &'a glow::Context,
font_manager: RefCell<FontManager>,
cache: RefCell<HashMap<String, Cache<'a>>>,
program: Program,
}
pub struct Cache<'a> {
gl: &'a glow::Context,
cache: HashMap<char, TextVType>,
width: usize,
height: usize,
last_pos: [usize; 2],
tex: RcGlTexture<'a>,
}
impl<'a> Cache<'a> {
pub fn new(gl: &'a glow::Context) -> Self {
let tex = unsafe {
let tex = gl.create_texture().unwrap();
gl.bind_texture(glow::TEXTURE_2D, Some(tex));
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MIN_FILTER,
glow::LINEAR as i32,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MAG_FILTER,
glow::LINEAR as i32,
);
gl.tex_image_2d(
glow::TEXTURE_2D,
0,
glow::R8 as i32,
1024,
1024,
0,
glow::RED,
glow::UNSIGNED_BYTE,
None,
);
tex
};
Self {
gl,
cache: HashMap::new(),
// cache_tex: vec![0; 1024 * 1024],
width: 1024,
height: 1024,
last_pos: [0, 0],
tex: RcGlTexture::new(gl, tex),
}
}
fn get(&self, c: char) -> Option<&TextVType> {
self.cache.get(&c)
}
fn insert_glyph(&mut self, tex: Image2d<Unorm8>, c: char) -> &TextVType {
// use image::GrayImage;
use ndarray::{s, Array2};
let width = tex.width();
let height = tex.height();
let data: Array2<u8> = tex.into();
let x = self.last_pos[0];
let y = self.last_pos[1];
use glow::PixelUnpackData;
unsafe {
self.gl
.bind_texture(glow::TEXTURE_2D, Some(self.tex.native()));
self.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
self.gl.tex_sub_image_2d(
glow::TEXTURE_2D,
0,
x as i32,
y as i32,
width as i32,
height as i32,
glow::RED,
glow::UNSIGNED_BYTE,
PixelUnpackData::Slice(&data.as_slice().unwrap()),
);
println!("{} {} {} {}", x, y, width, height);
println!("size: {}", &data.len());
self.gl.bind_texture(glow::TEXTURE_2D, None);
}
self.cache.insert(
c,
TextVType {
// tex_coords: [
// x as f32,
// (y + height - 1) as f32,
// (x + width - 1) as f32,
// (y + height - 1) as f32,
// (x + width - 1) as f32,
// y as f32,
// x as f32,
// y as f32,
// ],
tex_coords: [
x as f32,
(y + height - 1) as f32,
(x + width - 1) as f32,
y as f32,
],
// tex_coords: [0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0],
},
);
if x + width >= 1024 {
self.last_pos[0] = 0;
self.last_pos[1] += height;
} else {
self.last_pos[0] += width;
}
if y + height > self.height {
self.height += 1024;
// self.cache_tex.extend(vec![0; 1024 * 1024]);
}
self.cache.get(&c).unwrap()
}
fn test(&self) {
let mut data = vec![0; 1024 * 1024];
unsafe {
self.gl
.bind_texture(glow::TEXTURE_2D, Some(self.tex.native()));
self.gl.get_tex_image(
glow::TEXTURE_2D,
0,
glow::RED,
glow::UNSIGNED_BYTE,
glow::PixelPackData::Slice(&mut data),
);
self.gl.bind_texture(glow::TEXTURE_2D, None);
}
let img = image::GrayImage::from_raw(1024, 1024, data).unwrap();
img.save("test.png").unwrap();
}
}
#[derive(Clone)]
pub struct TextVType {
tex_coords: [f32; 4],
}
impl TextVType {
fn left_top(&self) -> [f32; 2] {
[self.tex_coords[0], self.tex_coords[1]]
}
fn right_bottom(&self) -> [f32; 2] {
[self.tex_coords[2], self.tex_coords[3]]
}
}
impl<'a> Text<'a> {
pub fn new(gl: &'a glow::Context, font_manager: FontManager) -> Result<Self> {
let vertex = Shader::new(
glow::VERTEX_SHADER,
crate::components::CodeType::from_piece(FontVertex::new()),
)?;
let geom = Shader::new(
glow::GEOMETRY_SHADER,
crate::components::CodeType::from_piece(FontGeometry::new()),
)?;
let fragment = Shader::new(
glow::FRAGMENT_SHADER,
CodeType::from_piece(FontFragment::new()),
)?;
let mut program = Program::new(vertex, fragment, Some(geom), "330 core");
Ok(Self {
gl,
font_manager: RefCell::new(font_manager),
cache: RefCell::new(HashMap::new()),
program,
})
}
pub fn set_viewport(&self, viewport: [f32; 2]) {
let loc = self.program.get_uniform_location(&self.gl, "viewport");
unsafe {
self.gl
.uniform_2_f32(loc.as_ref(), viewport[0], viewport[1]);
}
}
pub fn set_config(&self, config: FontConfig) {
match config {
FontConfig::Textline(style, font) => {
let loc = self.program.get_uniform_location(&self.gl, "location");
let anchor = self.program.get_uniform_location(&self.gl, "anchor");
unsafe {
self.gl.uniform_3_f32(loc.as_ref(), 0.0, 0.0, 1.0);
self.gl.uniform_3_f32(anchor.as_ref(), 0.0, 0.0, 0.0);
}
}
}
}
fn set_uniforms(&self) {
let conf = self.program.get_uniform_location(&self.gl, "uSdfConfig");
let u_mode = self.program.get_uniform_location(&self.gl, "uMode");
let u_border = self.program.get_uniform_location(&self.gl, "uBorder");
let u_stroke = self.program.get_uniform_location(&self.gl, "uStroke");
let u_fill = self.program.get_uniform_location(&self.gl, "uFill");
unsafe {
self.gl.uniform_4_f32(conf.as_ref(), 5.0, 3.0, 0.0, 0.0);
self.gl.uniform_1_i32(u_mode.as_ref(), -1);
self.gl.uniform_4_f32(u_border.as_ref(), 0.0, 0.0, 0.0, 0.0);
self.gl.uniform_4_f32(u_stroke.as_ref(), 1.0, 1.0, 1.0, 1.0);
self.gl.uniform_4_f32(u_fill.as_ref(), 1.0, 1.0, 1.0, 1.0);
}
}
}
impl<'a> Graphics for Text<'a> {
const id: &'static str = "Text";
type Config = FontConfig;
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
self.program.compile(gl)
}
fn destroy(&mut self, gl: &glow::Context) -> Result<()> {
self.program.destroy(gl);
Ok(())
}
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()> {
unsafe {
let loc = self.program.get_uniform_location(gl, "atlas_data");
gl.uniform_1_i32(loc.as_ref(), 0);
gl.active_texture(glow::TEXTURE0);
gl.bind_texture(
glow::TEXTURE_2D,
self.cache
.borrow()
.get("resources/Roboto-Regular.ttf")
.map(|v| v.tex.native()),
);
let width_loc = self.program.get_uniform_location(gl, "atlas_shape");
gl.uniform_2_f32(width_loc.as_ref(), 1024.0, 1024.0);
self.set_uniforms();
gl.draw_arrays(glow::POINTS, 0, count);
}
Ok(())
}
fn program_mut(&mut self) -> &mut Program {
&mut self.program
}
fn program_ref(&self) -> &Program {
&self.program
}
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()> {
unsafe {
match config {
FontConfig::Textline(style, font) => {
let loc = self.program.get_uniform_location(gl, "location");
let anchor = self.program.get_uniform_location(gl, "anchor");
gl.uniform_3_f32(loc.as_ref(), 0.0, 0.0, 2.0);
gl.uniform_3_f32(anchor.as_ref(), 0.0, 0.0, 0.0);
}
}
}
Ok(())
}
}
impl<'a> AttaWithBuffer for Text<'a> {
type Data = PositionText;
fn bake(
&self,
data: &Self::Data,
config: &<Self as Graphics>::Config,
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
let v = data.bake(
&self.gl,
&mut *self.font_manager.borrow_mut(),
&mut *self.cache.borrow_mut(),
)?;
Ok((v.vertex(), None, v.len() as i32))
}
fn init(
&self,
gl: &glow::Context,
) -> (
glow::NativeVertexArray,
glow::NativeBuffer,
Option<glow::NativeBuffer>,
) {
unsafe {
let vao = gl.create_vertex_array().unwrap();
gl.bind_vertex_array(Some(vao));
let vbo = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
gl.enable_vertex_attrib_array(0);
gl.vertex_attrib_pointer_f32(0, 4, glow::FLOAT, false, 48, 0);
gl.enable_vertex_attrib_array(1);
gl.vertex_attrib_pointer_f32(1, 4, glow::FLOAT, false, 48, 16);
gl.bind_vertex_array(None);
gl.bind_buffer(glow::ARRAY_BUFFER, None);
(vao, vbo, None)
}
}
}
#[derive(Clone, Debug)]
pub enum FontConfig {
Textline(LineStyle, FontStyle),
}
mod test {
#[test]
fn test() {
use super::*;
let mut font_manager = FontManager::new().unwrap();
}
}

View File

@ -0,0 +1,260 @@
use super::esdt::{Image2d, Params, Unorm8};
use super::TextVType;
use super::{esdt::glyph_to_sdf, Cache};
use crate::components::merge_includes;
use crate::font_manager::{CharImg, FontManager, FontSize};
use crate::{errors::*, font_manager::FontStyle};
use bytemuck::{Pod, Zeroable};
use geo::kernels;
use std::collections::HashMap;
const SDF_PARAM: Params = Params {
radius: 5.0,
pad: 4,
cutoff: 0.25,
solidify: true,
preprocess: false,
};
#[derive(Debug, Default, Clone)]
pub struct LineStyle {
line_height: f32,
line_spacing: f32,
}
#[derive(Clone)]
pub struct PositionText {
item: TextLine,
position: [f32; 3],
anchor: Anchor,
}
impl PositionText {
pub fn new(item: TextLine, position: [f32; 3], anchor: Anchor) -> Self {
Self {
item,
position,
anchor,
}
}
}
impl Text for PositionText {
fn bake<'a>(
&self,
gl: &'a glow::Context,
font_manager: &mut FontManager,
cache: &mut HashMap<String, Cache<'a>>,
) -> Result<TextVertexArray> {
self.item.bake(gl, font_manager, cache)
}
}
#[derive(Clone)]
pub struct TextLine {
text: String,
font_style: FontStyle,
line_style: LineStyle,
}
impl TextLine {
pub fn new<P: Into<String>>(
text: P,
font_style: Option<FontStyle>,
line_style: Option<LineStyle>,
) -> Self {
Self {
text: text.into(),
font_style: font_style.unwrap_or_default(),
line_style: line_style.unwrap_or_default(),
}
}
}
#[derive(Clone, Copy)]
pub enum Anchor {
TopLeft,
TopCenter,
TopRight,
CenterLeft,
Center,
CenterRight,
BottomLeft,
BottomCenter,
BottomRight,
}
#[derive(Clone, Copy)]
pub enum TextAlign {
Left,
Center,
Right,
}
impl Default for TextAlign {
fn default() -> Self {
Self::Left
}
}
#[repr(C)]
#[derive(Clone, Copy, Zeroable, Pod)]
pub struct TextVertexItem {
tex_coords: [f32; 4],
position: [f32; 4],
}
pub struct TextVertexArray {
points: Vec<TextVertexItem>,
}
impl TextVertexArray {
pub fn new() -> Self {
Self {
points: Vec::with_capacity(30),
}
}
pub fn len(&self) -> usize {
self.points.len()
}
pub fn push(&mut self, item: TextVertexItem) {
self.points.push(item);
}
pub fn insert_text(&mut self, tex_coords: TextVType, position: [f32; 2], size: [f32; 2]) {
let tex_left_top = tex_coords.left_top();
let tex_right_bottom = tex_coords.right_bottom();
self.push(TextVertexItem {
position: [position[0], position[1], size[0], size[1]],
tex_coords: [
tex_left_top[0],
tex_left_top[1],
tex_right_bottom[0],
tex_right_bottom[1],
],
});
}
pub fn vertex(&self) -> Vec<f32> {
let mut result = Vec::with_capacity(self.len() * 8);
self.points.iter().for_each(|v| {
result.extend_from_slice(&v.tex_coords);
result.extend_from_slice(&v.position);
});
result
}
pub fn to_bits(&self) -> &[u8] {
bytemuck::cast_slice(&self.points)
}
}
pub trait Text: Sized {
fn bake<'a>(
&self,
gl: &'a glow::Context,
font_manager: &mut FontManager,
cache: &mut HashMap<String, Cache<'a>>,
) -> Result<TextVertexArray>;
}
impl Text for TextLine {
fn bake<'a>(
&self,
gl: &'a glow::Context,
font_manager: &mut FontManager,
cache: &mut HashMap<String, Cache<'a>>,
) -> Result<TextVertexArray> {
let font_style = &self.font_style;
let font = font_manager.get_font_or_insert(&font_style.postscript_name);
if let Some(font) = font {
cache
.entry(font_style.postscript_name.clone())
.or_insert_with(|| Cache::new(&gl));
let cache = cache.get_mut(&font_style.postscript_name).unwrap();
let mut baked = TextVertexArray::new();
let mut pen = [0.0, 0.0];
let mut prev: Option<char> = None;
for char in self.text.chars() {
if char == '\n' {
break;
}
if char == ' ' {
pen[0] += 10.0;
continue;
}
let font_size = match &font_style.size {
FontSize::Absolute(s) => *s,
FontSize::DistanceScale(_) => panic!(""),
FontSize::WindowScale(_) => panic!(""),
};
font.set_char_size(font_size.floor() as isize);
font.set_char(char);
let (x_advanced, y_advanced) = font.get_advance();
let (x_kerning, _) = prev.map_or((0.0, 0.0), |v| {
let kerning = font.get_kerning(v, char);
kerning
});
let metrics = font.get_metrics(char);
let width = (metrics.width >> 6) as f32;
let height = (metrics.height >> 6) as f32;
let bear_x = (metrics.horiBearingX >> 6) as f32;
let bear_y = (metrics.horiBearingY >> 6) as f32;
let x0 = pen[0] + bear_x as f32 + x_kerning;
let y0 = pen[1] - bear_y as f32;
let x1 = pen[0] + metrics.width as f32 + x_kerning;
let y1 = pen[1] + metrics.height as f32;
if let Some(cache) = cache.get(char) {
baked.insert_text(cache.to_owned(), [x0, y0], [width, height]);
} else {
let char_glyph = font.get_char(char, 64)?;
let mut img = char_glyph.into();
let (result, _) = glyph_to_sdf(&mut img, SDF_PARAM, None);
let b = cache.insert_glyph(result, char);
baked.insert_text(b.to_owned(), [x0, y0], [width, height]);
}
pen[0] += x_advanced;
pen[1] += y_advanced;
prev = Some(char);
}
Ok(baked)
} else {
Err(Error::FontError(format!(
"Font {} not found",
font_style.postscript_name
)))
}
}
}
impl From<CharImg> for Image2d<Unorm8> {
fn from(value: CharImg) -> Self {
// let img = Image2d::new(value.width() as u32, value.height() as u32, value.pixels);
let img = Image2d::from_storage(
value.width(),
value.height(),
value
.pixels()
.iter()
.map(|v| Unorm8::from_bits(*v))
.collect(),
);
img
}
}

View File

@ -1,33 +1,27 @@
use std::ops::{Deref, DerefMut};
use bytemuck::{Pod, Zeroable};
use bytemuck::{cast_slice, Pod, Zeroable};
use glow::{HasContext, NativeBuffer, NativeVertexArray};
use crate::components::{fetchcode, CodeComponent, CodeType, Program, Shader, Snippet};
use crate::errors::*;
use crate::graphics::transforms::viewport::Viewport;
use crate::graphics::transforms::Transform;
use crate::graphics::ty::Ty;
use crate::graphics::Graphics;
use super::{AttaWithBuffer, Config};
pub struct Hello {
program: Program,
buffer: Vec<Point>,
vao: Option<NativeVertexArray>,
vbo: Option<NativeBuffer>,
}
impl Hello {
pub fn new() -> Result<Self> {
let vertex = Shader::new(
glow::VERTEX_SHADER,
CodeType::<String>::Path("hello.vert".into()),
)?;
let fragment = Shader::new(
glow::FRAGMENT_SHADER,
CodeType::<String>::Path("hello.frag".into()),
)?;
let vertex = Shader::new(glow::VERTEX_SHADER, CodeType::from_path("hello.vert"))?;
let fragment = Shader::new(glow::FRAGMENT_SHADER, CodeType::from_path("hello.frag"))?;
let program = Program::new(vertex, fragment, None, "330 core");
@ -38,6 +32,7 @@ impl Hello {
program,
buffer: Vec::with_capacity(128),
vao: None,
vbo: None,
})
}
@ -51,6 +46,8 @@ impl Hello {
}
impl Graphics for Hello {
const id: &'static str = "Hello";
type Config = ();
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
self.program.compile(gl)?;
unsafe {
@ -58,19 +55,46 @@ impl Graphics for Hello {
.create_vertex_array()
.expect("Cannot create vertex array");
let buffer: Vec<[f32; 3]> = vec![
[-0.5, -0.5, 0.0],
[-0.5, 0.5, 0.0],
[0.5, 0.5, 0.0],
[0.5, -0.5, 0.0],
];
gl.bind_vertex_array(Some(vertex_array));
let vbo = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
gl.buffer_data_u8_slice(glow::ARRAY_BUFFER, cast_slice(&buffer), glow::STATIC_DRAW);
gl.enable_vertex_attrib_array(0);
gl.vertex_attrib_pointer_f32(0, 3, glow::FLOAT, false, 12, 0);
let ebo = gl.create_buffer().unwrap();
// let e: [[u32; 3]; 2] = [[0, 1, 3], [0, 2, 3]];
let e: [[u32; 3]; 2] = [[0, 1, 2], [2, 3, 0]];
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ebo));
gl.buffer_data_u8_slice(
glow::ELEMENT_ARRAY_BUFFER,
cast_slice(&e),
glow::STATIC_DRAW,
);
self.vao = Some(vertex_array);
self.vbo = Some(vbo);
}
Ok(())
}
fn draw(&self, gl: &glow::Context) -> Result<()> {
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()> {
unsafe {
gl.clear_color(0.05, 0.05, 0.1, 1.0);
gl.clear(glow::COLOR_BUFFER_BIT);
gl.use_program(Some(self.program.native_program.unwrap()));
gl.bind_vertex_array(self.vao);
gl.draw_arrays(glow::TRIANGLES, 0, 3);
gl.draw_elements(glow::TRIANGLES, 6, glow::UNSIGNED_INT, 0);
}
Ok(())
@ -80,11 +104,55 @@ impl Graphics for Hello {
self.program.destroy(gl);
unsafe {
self.vbo.map(|vbo| gl.delete_buffer(vbo));
self.vao.map(|vao| gl.delete_vertex_array(vao));
}
Ok(())
}
fn program_ref(&self) -> &Program {
&self.program
}
fn program_mut(&mut self) -> &mut Program {
&mut self.program
}
fn set_config(&mut self, gl: &glow::Context, config: &()) -> Result<()> {
Ok(())
}
fn mount(&mut self, gl: &glow::Context) -> Result<()> {
unsafe {
gl.use_program(self.program.native_program);
}
Ok(())
}
fn unmount(&mut self, gl: &glow::Context) -> Result<()> {
unsafe {
gl.use_program(None);
}
Ok(())
}
}
impl AttaWithBuffer for Hello {
type Data = ();
fn bake(
&self,
data: &Self::Data,
config: &<Self as Graphics>::Config,
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
return Ok((vec![], None, 0));
}
fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option<NativeBuffer>) {
panic!("Not implemented");
}
}
#[repr(C)]
@ -209,8 +277,6 @@ impl DerefMut for Path {
mod test {
use crate::graphics::transforms::{polar::Polar, position::Position, trackball::Trackball};
use super::*;
#[test]

View File

@ -1,36 +1,149 @@
pub mod collections;
pub mod colormap;
mod colormesh;
pub mod font;
pub mod hello;
pub mod plane;
pub mod ppi;
pub mod threed;
pub mod tools;
pub mod transforms;
pub mod ty;
use crate::{components::Program, errors::*};
pub use colormesh::ColorMesh;
use imgui::TextureId;
use crate::{
camera::Camera,
components::Program,
errors::*,
graphics::font::FontConfig,
pg::layout_type::ViewPort,
ui::{operation::Projection, typ::CameraOP},
};
use glow::{HasContext, NativeBuffer, NativeVertexArray};
use ppi::PPIConfig;
pub trait Graphics {
fn draw(&self, gl: &glow::Context) -> Result<()>;
const id: &'static str;
type Config;
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()>;
fn compile(&mut self, gl: &glow::Context) -> Result<()>;
fn destroy(&mut self, gl: &glow::Context) -> Result<()>;
fn program_ref(&self) -> &Program;
fn program_mut(&mut self) -> &mut Program;
fn mount(&mut self, gl: &glow::Context) -> Result<()> {
unsafe {
gl.use_program(self.program_ref().native_program.clone());
}
Ok(())
}
fn unmount(&mut self, gl: &glow::Context) -> Result<()> {
unsafe {
gl.use_program(None);
}
Ok(())
}
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()>;
}
pub trait AttaWithProgram {
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()>;
}
pub trait AttaWithBuffer {
pub trait AttaWithBuffer: Graphics {
type Data;
fn attach_with_buffer(&mut self, gl: &glow::Context, data: &Self::Data) -> Result<()>;
fn bake(
&self,
data: &Self::Data,
config: &<Self as Graphics>::Config,
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)>;
fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option<NativeBuffer>);
}
macro_rules! config_for_everyitem {
($({$conf:ty => $name:tt},)+) => {
$(
impl From<$conf> for Config {
fn from(value: $conf) -> Self {
Self::$name(value)
}
}
impl From<Config> for $conf {
fn from(value: Config) -> Self {
if let Config::$name(value) = value {
value
} else {
panic!("error transfer");
}
}
}
impl<'a> From<&'a Config> for &'a $conf {
fn from(value: &'a Config) -> &'a $conf {
if let Config::$name(value) = value {
&value
} else {
panic!("error transfer");
}
}
}
pub trait AttaWithWindow {
fn attach_with_window(&mut self, gl: &glow::Context) -> Result<TextureId>;
)+
impl From<()> for Config {
fn from(_: ()) -> Self {
Self::None
}
}
};
}
#[derive(Debug, Clone)]
pub enum Config {
PPI(PPIConfig),
Font(FontConfig),
None,
}
config_for_everyitem!({PPIConfig => PPI},{FontConfig => Font}, );
pub trait AttachWithIO {
type State: CameraOP;
fn attach_with_mouse(
&mut self,
state: &Self::State,
camera: &mut Camera,
projection: &mut Projection,
viewport: &ViewPort,
) -> bool;
fn reset(&mut self);
fn init_camera(&self) -> Camera;
}
#[derive(Debug, Clone)]
pub enum MouseState {
Drag { from: [f32; 2], delta: [f32; 2] },
Wheel(f32),
None,
}
#[derive(Debug, Clone)]
pub struct MouseKeyboardState {
pub mouse_state: MouseState,
pub keyboard_state: [bool; 652],
}

7
src/graphics/plane.rs Normal file
View File

@ -0,0 +1,7 @@
pub struct Plane {}
impl Plane {
pub fn new() -> Self {
Self {}
}
}

View File

@ -1,133 +1,64 @@
use std::cell::RefCell;
use std::rc::Rc;
use bytemuck::cast_slice;
use glow::{HasContext, NativeBuffer, NativeFramebuffer, NativeTexture, NativeVertexArray};
use ndarray::{s, IndexLonger};
use crate::components::{CodeType, Program, Shader, Snippet};
use crate::data_loader::{CoordType, Data, DataType};
use crate::errors::*;
use super::colormap::ColorMap;
use super::transforms::viewport::Viewport;
use super::transforms::Transform;
use super::{ty, AttaWithBuffer, AttaWithWindow, Graphics};
use super::{transforms, AttaWithBuffer, AttaWithProgram, AttachWithIO, Config, Graphics};
use crate::components::{CodeType, Program, Shader};
use crate::data_loader::{CoordType, Data, DataType};
use crate::errors::*;
use crate::graphics::colormap::linear::LinearColormap;
use glow::{HasContext, NativeBuffer, NativeVertexArray};
pub struct PPI {
program: Program,
layer: isize,
vao: Option<NativeVertexArray>,
vbo: Option<NativeBuffer>,
cmap: Option<Box<dyn ColorMap>>,
framebuffer: Option<NativeFramebuffer>,
frametexture: Option<NativeTexture>,
buffer: Vec<[f32; 3]>,
cmap: LinearColormap,
}
impl PPI {
pub fn new() -> Result<Self> {
let vertex = Shader::new(
glow::VERTEX_SHADER,
CodeType::<String>::Path("ppi.vert".into()),
)?;
let geom = Shader::new(
glow::GEOMETRY_SHADER,
CodeType::<String>::Path("ppi.geom".into()),
)?;
use crate::shaders::ppi::*;
let vertex = Shader::new(glow::VERTEX_SHADER, CodeType::from_piece(PPIVertex::new()))?;
let geom = Shader::new(glow::GEOMETRY_SHADER, CodeType::from_piece(PPIGeom::new()))?;
let fragment = Shader::new(
glow::FRAGMENT_SHADER,
CodeType::<String>::Path("ppi.frag".into()),
CodeType::from_piece(PPIFragment::new()),
)?;
let mut cmap = LinearColormap::new()?;
let mut program = Program::new(vertex, fragment, Some(geom), "330 core");
let program = Program::new(vertex, fragment, Some(geom), "330 core");
Ok(Self {
program,
layer: 0,
vao: None,
vbo: None,
buffer: Vec::with_capacity(180 * 50),
framebuffer: None,
frametexture: None,
cmap: None,
})
}
pub fn set_transform<T: Transform>(&mut self, transform: &T) {
self.program.set_transform(transform);
}
pub fn set_viewport(&mut self, viewport: &Viewport) {
self.program.set_viewport(viewport);
}
pub fn set_colormap(&mut self, colormap: Box<dyn ColorMap>) {
self.program.set_hook("colormap", colormap.snippet_ref());
self.cmap = Some(colormap);
Ok(Self { program, cmap })
}
pub fn program(&mut self) -> &mut Program {
&mut self.program
}
fn set_conf(&self, gl: &glow::Context, dpir: f32, dpie: f32) {
fn set_conf(&mut self, gl: &glow::Context, config: &PPIConfig) {
let rdpi = config.rdpi;
let adpi = config.adpi;
let location = self.program.get_uniform_location(gl, "conf");
self.cmap
.set_range(config.color_range[0], config.color_range[1]);
self.cmap.set_colors(config.colors.clone());
self.cmap.set_unvalid_value(config.unvalid_value);
self.cmap.attach_with_program(gl, &self.program);
unsafe {
gl.uniform_4_f32(location.as_ref(), dpir, dpie, 0f32, 0f32);
gl.uniform_4_f32(
location.as_ref(),
rdpi,
adpi,
0f32,
if config.three_d { 1.0 } else { 0.0 },
);
}
}
pub fn add_ppi_layer(&mut self, layer: isize) {
self.layer = self.layer + layer;
self.layer.clamp(0, 23);
}
pub fn program_ref(&self) -> &Program {
&self.program
}
fn bake_data(&self, data: &Data) -> Result<Vec<[f32; 3]>> {
let first_block = data.blocks.get(0).unwrap();
let first_block_data = first_block.data.view();
if let CoordType::Polar {
r_range,
azimuth,
r,
..
} = &first_block.coord_type
{
let azimuth_len = azimuth.len();
let r_len = r.len();
let mut vertices = Vec::with_capacity(azimuth_len * r_len);
for azi_idx in 0..azimuth_len {
for r_idx in 0..r_len {
let azi = azimuth.get(azi_idx).unwrap();
let r = r.get(r_idx).unwrap() / r_range[1] as f32;
let dt = first_block_data
.get([self.layer as usize, azi_idx, r_idx])
.unwrap();
vertices.push([r, *azi, *dt]);
}
}
return Ok(vertices);
} else {
return Err(Error::InvalidDataType);
}
}
fn data_info(&self, data: &Data) -> Result<(f32, f32, DataType)> {
pub fn data_info(&self, data: &Data) -> Result<(f32, f32, DataType, usize, f32)> {
let first_block = data.blocks.get(0).unwrap();
if let CoordType::Polar {
azimuth,
r,
r_range,
elevation,
..
} = &first_block.coord_type
{
@ -135,11 +66,29 @@ impl PPI {
sorted_azimuth.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater));
let azi_step = max_step(&sorted_azimuth);
let r_step = min_step(r) / (r_range[1]) as f32;
return Ok((r_step, azi_step, first_block.data_type));
let unvalid = first_block.unvalid_value;
return Ok((
r_step,
azi_step,
first_block.data_type,
elevation.len(),
unvalid,
));
} else {
return Err(Error::InvalidDataType);
}
}
fn init(&mut self, gl: &glow::Context, config: &PPIConfig) {
self.set_conf(gl, config);
self.cmap.attach_with_program(gl, &self.program).unwrap();
let origin = self.program.get_uniform_location(gl, "polar_origin");
unsafe {
gl.uniform_1_f32(origin.as_ref(), 90.0f32.to_radians());
}
}
}
fn min_step(data: &Vec<f32>) -> f32 {
@ -171,86 +120,49 @@ fn max_step(data: &Vec<f32>) -> f32 {
}
impl Graphics for PPI {
const id: &'static str = "PPI";
type Config = PPIConfig;
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
self.program.compile(gl)?;
self.cmap
.as_mut()
.unwrap()
.attach_with_program(gl, &self.program)?;
unsafe {
let vao = gl.create_vertex_array().unwrap();
gl.bind_vertex_array(Some(vao));
let vbo = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
gl.enable_vertex_attrib_array(0);
gl.vertex_attrib_pointer_f32(0, 3, glow::FLOAT, false, 12, 0);
self.vao = Some(vao);
self.vbo = Some(vbo);
gl.bind_vertex_array(None);
}
// let location = self.program.get_uniform_location(gl, "conf");
let origin = self.program.get_uniform_location(gl, "polar_origin");
unsafe {
// gl.uniform_4_f32(location.as_ref(), dpir, dpie, 0.0, 0.0);
gl.uniform_1_f32(origin.as_ref(), 90.0f32.to_radians());
}
Ok(())
}
fn destroy(&mut self, gl: &glow::Context) -> Result<()> {
self.program.destroy(gl);
unsafe {
self.vao.map(|vao| gl.delete_vertex_array(vao));
self.vbo.map(|vbo| gl.delete_buffer(vbo));
self.framebuffer.map(|fb| gl.delete_framebuffer(fb));
self.frametexture.map(|ft| gl.delete_texture(ft));
self.cmap.as_mut().unwrap().destroy(gl);
}
Ok(())
}
fn draw(&self, gl: &glow::Context) -> Result<()> {
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()> {
unsafe {
gl.clear(glow::COLOR_BUFFER_BIT);
gl.use_program(Some(self.program.native_program.unwrap()));
self.cmap
.as_ref()
.unwrap()
.bind_texture(gl, &self.program)?;
if self.framebuffer.is_some() {
gl.bind_framebuffer(glow::FRAMEBUFFER, self.framebuffer);
self.cmap.bind_texture(gl, &self.program)?;
gl.draw_arrays(glow::POINTS, 0, count);
}
Ok(())
}
gl.depth_mask(false);
gl.bind_vertex_array(Some(self.vao.unwrap()));
gl.draw_arrays(glow::POINTS, 0, self.buffer.len() as i32);
gl.depth_mask(true);
if self.framebuffer.is_some() {
gl.framebuffer_texture_2d(
glow::FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
self.frametexture,
0,
);
assert_eq!(
gl.check_framebuffer_status(glow::FRAMEBUFFER),
glow::FRAMEBUFFER_COMPLETE
);
gl.bind_texture(glow::TEXTURE_2D, None);
gl.bind_framebuffer(glow::FRAMEBUFFER, None);
fn program_ref(&self) -> &Program {
&self.program
}
gl.bind_vertex_array(None);
fn program_mut(&mut self) -> &mut Program {
&mut self.program
}
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()> {
self.init(gl, config);
Ok(())
}
fn mount(&mut self, gl: &glow::Context) -> Result<()> {
unsafe {
gl.use_program(self.program.native_program);
}
Ok(())
}
fn unmount(&mut self, gl: &glow::Context) -> Result<()> {
unsafe {
gl.use_program(None);
}
Ok(())
@ -260,68 +172,69 @@ impl Graphics for PPI {
impl AttaWithBuffer for PPI {
type Data = Data;
fn attach_with_buffer(&mut self, gl: &glow::Context, data: &Self::Data) -> Result<()> {
let (rdpi, adpi, data_type) = self.data_info(data)?;
let baked_buffer = self.bake_data(data)?;
self.buffer = baked_buffer;
unsafe {
gl.use_program(self.program.native_program);
self.set_conf(gl, rdpi, adpi);
gl.bind_vertex_array(self.vao);
gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
cast_slice(&self.buffer),
glow::STATIC_DRAW,
);
}
fn bake(
&self,
data: &Self::Data,
config: &<Self as Graphics>::Config,
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
let layer = config.layer;
let first_block = data.blocks.get(0).unwrap();
let first_block_data = first_block.data.view();
if let CoordType::Polar {
r_range,
azimuth,
elevation,
r,
..
} = &first_block.coord_type
{
let azimuth_len = azimuth.len();
let r_len = r.len();
Ok(())
let mut vertices = Vec::with_capacity(azimuth_len * r_len);
let ele = elevation.get(layer).unwrap();
for azi_idx in 0..azimuth_len {
for r_idx in 0..r_len {
let azi = azimuth.get(azi_idx).unwrap();
let r = r.get(r_idx).unwrap() / r_range[1] as f32;
// let r = *r.get(r_idx).unwrap();
let dt = first_block_data.get([layer, azi_idx, r_idx]).unwrap();
vertices.extend([r, *azi, *ele, *dt]);
}
}
impl AttaWithWindow for PPI {
fn attach_with_window(&mut self, gl: &glow::Context) -> Result<imgui::TextureId> {
unsafe {
if self.framebuffer.is_none() {
let framebuffer = gl.create_framebuffer().unwrap();
gl.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer));
let texture = gl.create_texture().unwrap();
gl.bind_texture(glow::TEXTURE_2D, Some(texture));
gl.tex_image_2d(
glow::TEXTURE_2D,
0,
glow::RGB8 as i32,
800,
600,
0,
glow::RGB,
glow::UNSIGNED_BYTE,
None,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MIN_FILTER,
glow::LINEAR as i32,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MAG_FILTER,
glow::LINEAR as i32,
);
gl.bind_framebuffer(glow::FRAMEBUFFER, None);
gl.bind_texture(glow::TEXTURE_2D, None);
self.frametexture = Some(texture);
Ok(imgui::TextureId::from(texture.0.get() as usize))
let len = vertices.len() as i32 / 4;
return Ok((vertices, None, len));
} else {
Ok(imgui::TextureId::from(
self.frametexture.unwrap().0.get() as usize
))
return Err(Error::InvalidDataType);
}
}
fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option<NativeBuffer>) {
unsafe {
let vao = gl.create_vertex_array().unwrap();
gl.bind_vertex_array(Some(vao));
let vbo = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
gl.enable_vertex_attrib_array(0);
gl.vertex_attrib_pointer_f32(0, 4, glow::FLOAT, false, 16, 0);
gl.bind_vertex_array(None);
gl.bind_buffer(glow::ARRAY_BUFFER, None);
(vao, vbo, None)
}
}
}
#[derive(Default, Clone, Debug)]
pub struct PPIConfig {
pub unvalid_value: f32,
pub layer: usize,
pub colors: Vec<[u8; 4]>,
pub color_range: [f32; 2],
pub rdpi: f32,
pub adpi: f32,
pub three_d: bool,
}
mod test {

77
src/graphics/threed.rs Normal file
View File

@ -0,0 +1,77 @@
use std::rc::Rc;
use tracker::track;
use super::transforms::{position::Position, trackball::TrackballModel};
use super::{AttaWithProgram, AttachWithIO};
use crate::camera::{self, Camera};
use crate::errors::*;
use crate::pg::layout_type::ViewPort;
use crate::ui::operation::Projection;
use glow::HasContext;
use nalgebra_glm::{Mat4, Vec3};
#[derive(Clone)]
pub struct Trackball {
trackball: TrackballModel,
}
impl Trackball {
pub fn new(aspect: f32, z_near: f32, z_far: f32, fov: f32) -> Result<Self> {
let trackball = TrackballModel::new(0.0, 90.0, 20.0);
Ok(Self { trackball })
}
}
impl Default for Trackball {
fn default() -> Self {
Self::new(16.0 / 9.0, 0.1, 1000.0, 45.0).unwrap()
}
}
impl AttaWithProgram for Trackball {
fn attach_with_program(
&self,
gl: &glow::Context,
program: &crate::components::Program,
) -> Result<()> {
self.trackball.attach_with_program(gl, program);
Ok(())
}
}
impl AttachWithIO for Trackball {
type State = super::MouseState;
fn attach_with_mouse(
&mut self,
state: &Self::State,
camera: &mut Camera,
projection: &mut Projection,
viewport: &ViewPort,
) -> bool {
match state {
&super::MouseState::Wheel(delta) => {
projection.set_fov((projection.fov() - delta).clamp(15.0, 120.0));
true
}
super::MouseState::Drag { from, delta } => {
self.trackball.drag_to(from[0], from[1], delta[0], delta[1]);
true
}
_ => false,
}
}
fn reset(&mut self) {
self.trackball.set_phi(90.0);
self.trackball.set_theta(0.0);
}
fn init_camera(&self) -> Camera {
Camera::new(
Vec3::new(0.0, 30.0, 10.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 0.0, 0.0),
)
}
}

View File

@ -0,0 +1 @@

View File

@ -1,3 +1,4 @@
pub mod plane;
pub mod polar;
pub mod position;
pub mod trackball;
@ -6,59 +7,56 @@ use crate::components::{Program, Snippet};
use super::AttaWithProgram;
pub struct ChainedTransform {
snippet: Snippet,
chain: Vec<Box<dyn Transform>>,
}
// pub struct ChainedTransform {
// snippet: Snippet,
// chain: Vec<Box<dyn Transform>>,
// }
impl ChainedTransform {
pub fn from<T: Transform + 'static>(transform: T) -> Self {
let snippet = transform.snippet().clone();
// let snippet = transform.snippet().to_owned();
Self {
snippet,
chain: vec![Box::new(transform)],
}
}
// impl ChainedTransform {
// pub fn from<T: Transform + Clone + 'static>(transform: &T) -> Self {
// let snippet = transform.snippet().clone();
// // let snippet = transform.snippet().to_owned();
// Self {
// snippet,
// chain: vec![Box::new(transform.clone())],
// }
// }
pub fn chain(mut self, other: impl Transform + 'static) -> Self {
let new_snippet = self.snippet.clone().chain(other.snippet().to_owned());
self.snippet = new_snippet;
self.chain.push(Box::new(other));
self
}
}
// pub fn chain<P: Transform + 'static + Clone>(mut self, other: &P) -> Self {
// let new_snippet = self.snippet.clone().chain(other.snippet().to_owned());
// self.snippet = new_snippet;
// self.chain.push(Box::new(other.clone()));
// self
// }
// }
impl Transform for ChainedTransform {
fn snippet(&self) -> &Snippet {
&self.snippet
}
}
// impl Transform for ChainedTransform {
// fn snippet(&self) -> &Snippet {
// &self.snippet
// }
// }
impl AttaWithProgram for ChainedTransform {
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> super::Result<()> {
for transform in &self.chain {
transform.attach_with_program(gl, program)?;
}
// impl AttaWithProgram for ChainedTransform {
// fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> super::Result<()> {
// for transform in &self.chain {
// transform.attach_with_program(gl, program)?;
// }
Ok(())
}
}
pub trait Transform: AttaWithProgram {
fn snippet(&self) -> &Snippet;
}
// Ok(())
// }
// }
mod test {
use super::*;
// mod test {
// use super::*;
#[test]
fn test_transform() {
let polar = polar::Polar::new().unwrap();
// let trackball = trackball::Trackball::new().unwrap();
// #[test]
// fn test_transform() {
// let polar = polar::Polar::new().unwrap();
// // let trackball = trackball::Trackball::new().unwrap();
// let chained = ChainedTransform::from(polar).chain(trackball);
// // let chained = ChainedTransform::from(polar).chain(trackball);
// println!("{}", chained.snippet().prepare_code());
// println!("{}", chained.snippet().call(&vec![]).unwrap());
}
}
// // println!("{}", chained.snippet().prepare_code());
// // println!("{}", chained.snippet().call(&vec![]).unwrap());
// }
// }

View File

@ -0,0 +1,125 @@
use crate::components::{CodeType, Program, Snippet};
use crate::errors::Result;
use crate::graphics::{AttaWithProgram, AttachWithIO, MouseKeyboardState, MouseState};
use crate::pg::layout_type::ViewPort;
use crate::ui::operation::Projection;
use glow::HasContext;
use nalgebra::{Matrix4, Quaternion, Translation3, Unit, UnitQuaternion, Vector3};
use nalgebra_glm::vec3;
use serde::de;
use super::trackball::{self, TrackballModel};
use super::viewport;
#[derive(Debug, Clone)]
pub struct PlaneTrans {
trackball: TrackballModel,
}
impl PlaneTrans {
fn translate(
&self,
projection: &Projection,
camera: &mut crate::camera::Camera,
delta: &[f32; 2],
viewport_size: &[f32; 2],
) {
let watch_vec = camera.front();
let fov = projection.fov().to_radians();
let aspect = projection.aspect();
// Z_near
let z_distance = camera.front().norm().abs();
let h = 2.0 * z_distance * (fov / 2.0).tan();
let w = h * aspect;
let move_width = (delta[0] * w) / viewport_size[0];
let move_height = (delta[1] * h) / viewport_size[1];
let up = camera.get_upward();
let right = watch_vec.cross(&up).normalize();
let _move = move_width * right + move_height * up;
camera.set_position(camera.get_position() - _move);
camera.set_center(camera.get_center() - _move);
}
}
impl AttachWithIO for PlaneTrans {
type State = MouseKeyboardState;
fn attach_with_mouse(
&mut self,
state: &Self::State,
camera: &mut crate::camera::Camera,
projection: &mut crate::ui::operation::Projection,
viewport: &ViewPort,
) -> bool {
let viewport_size = viewport.size();
let shift_key = state.keyboard_state[imgui::Key::LeftShift as usize];
let ctrl_key = state.keyboard_state[imgui::Key::LeftCtrl as usize];
match &state.mouse_state {
MouseState::Drag { from, delta } => {
if shift_key {
self.trackball.drag_to(
from[0] - viewport_size[0] / 2.0,
from[1] - viewport_size[1] / 2.0,
0.0,
-delta[1],
);
} else if ctrl_key {
self.trackball.drag_to(
from[0] - viewport_size[0] / 2.0,
from[1] - viewport_size[1] / 2.0,
-delta[0],
0.0,
);
} else if shift_key && ctrl_key {
self.trackball.drag_to(
from[0] - viewport_size[0] / 2.0,
from[1] - viewport_size[1] / 2.0,
-delta[0],
-delta[1],
)
} else {
self.translate(projection, camera, delta, &viewport_size);
}
true
}
MouseState::Wheel(delta) => {
projection.set_fov((projection.fov() - delta).clamp(15.0, 120.0));
true
}
_ => false,
}
}
fn init_camera(&self) -> crate::camera::Camera {
crate::camera::Camera::new(
vec3(0.0, 0.0, 20.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 0.0),
)
}
fn reset(&mut self) {}
}
impl AttaWithProgram for PlaneTrans {
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()> {
unsafe {
let loc = program.get_uniform_location(gl, "trackball_model");
self.trackball.attach_with_program(gl, program)?;
}
Ok(())
}
}
impl Default for PlaneTrans {
fn default() -> Self {
let trackball = TrackballModel::new(0.0, 0.0, 200.0);
Self { trackball }
}
}

View File

@ -4,8 +4,6 @@ use crate::{
graphics::AttaWithProgram,
};
use super::Transform;
pub struct Polar {
snippet: Snippet,
origin: f32,
@ -15,7 +13,7 @@ impl Polar {
pub fn new() -> Result<Self> {
let snippets = Snippet::new(
"polar",
CodeType::<&'static str>::Path("transform/polar.glsl".into()),
CodeType::from_path("transform/polar.glsl"),
true,
Some("forward".to_string()),
)?;
@ -27,12 +25,6 @@ impl Polar {
}
}
impl Transform for Polar {
fn snippet(&self) -> &Snippet {
&self.snippet
}
}
impl AttaWithProgram for Polar {
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()> {
Ok(())

View File

@ -1,9 +1,9 @@
use super::Transform;
use crate::{
components::{CodeType, Program, Snippet},
errors::*,
graphics::AttaWithProgram,
};
#[derive(Debug, Clone)]
pub struct Position {
snippet: Snippet,
}
@ -12,8 +12,8 @@ impl Position {
pub fn new() -> Result<Self> {
let snippets = Snippet::new(
"position",
CodeType::<&'static str>::Path("transform/position.glsl".into()),
true,
CodeType::from_path("transform/position.glsl"),
false,
None,
)?;
@ -21,12 +21,6 @@ impl Position {
}
}
impl Transform for Position {
fn snippet(&self) -> &Snippet {
&self.snippet
}
}
impl AttaWithProgram for Position {
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()> {
Ok(())

View File

@ -2,11 +2,10 @@ use crate::components::{CodeType, Program, Snippet};
use crate::errors::Result;
use crate::graphics::AttaWithProgram;
use super::Transform;
use glow::HasContext;
use nalgebra::{Matrix4, Quaternion, Translation3, Unit, UnitQuaternion, Vector3};
#[derive(Debug, Clone)]
pub struct TrackballModel {
rotation: UnitQuaternion<f32>,
count: usize,
@ -20,13 +19,13 @@ pub struct TrackballModel {
}
impl TrackballModel {
pub fn new(theta: f32, phi: f32) -> Self {
pub fn new(phi: f32, theta: f32, range: f32) -> Self {
let mut trackball = Self {
rotation: UnitQuaternion::identity(),
count: 0,
model: Matrix4::identity(),
renorm_count: 97,
trackball_size: 0.8,
trackball_size: range,
x: 0.0,
y: 0.0,
theta,
@ -36,6 +35,17 @@ impl TrackballModel {
trackball
}
pub fn rotate_z(&mut self, angle: f32) {
let q = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), angle);
self.rotation *= q;
self.count += 1;
if self.count > self.renorm_count {
self.rotation = UnitQuaternion::new_normalize(*self.rotation.clone());
self.count = 0;
}
self.model = self.rotation.to_homogeneous();
}
pub fn drag_to(&mut self, x: f32, y: f32, dx: f32, dy: f32) {
let q = self.rotate(x, y, dx, dy);
self.rotation *= q;
@ -47,11 +57,11 @@ impl TrackballModel {
self.model = self.rotation.to_homogeneous();
}
pub fn model(&self) -> &Matrix4<f32> {
fn model(&self) -> &Matrix4<f32> {
&self.model
}
pub fn theta(&self) -> f32 {
fn theta(&self) -> f32 {
self.theta
}
@ -59,7 +69,7 @@ impl TrackballModel {
self.set_orientation(theta % 360.0, self.phi % 360.0);
}
pub fn phi(&self) -> f32 {
fn phi(&self) -> f32 {
self.phi
}
@ -82,12 +92,12 @@ impl TrackballModel {
self.theta = theta;
self.phi = phi;
let angle = self.theta * (std::f32::consts::PI / 180.0);
let angle = self.theta.to_radians();
let sine = (0.5 * angle).sin();
let xrot =
UnitQuaternion::from_quaternion(Quaternion::new((0.5 * angle).cos(), sine, 0.0, 0.0));
let angle = self.phi * (std::f32::consts::PI / 180.0);
let angle = self.phi.to_radians();
let sine = (0.5 * angle).sin();
let zrot =
UnitQuaternion::from_quaternion(Quaternion::new((0.5 * angle).cos(), 0.0, 0.0, sine));
@ -128,126 +138,44 @@ impl TrackballModel {
}
}
pub struct Trackball {
snippet: Snippet,
// impl Trackball {
// pub fn new() -> Result<Self> {
// let model = TrackballModel::new(0.0, 0.0);
znear: f32,
zfar: f32,
zoom: f32,
aspect: f32,
model: TrackballModel,
// Ok(Self { model })
// }
projection: nalgebra_glm::Mat4x4,
view: nalgebra::Matrix4<f32>,
}
// pub fn on_mouse_drag(&mut self, x: f32, y: f32, dx: f32, dy: f32) {
// self.model.drag_to(x, y, dx, dy);
// }
impl Trackball {
pub fn new() -> Result<Self> {
let snippets = Snippet::new(
"trackball",
CodeType::<&'static str>::Path("transform/trackball.glsl".into()),
true,
None,
)?;
let model = TrackballModel::new(45.0, 45.0);
let aspect = 1.0;
let fovy = 45.0;
let near = 2.0;
let far = 1000.0;
let projection = nalgebra_glm::perspective(aspect, fovy, near, far);
let mut view: Matrix4<f32> = Matrix4::identity();
let distance: f32 = 8.0;
let translation = Translation3::new(0.0, 0.0, -distance.abs());
view = translation.to_homogeneous() * view;
Ok(Self {
snippet: snippets,
znear: near,
zfar: far,
zoom: fovy,
aspect,
model,
projection,
view,
})
}
pub fn on_mouse_drag(
&mut self,
gl: &glow::Context,
program: &mut Program,
x: f32,
y: f32,
dx: f32,
dy: f32,
) {
self.model.drag_to(x, y, dx, dy);
unsafe {
let model = self.snippet.find_symbol("trackball_model").unwrap();
let l = program.get_uniform_location(gl, &model);
gl.uniform_matrix_4_f32_slice(l.as_ref(), false, self.model.model().as_slice());
}
}
pub fn on_mouse_scroll(
&mut self,
gl: &glow::Context,
program: &mut Program,
x: f32,
y: f32,
dz: f32,
) {
self.zoom += dz;
self.projection = nalgebra_glm::perspective(self.aspect, self.zoom, self.znear, self.zfar);
unsafe {
let model = self.snippet.find_symbol("trackball_projection").unwrap();
let l = program.get_uniform_location(gl, &model);
gl.uniform_matrix_4_f32_slice(l.as_ref(), false, self.model.model().as_slice());
}
}
// pub fn model(&self) -> &Matrix4<f32> {
// self.model.model()
// }
// }
impl AttaWithProgram for TrackballModel {
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()> {
unsafe {
let model = self.snippet.find_symbol("trackball_model").unwrap();
let l = program.get_uniform_location(gl, &model);
gl.uniform_matrix_4_f32_slice(l.as_ref(), false, self.model.model().as_slice());
let projection = self.snippet.find_symbol("trackball_projection").unwrap();
let l = program.get_uniform_location(gl, &projection);
gl.uniform_matrix_4_f32_slice(l.as_ref(), false, self.projection.as_slice());
let view = self.snippet.find_symbol("trackball_view").unwrap();
let l = program.get_uniform_location(gl, &view);
gl.uniform_matrix_4_f32_slice(l.as_ref(), false, self.view.as_slice());
let l = program.get_uniform_location(gl, "trackball_model");
gl.uniform_matrix_4_f32_slice(l.as_ref(), false, self.model().as_slice());
}
Ok(())
}
}
impl Transform for Trackball {
fn snippet(&self) -> &Snippet {
&self.snippet
}
}
impl AttaWithProgram for Trackball {
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()> {
self.attach_with_program(gl, program)
}
}
mod test {
use super::*;
#[test]
fn test_trackball() {
let mut trackball = TrackballModel::new(45.0, 45.0, 20.0);
println!("{:?}", trackball.model());
trackball.drag_to(0.0, 10.0, 15.0, 30.0);
println!("{:?}", trackball.model());
// let trackball = Trackball::new().unwrap();
// println!("{}", trackball.snippet);
}

View File

@ -16,7 +16,7 @@ impl Viewport {
pub fn new() -> Result<Self> {
let snippets = Snippet::new(
"viewport",
CodeType::<&'static str>::Path("transform/viewport.glsl".into()),
CodeType::from_path("transform/viewport.glsl"),
true,
None,
)?;

View File

@ -1,83 +1,32 @@
use std::cell::RefCell;
use std::rc::Rc;
use data_loader::Data;
use graphics::colormap::linear::LinearColormap;
use graphics::ppi::PPI;
use graphics::transforms::position::Position;
use graphics::transforms::viewport::Viewport;
use graphics::{AttaWithBuffer, Graphics, AttaWithWindow};
use imgui::Condition;
#![feature(proc_macro_hygiene)]
#![allow(unused)]
mod camera;
mod components;
mod data_loader;
mod errors;
mod font_manager;
mod graphics;
mod pg;
mod setting;
mod shaders;
mod support;
mod ui;
mod utils;
use imgui::*;
mod final_pg;
use pg::App;
use support::supporter::run;
use rust_embed::RustEmbed;
#[derive(RustEmbed)]
#[folder = "resources/"]
pub struct Asset;
use once_cell::sync::Lazy;
static SETTING: Lazy<setting::Setting> = Lazy::new(|| setting::Setting::new());
fn main() {
env_logger::init();
let transform = Position::new().unwrap();
let mut viewport = Viewport::new().unwrap();
viewport.set_global(0.0, 0.0, 600.0, 800.0);
let mut cmap = LinearColormap::new().unwrap();
cmap.set_colors(vec![
[170, 170, 170, 255],
[0, 34, 255, 255],
[1, 160, 246, 255],
[0, 236, 236, 255],
[0, 216, 0, 255],
[1, 144, 0, 255],
[255, 255, 0, 255],
[231, 192, 0, 255],
[255, 144, 0, 255],
[255, 0, 0, 255],
[214, 0, 0, 255],
[192, 0, 0, 255],
[255, 0, 240, 255],
[150, 0, 180, 255],
]);
cmap.set_range(0.0, 70.0);
let mut ppi = PPI::new().unwrap();
ppi.set_transform(&transform);
ppi.set_viewport(&viewport);
ppi.set_colormap(Box::new(cmap));
let ppi = Rc::new(RefCell::new(ppi));
let registers = vec![ppi.clone()];
let registers = registers.into_iter().map(|r| {
r as Rc<RefCell<dyn Graphics>>
}).collect();
support::supporter::init(move |run, ui, window, gl| {
let mut ppi = ppi.borrow_mut();
let texture = ppi.attach_with_window(gl).unwrap();
ui.window("test").build(||{
if ui.button("load") {
let data = Data::from_path(r#"C:\Users\qwin7\Downloads\ZJSXAA_20230113070200_R.dat.gz"#).unwrap();
ppi.attach_with_buffer(gl, &data).unwrap();
}
});
ui.window("Test").size([200.0,400.0], imgui::Condition::FirstUseEver).build(|| {
imgui::Image::new(texture, [200.0, 400.0]).build(ui);
});
},
registers,
);
run(move |gl, helper| App::new(gl, helper).unwrap());
}

110
src/pg/app.rs Normal file
View File

@ -0,0 +1,110 @@
use log::*;
use std::{cell::RefCell, path::PathBuf, rc::Rc};
use crate::{
data_loader::Data,
errors::*,
graphics::{
colormap::linear::LinearColormap, ppi::PPI, threed::Trackball, AttaWithBuffer,
AttaWithProgram, Graphics,
},
ui::{
helper::{self, Helper},
State, GUI,
},
utils::{
cache::{Cache, CachedData},
resources::{
ManagedResource, RcGlBuffer, RcGlFramebuffer, RcGlRcFramebuffer, RcGlRcRenderbuffer,
RcGlRcResource, RcGlRenderbuffer, RcGlResource, RcGlVertexArray, GL,
},
},
};
use glow::HasContext;
use imgui::Ui;
use super::layout_type::{self, Layout};
use super::{ModulePackage, Programs};
use crate::{font_manager::FontManager, graphics::font::Text};
pub struct App<'gl> {
gl: &'gl GL,
context: Context<'gl>,
gui: GUI,
layout: Layout<'gl>,
}
impl<'gl> App<'gl> {
pub fn new(gl: &'gl GL, helper: Helper) -> Result<Self> {
let programs = Programs::new(gl).unwrap();
let mut default_state = State::default();
let mut gui = GUI::new(State::default());
let layout = gui.layout(gl);
let context = Context::new(gl, Cache::new(), helper, programs);
Ok(Self {
gui,
gl,
context,
layout,
})
}
pub fn prepare(&mut self) {
if let Err(e) = self.context.programs.prepare() {
error!("prepare failed: {:?}", e);
}
}
fn gl(&self) -> &GL {
&self.context.gl
}
fn datapool(&mut self) -> &mut Cache<PathBuf, CachedData<Data>> {
&mut self.context.datapool
}
pub fn render<'a>(&'a mut self) {
if let Err(e) = self.layout.launch_render_task(&mut self.context) {
error!("draw_program failed: {:?}", e);
}
}
pub fn render_ui<'a>(&'a mut self, ui: &Ui, window: &winit::window::Window, run: &mut bool) {
*run = self.gui.render(ui, &mut self.context, &mut self.layout);
}
pub fn destroy(&mut self) {
self.context.programs.destroy().unwrap();
}
pub fn resize(&mut self, size: [f32; 2]) {}
}
pub struct Context<'gl> {
pub gl: &'gl GL,
pub helper: Helper,
pub programs: Programs<'gl>,
pub datapool: Cache<PathBuf, CachedData<Data>>,
}
impl<'gl> Context<'gl> {
fn new(
gl: &'gl GL,
datapool: Cache<PathBuf, CachedData<Data>>,
helper: Helper,
programs: Programs<'gl>,
) -> Self {
let context = Context {
helper,
datapool,
programs,
gl,
};
context
}
pub fn load_data<P: Into<PathBuf>>(&mut self, path: P) -> Result<ModulePackage<'gl>> {
let data = self.datapool.get_or_insert(path.into())?;
let module_cursor = self.programs.load_data(data)?;
Ok(module_cursor)
}
}

241
src/pg/layout_type.rs Normal file
View File

@ -0,0 +1,241 @@
use crate::graphics::ty;
use crate::pg::{Context, ModulePackage};
use crate::ui::typ::{LayoutAttach, MainLoadAttach};
use crate::utils::resources::{
ManagedResource, RcGlRcFramebuffer, RcGlRcRenderbuffer, RcGlRcResource, Resource, GL,
};
use crate::{errors::*, main};
use glow::{HasContext, NativeRenderbuffer, NativeTexture};
const RBO_WIDTH: i32 = 3840;
const RBO_HEIGHT: i32 = 2160;
// App Layout Type
macro_rules! impl_layout {
($({$name:ident => $attach:ty}),+ $(,)?) => {
pub enum Layout<'gl> {
$(
$name($attach),
)+
}
impl<'gl> Layout<'gl> {
pub fn launch_render_task(&mut self, mut context: &mut Context<'gl>) -> Result<()>
{
let programs = &mut context.programs;
let gl = programs.gl;
let helper = &mut context.helper;
match self {
$(
Self::$name(typ) => {
typ.render_task(gl, programs, helper)?;
}
)+
}
Ok(())
}
pub fn append_package(&mut self, package: ModulePackage<'gl>) {
match self {
$(
Self::$name(typ) => {
typ.append(package);
}
)+
}
}
}
};
}
pub struct ViewPort<T: Resource = NativeRenderbuffer> {
renderer_size: [f32; 2],
main_fbo: RcGlRcFramebuffer,
_main_rbo: RcGlRcResource<T>,
_main_depth_rbo: RcGlRcRenderbuffer,
}
impl<T: Resource> ViewPort<T> {
pub fn bind(&self, gl: &glow::Context) {
self.main_fbo.bind(glow::FRAMEBUFFER);
unsafe {
gl.viewport(
0,
0,
self.renderer_size[0] as i32,
self.renderer_size[1] as i32,
);
}
}
pub fn unbind(&self) {
self.main_fbo.unbind(glow::FRAMEBUFFER);
}
pub fn set_size(&mut self, size: [f32; 2]) -> bool {
if size != self.renderer_size {
self.renderer_size = size;
true
} else {
false
}
}
pub fn fbo(&self) -> &RcGlRcFramebuffer {
&self.main_fbo
}
pub fn size(&self) -> [f32; 2] {
self.renderer_size
}
}
impl ViewPort<NativeTexture> {
pub fn new(gl: &GL, depth: bool) -> Self {
let main_fbo: RcGlRcFramebuffer = gl.create_resource_rc();
let main_rbo: RcGlRcResource<NativeTexture> = gl.create_resource_rc();
let main_depth_rbo: RcGlRcRenderbuffer = gl.create_resource_rc();
main_fbo.bind(glow::FRAMEBUFFER);
unsafe {
// Color Attachment
gl.active_texture(glow::TEXTURE0);
main_rbo.bind(glow::TEXTURE_2D);
gl.tex_image_2d(
glow::TEXTURE_2D,
0,
glow::RGBA8 as i32,
RBO_WIDTH,
RBO_HEIGHT,
0,
glow::RGBA,
glow::UNSIGNED_BYTE,
None,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MIN_FILTER,
glow::LINEAR as i32,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MAG_FILTER,
glow::LINEAR as i32,
);
gl.framebuffer_texture_2d(
glow::FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
Some(main_rbo.native()),
0,
);
if depth {
// Depth Attachment
main_depth_rbo.bind(glow::RENDERBUFFER);
gl.renderbuffer_storage(
glow::RENDERBUFFER,
glow::DEPTH_COMPONENT24,
RBO_WIDTH,
RBO_HEIGHT,
);
gl.framebuffer_renderbuffer(
glow::FRAMEBUFFER,
glow::DEPTH_ATTACHMENT,
glow::RENDERBUFFER,
Some(main_depth_rbo.native()),
);
main_depth_rbo.unbind(glow::RENDERBUFFER);
gl.enable(glow::DEPTH_TEST);
}
gl.clear_color(0.0, 0.0, 0.0, 0.0);
gl.clear(glow::COLOR_BUFFER_BIT);
// 检查帧缓冲是否完整
if gl.check_framebuffer_status(glow::FRAMEBUFFER) != glow::FRAMEBUFFER_COMPLETE {
panic!("Framebuffer is not complete!");
}
}
main_fbo.unbind(glow::FRAMEBUFFER);
Self {
renderer_size: [0.0, 0.0],
main_fbo,
_main_rbo: main_rbo,
_main_depth_rbo: main_depth_rbo,
}
}
pub fn texture(&self) -> &RcGlRcResource<NativeTexture> {
&self._main_rbo
}
}
impl ViewPort<NativeRenderbuffer> {
pub fn new(gl: &GL, depth: bool) -> Self {
let main_fbo: RcGlRcFramebuffer = gl.create_resource_rc();
let main_rbo: RcGlRcRenderbuffer = gl.create_resource_rc();
let main_depth_rbo: RcGlRcRenderbuffer = gl.create_resource_rc();
main_fbo.bind(glow::FRAMEBUFFER);
unsafe {
// Color Attachment
main_rbo.bind(glow::RENDERBUFFER);
gl.renderbuffer_storage(glow::RENDERBUFFER, glow::RGBA8, RBO_WIDTH, RBO_HEIGHT);
gl.framebuffer_renderbuffer(
glow::FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::RENDERBUFFER,
Some(main_rbo.native()),
);
main_rbo.unbind(glow::RENDERBUFFER);
if depth {
// Depth Attachment
main_depth_rbo.bind(glow::RENDERBUFFER);
gl.renderbuffer_storage(
glow::RENDERBUFFER,
glow::DEPTH_COMPONENT24,
RBO_WIDTH,
RBO_HEIGHT,
);
gl.framebuffer_renderbuffer(
glow::FRAMEBUFFER,
glow::DEPTH_ATTACHMENT,
glow::RENDERBUFFER,
Some(main_depth_rbo.native()),
);
main_depth_rbo.unbind(glow::RENDERBUFFER);
gl.enable(glow::DEPTH_TEST);
}
gl.clear_color(0.0, 0.0, 0.0, 0.0);
gl.clear(glow::COLOR_BUFFER_BIT);
// 检查帧缓冲是否完整
if gl.check_framebuffer_status(glow::FRAMEBUFFER) != glow::FRAMEBUFFER_COMPLETE {
panic!("Framebuffer is not complete!");
}
}
main_fbo.unbind(glow::FRAMEBUFFER);
Self {
renderer_size: [0.0, 0.0],
main_fbo,
_main_rbo: main_rbo,
_main_depth_rbo: main_depth_rbo,
}
}
}
impl_layout!(
{MainLoad => MainLoadAttach<'gl>},
);

187
src/pg/mod.rs Normal file
View File

@ -0,0 +1,187 @@
mod app;
use femtovg::renderer::OpenGl;
use femtovg::Canvas;
use glow::HasContext;
use layout_type::ViewPort;
pub mod layout_type;
mod modules;
use crate::font_manager::FontManager;
use crate::graphics::collections::agg_fast_path::AggFastPath;
use crate::graphics::font::Text;
use crate::graphics::ppi::PPI;
use crate::graphics::threed::Trackball;
use crate::graphics::transforms::plane::PlaneTrans;
use crate::graphics::transforms::viewport;
use crate::graphics::{AttaWithProgram, AttachWithIO};
use crate::ui::operation::Operation;
use crate::ui::typ::LayoutAttach;
use crate::utils::cache::CachedData;
use crate::utils::resources::GL;
use crate::{errors::*, graphics::Graphics};
pub use app::{App, Context};
pub use modules::{Module, ModuleCursor, ModuleData, PPIModule, PPIPackage};
use std::sync::atomic::AtomicUsize;
static MODULE_PACKAGE_ID: AtomicUsize = AtomicUsize::new(0);
pub(super) struct Programs<'gl> {
gl: &'gl GL,
_ppi: PPI,
_text: Text<'gl>,
_line: AggFastPath,
}
impl<'gl> Programs<'gl> {
pub fn new(gl: &'gl GL) -> Result<Self> {
let font_manager = FontManager::new()?;
Ok(Self {
gl,
_ppi: PPI::new()?,
_text: Text::new(gl, font_manager)?,
_line: AggFastPath::new()?,
})
}
pub fn prepare(&mut self) -> Result<()> {
self._ppi.program().compile(&self.gl)?;
self._line.program().compile(&self.gl)?;
self._text.program_mut().compile(&self.gl)?;
Ok(())
}
pub fn ppi<'b>(&'b mut self) -> modules::PPIModule<'b, 'gl> {
modules::PPIModule::new(&self.gl, &mut self._ppi, &mut self._text, &mut self._line).unwrap()
}
pub fn destroy(&mut self) -> Result<()> {
self._ppi.destroy(&self.gl)?;
self._text.destroy(&self.gl)?;
self._line.destroy(&self.gl)?;
Ok(())
}
pub fn load_data(
&mut self,
data: &CachedData<crate::data_loader::Data>,
) -> Result<ModulePackage<'gl>> {
let cdata = data.borrow();
if cdata.blocks.len() == 0 {
return Err(Error::InvalidDataType);
}
let first_block = cdata.blocks.first().unwrap();
match first_block.coord_type {
crate::data_loader::CoordType::Polar { .. } => {
let package: PPIPackage<'gl> = self.ppi().load_data(data)?;
return Ok(package.into());
}
_ => {
unimplemented!("Only polar data is supported for now");
}
}
}
pub fn draw_modules(
&mut self,
modules: &mut ModulePackage<'gl>,
operation: &Operation<PlaneTrans>,
viewport: &ViewPort,
) -> Result<()> {
match &mut modules.modules {
_ModulePackage::PPI(ppi) => {
self.ppi().render(ppi, operation, viewport)?;
}
}
modules.need_update = false;
Ok(())
}
}
pub enum Data<'a> {
Data(&'a crate::data_loader::Data),
Other,
}
macro_rules! impl_module_data {
($({$t:ty => $b: tt}),+) => {
$(
impl ModuleData for $t {
fn to_data(&self) -> Data {
Data::$b(self)
}
}
)+
};
}
macro_rules! impl_module_package {
($({$t:ty => $b: tt}),+) => {
$(
impl<'a> From<$t> for _ModulePackage<'a> {
fn from(t: $t) -> Self {
_ModulePackage::$b(t)
}
}
)+
};
}
impl_module_data!({
crate::data_loader::Data => Data
});
pub struct ModulePackage<'gl> {
id: usize,
pub need_update: bool,
modules: _ModulePackage<'gl>,
}
pub enum _ModulePackage<'gl> {
PPI(PPIPackage<'gl>),
}
impl_module_package!({
PPIPackage<'a> => PPI
});
impl<'gl, T> From<T> for ModulePackage<'gl>
where
T: ModuleCursor + Into<_ModulePackage<'gl>>,
{
fn from(t: T) -> Self {
Self {
id: MODULE_PACKAGE_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst),
modules: t.into(),
need_update: true,
}
}
}
impl PartialEq for ModulePackage<'_> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl ModulePackage<'_> {
pub fn ui_build(&mut self, ui: &imgui::Ui) -> Result<bool> {
Ok(match &mut self.modules {
_ModulePackage::PPI(ppi) => ppi.ui_build(ui),
})
}
pub fn dirty(&mut self) {
self.need_update = true;
}
pub fn helper_task(&self, canvas: &mut Canvas<OpenGl>) -> bool {
match &self.modules {
_ModulePackage::PPI(ppi) => ppi.helper_layer(canvas),
}
}
}

121
src/pg/modules/mod.rs Normal file
View File

@ -0,0 +1,121 @@
use crate::{
graphics::AttachWithIO,
ui::{
operation::{self, Operation},
typ::CameraOP,
},
utils::{
cache::CachedData,
resources::{ManagedResource, RcGlBuffer, RcGlVertexArray},
},
};
use femtovg::{renderer::OpenGl, Canvas};
use glow::{HasContext, NativeBuffer, NativeVertexArray};
mod ppi;
use crate::errors::*;
pub use ppi::{PPIModule, PPIPackage};
use super::{layout_type::ViewPort, Data};
#[derive(Debug, Clone)]
struct Attach<'a> {
gl: &'a glow::Context,
pub vao: RcGlVertexArray<'a>,
pub vbo: RcGlBuffer<'a>,
pub ebo: Option<RcGlBuffer<'a>>,
pub len: i32,
}
impl<'a> Attach<'a> {
fn new(
gl: &'a glow::Context,
vao: NativeVertexArray,
vbo: NativeBuffer,
ebo: Option<NativeBuffer>,
len: Option<i32>,
) -> Self {
Self {
gl,
vao: RcGlVertexArray::new(gl, vao),
vbo: RcGlBuffer::new(gl, vbo),
ebo: ebo.map(|ebo| RcGlBuffer::new(gl, ebo)),
len: len.unwrap_or(0),
}
}
fn bind_self(&self) {
self.vao.bind(glow::VERTEX_ARRAY);
self.vbo.bind(glow::ARRAY_BUFFER);
if let Some(ebo) = self.ebo.as_ref() {
ebo.bind(glow::ELEMENT_ARRAY_BUFFER);
}
}
fn unbind_self(&self) {
self.vao.unbind(glow::VERTEX_ARRAY);
self.vbo.unbind(glow::ARRAY_BUFFER);
if let Some(ebo) = self.ebo.as_ref() {
ebo.unbind(glow::ELEMENT_ARRAY_BUFFER);
}
}
fn bind_data(&mut self, vbo: &Vec<f32>, ebo: Option<&Vec<u32>>, len: i32, usage: u32) {
use bytemuck::cast_slice;
self.vbo.bind(glow::ARRAY_BUFFER);
unsafe {
self.gl
.buffer_data_u8_slice(glow::ARRAY_BUFFER, cast_slice(&vbo), usage);
if let Some(ebo) = ebo {
self.gl.bind_buffer(
glow::ELEMENT_ARRAY_BUFFER,
Some(self.ebo.as_ref().unwrap().native()),
);
self.gl
.buffer_data_u8_slice(glow::ELEMENT_ARRAY_BUFFER, cast_slice(&ebo), usage);
self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
}
}
self.vbo.unbind(glow::ARRAY_BUFFER);
self.len = len;
}
fn len(&self) -> i32 {
self.len
}
}
pub trait Module: Sized {
type Cursor: ModuleCursor;
type Data;
type Operation: AttachWithIO;
fn render(
&mut self,
cursor: &mut Self::Cursor,
operation: &Operation<Self::Operation>,
viewport: &ViewPort,
) -> Result<()>;
fn load_data<'dt>(&self, data: &CachedData<Self::Data>) -> Result<Self::Cursor>;
}
pub trait ModuleData: Sized {
fn to_data(&self) -> Data;
}
pub trait ModuleCursor {
type Module: Module;
type Data: ModuleData;
type Config;
fn set_config<F>(&mut self, f: F)
where
F: FnOnce(&mut Self::Config);
fn ui_build(&mut self, ui: &imgui::Ui) -> bool;
fn helper_layer(&self, canvas: &mut Canvas<OpenGl>) -> bool {
false
}
}

439
src/pg/modules/ppi.rs Normal file
View File

@ -0,0 +1,439 @@
use collections::agg_fast_path::{AggFastPath, AggFastPathConfig, Path};
use core::f32;
use font::{FontConfig, LineStyle, PositionText, TextLine};
use glow::HasContext;
use std::rc::Rc;
use tracker::track;
use transforms::plane::PlaneTrans;
use transforms::viewport;
use crate::font_manager::{FontSize, FontStyle};
use crate::graphics::{font::Text, *};
use crate::pg::layout_type::ViewPort;
use crate::ui::helper_components::colorbar::colorbar;
use crate::ui::operation::{self, Operation};
use crate::SETTING;
use crate::{
data_loader::Data,
errors::*,
font_manager::FontManager,
ui::typ,
utils::{cache::CachedData, resources::ManagedResource},
};
use imgui::VerticalSlider;
use ppi::{PPIConfig, PPI};
use threed::Trackball;
use super::{Attach, Module, ModuleCursor};
pub struct PPIModule<'b, 'a: 'b> {
gl: &'a glow::Context,
ppi_program: &'b mut PPI,
line_program: &'b mut AggFastPath,
text_program: &'b mut Text<'a>,
}
impl<'b, 'a: 'b> PPIModule<'b, 'a> {
pub fn new(
gl: &'a glow::Context,
ppi: &'b mut PPI,
text: &'b mut Text<'a>,
line: &'b mut AggFastPath,
) -> Result<Self> {
let config = PPIConfig::default();
Ok(Self {
gl,
ppi_program: ppi,
text_program: text,
line_program: line,
})
}
fn bind_ppi_pg(
&self,
attach: &mut Attach,
data: &Data,
config: &PPIModuleConfig,
) -> Result<()> {
let (vbo, ebo, len) = self.ppi_program.bake(data, &config.to_ppi_config())?;
attach.bind_data(&vbo, ebo.as_ref(), len, glow::DYNAMIC_DRAW);
Ok(())
}
fn bind_line_pg(
&self,
attach: &mut Attach,
data: &Data,
config: &PPIModuleConfig,
) -> Result<()> {
let raw_config = config.to_line_config();
// Will be changed in the future
let outskirt = 10.0;
let mut paths = vec![];
let range_line_num = config.range_line_num;
let r = outskirt / range_line_num as f32;
let seg_num = 200;
let angle = 2f32 * f32::consts::PI / seg_num as f32;
for range in 1..=range_line_num {
// Create the path
let mut path = Path::new(true);
let r = r * range as f32;
// Draw the circle
for seg in 0..seg_num {
let angle = angle * seg as f32;
let x = (angle.cos() * r) as f32;
let y = (angle.sin() * r) as f32;
path.push([x, y, 0.01]);
}
path.finish();
paths.push(path);
}
let ath_lin_num = config.ath_line_num;
let a = 2.0 * f32::consts::PI / ath_lin_num as f32;
for _a in 0..=ath_lin_num {
let mut path = Path::new(false);
let x = (a * _a as f32).cos() * outskirt;
let y = (a * _a as f32).sin() * outskirt;
path.push([0.0, 0.0, 0.01]);
path.push([x, y, 0.01]);
path.finish();
paths.push(path);
}
if config.vertical_axis {
let mut path = Path::new(false);
path.push([0.0, 0.0, 0.0]);
path.push([0.0, 0.0, outskirt]);
path.finish();
paths.push(path);
}
let (vbo, ebo, len) = self.line_program.bake(&paths, &raw_config)?;
attach.bind_data(&vbo, ebo.as_ref(), len, glow::STATIC_DRAW);
Ok(())
}
fn bind_tick(&self, attach: &mut Attach, data: &Data, config: &PPIModuleConfig) -> Result<()> {
let font_style = config.to_font_config();
match font_style {
FontConfig::Textline(line_style, font_style) => {
let new_text = TextLine::new("Hello,World", Some(font_style), None);
let position_text =
PositionText::new(new_text, [0.0, 0.0, 0.0], font::Anchor::BottomCenter);
let text_pg_config = config.to_font_config();
let (vbo, ebo, len) = self.text_program.bake(&position_text, &text_pg_config)?;
attach.bind_data(&vbo, ebo.as_ref(), len, glow::STATIC_DRAW);
}
}
Ok(())
}
}
impl<'b, 'a: 'b> Module for PPIModule<'b, 'a> {
type Cursor = PPIPackage<'a>;
type Data = Data;
// type Operation = Trackball;
type Operation = PlaneTrans;
fn render<'dt>(
&mut self,
cursor: &mut Self::Cursor,
operation: &Operation<Self::Operation>,
viewport: &ViewPort,
) -> Result<()> {
// Mount PPI Program
self.ppi_program.mount(&self.gl)?;
// Deal with the operation
operation.attach_with_program(&self.gl, self.ppi_program.program());
// PPI Program
let ppi_attach = &mut cursor.ppi_attach;
let data = &cursor.ppi_data.borrow();
let config = &mut cursor.ppi_config;
// Update the config
self.ppi_program
.set_config(&self.gl, &config.to_ppi_config())?;
// if the layer is changed, we need to rebind the data
if config.changed_layer() {
self.bind_ppi_pg(ppi_attach, data, config);
}
if config.changed_range_line_num()
|| config.changed_ath_line_num()
|| config.changed_vertical_axis()
{
self.bind_line_pg(&mut cursor.line_attach, data, config)?;
}
// PPI Draw
ppi_attach.bind_self();
self.ppi_program.draw(&self.gl, ppi_attach.len())?;
ppi_attach.unbind_self();
// Unmount PPI Program
self.ppi_program.unmount(&self.gl)?;
// Mount Line Program
self.line_program.mount(&self.gl)?;
// Deal with the operation
operation.attach_with_program(&self.gl, self.line_program.program());
// Set the viewport, this is important
self.line_program.set_viewport(&self.gl, viewport.size());
// Update the config
self.line_program
.set_config(&self.gl, &config.to_line_config());
// PPI Tick Draw
let attach = &mut cursor.line_attach;
attach.bind_self();
self.line_program.draw(&self.gl, attach.len())?;
attach.unbind_self();
self.line_program.unmount(&self.gl)?;
self.text_program.mount(&self.gl);
let tick_attach = &mut cursor.tick_attach;
// Deal with the operation
operation.attach_with_program(&self.gl, self.text_program.program_mut());
self.text_program.set_viewport(viewport.size());
self.text_program
.set_config(&self.gl, &config.to_font_config());
tick_attach.bind_self();
self.text_program.draw(&self.gl, tick_attach.len())?;
tick_attach.unbind_self();
config.reset();
Ok(())
}
fn load_data<'dt>(&self, data: &CachedData<Self::Data>) -> Result<Self::Cursor> {
let _data = data.borrow();
// Check if the data is valid
if _data.blocks.len() == 0 {
return Err(Error::InvalidDataType);
}
// Init the memory
let (vao, vbo, ebo) = self.ppi_program.init(&self.gl);
let mut ppi_attach = Attach::new(&self.gl, vao, vbo, ebo, None);
// Get the data info
let (r, a, t, max_layer, unvalid) = self.ppi_program.data_info(&_data)?;
// Find the color map
let cmap = SETTING.find(&t);
// Check if the color map is valid
if cmap.is_none() {
return Err(Error::InvalidDataType);
}
let cmap = cmap.unwrap();
// Init the memory for the line program
let (vao, vbo, ebo) = self.line_program.init(&self.gl);
let mut line_attach = Attach::new(&self.gl, vao, vbo, ebo, None);
// Tick Label
let (vao, vbo, ebo) = self.text_program.init(&self.gl);
let mut tick_attach = Attach::new(&self.gl, vao, vbo, ebo, None);
let mut config = PPIModuleConfig::default();
config.rdpi = r;
config.adpi = a;
config.max_layer = max_layer;
config.unvalid_value = unvalid;
config.colors = cmap.color()?;
config.color_range = cmap.value_range();
// Bind the data
self.bind_ppi_pg(&mut ppi_attach, &data.borrow(), &config);
self.bind_line_pg(&mut line_attach, &data.borrow(), &config);
// self.bind_tick(&mut tick_attach, &data.borrow(), &config);
Ok(PPIPackage::new(
config,
ppi_attach,
line_attach,
tick_attach,
data,
))
}
}
pub struct PPIPackage<'gl> {
draw_helper: bool,
ppi_config: PPIModuleConfig,
ppi_attach: Attach<'gl>,
line_attach: Attach<'gl>,
tick_attach: Attach<'gl>,
ppi_data: CachedData<Data>,
}
impl<'gl> PPIPackage<'gl> {
fn new(
ppi_config: PPIModuleConfig,
ppi_attach: Attach<'gl>,
line_attach: Attach<'gl>,
tick_attach: Attach<'gl>,
data: &CachedData<Data>,
) -> Self {
Self {
draw_helper: true,
ppi_config,
ppi_attach,
line_attach,
tick_attach,
ppi_data: Rc::clone(data),
}
}
}
impl<'gl> ModuleCursor for PPIPackage<'gl> {
type Module = PPIModule<'gl, 'gl>;
type Data = Data;
type Config = PPIModuleConfig;
fn set_config<F>(&mut self, f: F)
where
F: FnOnce(&mut Self::Config),
{
f(&mut self.ppi_config);
}
fn ui_build(&mut self, ui: &imgui::Ui) -> bool {
let mut layer = self.ppi_config.layer;
let mut is_three_d = self.ppi_config.is_three_d;
ui.text("PPI Data Config");
ui.slider("Layer", 0, self.ppi_config.max_layer - 1, &mut layer);
ui.checkbox("three d?", &mut is_three_d);
ui.separator();
let mut line_width = self.ppi_config.line_width;
let mut line_antialias = self.ppi_config.line_antialias;
let mut range_line_num = self.ppi_config.range_line_num;
let mut ath_line_num = self.ppi_config.ath_line_num;
ui.text("PPI Line Config");
ui.slider("line width", 0.1, 10.0, &mut line_width);
ui.slider("line ana", 0.1, 10.0, &mut line_antialias);
ui.slider("Range Line Num", 0, 10, &mut range_line_num);
ui.slider("Ath Line Num", 0, 10, &mut ath_line_num);
ui.separator();
let mut value = self.ppi_config.vertical_axis;
ui.checkbox("Axis?", &mut value);
self.ppi_config.set_layer(layer);
self.ppi_config.set_is_three_d(is_three_d);
self.ppi_config.set_line_width(line_width);
self.ppi_config.set_line_antialias(line_antialias);
self.ppi_config.set_range_line_num(range_line_num);
self.ppi_config.set_ath_line_num(ath_line_num);
self.ppi_config.set_vertical_axis(value);
self.ppi_config.changed_any()
}
fn helper_layer(&self, canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>) -> bool {
colorbar(canvas, &self.ppi_config.colors, &vec![]);
true
}
}
#[track]
#[derive(PartialEq)]
pub struct PPIModuleConfig {
pub ticks: bool,
pub line_color: [f32; 4],
pub line_width: f32,
pub line_antialias: f32,
pub range_line_num: usize,
pub ath_line_num: usize,
pub vertical_axis: bool,
pub layer: usize,
pub colors: Vec<[u8; 4]>,
pub color_range: [f32; 2],
pub is_three_d: bool,
pub tick_label_color: [f32; 4],
pub tick_label_size: f32,
#[do_not_track]
pub unvalid_value: f32,
#[do_not_track]
pub max_layer: usize,
#[do_not_track]
pub rdpi: f32,
#[do_not_track]
pub adpi: f32,
}
impl Default for PPIModuleConfig {
fn default() -> Self {
Self {
ticks: true,
line_color: [1.0, 1.0, 1.0, 1.0],
line_width: 1.5,
line_antialias: 0.5,
layer: 0,
vertical_axis: false,
range_line_num: 5,
ath_line_num: 6,
colors: vec![],
color_range: [0.0, 0.0],
is_three_d: true,
unvalid_value: 0.0,
tick_label_color: [1.0, 1.0, 1.0, 1.0],
tick_label_size: 24.0,
max_layer: 0,
rdpi: 0.0,
adpi: 0.0,
tracker: 0,
}
}
}
impl PPIModuleConfig {
fn to_ppi_config(&self) -> PPIConfig {
PPIConfig {
unvalid_value: self.unvalid_value,
color_range: self.color_range,
colors: self.colors.clone(),
layer: self.layer,
rdpi: self.rdpi,
adpi: self.adpi,
three_d: self.is_three_d,
}
}
fn to_line_config(&self) -> AggFastPathConfig {
AggFastPathConfig {
color: self.line_color,
linewidth: self.line_width,
antialias: self.line_antialias,
}
}
fn to_font_config(&self) -> FontConfig {
let line_style = LineStyle::default();
let mut font_style = FontStyle::default();
font_style.size = FontSize::Absolute(self.tick_label_size);
font_style.color = self.tick_label_color;
FontConfig::Textline(line_style, font_style)
}
}

114
src/setting.rs Normal file
View File

@ -0,0 +1,114 @@
use crate::{errors::*, Asset};
use serde::{Deserialize, Serialize};
use std::{env, fs::{read, read_to_string}, io::Write};
use crate::{data_loader::DataType, utils::color_tools::hex_to_rgba_u8};
macro_rules! find_cmap {
($c:ident,$find_on:ident,$({$b:tt => $name:literal}),*) => {
{
let mut cmap = None;
match $c {
$(
$b => {
let find_v = $find_on.iter().find(|cb| cb.type_name == $name).map(|cb| cb);
cmap = find_v;
}
)*
_ => {}
}
cmap
}
};
}
#[derive(Deserialize, Serialize)]
pub struct Setting {
pub cmap: Vec<CB>,
}
impl Setting {
pub fn find(&self, name: &DataType) -> Option<&CB> {
let cmap = &self.cmap;
use DataType::*;
find_cmap!(
name, cmap,
{DBZ => "DBZ"},
{VEl => "VEL"},
{VIL => "VIL"}
)
}
}
impl Setting {
pub fn new() -> Self {
let current_dir = env::current_dir().unwrap();
if !current_dir.join("radar.toml").exists() {
let default_config = Asset::get("radar.toml").unwrap();
let mut folder_path = current_dir.clone();
let mut conf = folder_path.join("radar.toml");
let mut file = std::fs::File::create_new(&conf).unwrap();
file.write_all(&default_config.data).unwrap();
}
let file = read_to_string(current_dir.join("radar.toml")).unwrap();
let setting: Setting = toml::from_str(&file).unwrap();
setting
}
}
#[derive(Deserialize, Serialize)]
pub struct CB {
#[serde(rename = "type")]
pub type_name: String,
pub colors: Vec<String>,
pub levels: Vec<f32>,
}
impl CB {
pub fn value_range(&self) -> [f32; 2] {
let mut range = [0.0, 0.0];
let levels = &self.levels;
range[0] = levels[0];
range[1] = levels[levels.len() - 1];
range
}
pub fn color(&self) -> Result<Vec<[u8; 4]>> {
if self.colors.len() != self.levels.len() - 1 {
return Err(Error::SettingError("Color and level mismatch".to_string()));
}
let mut result = self
.colors
.iter()
.map(|v| hex_to_rgba_u8(v))
.collect::<std::result::Result<Vec<_>, String>>()
.map_err(|v| Error::SettingError(v.to_string()))?;
let mut span = Vec::with_capacity(self.levels.len() - 1);
for idx in 0..self.levels.len() - 1 {
let start = self.levels[idx];
let end = self.levels[idx + 1];
let range = end - start;
span.push(range);
}
let range = self.value_range();
let all_range = range[1] - range[0];
for (level, r) in span.iter().zip(result.iter_mut()) {
r[3] = ((*level / all_range) * 255.0) as u8;
}
Ok(result)
}
}

126
src/shaders/agg_path.rs Normal file
View File

@ -0,0 +1,126 @@
use glsl::syntax::ShaderStage;
use glsl::syntax::TranslationUnit;
use glsl::transpiler::glsl::show_translation_unit;
use glsl_quasiquote::glsl;
use crate::impl_code_piece;
use super::{trackball::Trackball, CodePiece};
pub struct AggPathVertex(ShaderStage);
pub struct AggPathFragment(ShaderStage);
impl AggPathVertex {
pub fn new() -> Self {
let mut transform = Trackball::new().0;
let raw = glsl! {
layout(location = 0) in vec3 prev;
layout(location = 1) in vec3 curr;
layout(location = 2) in vec3 next;
layout(location = 3) in float id;
uniform float antialias;
uniform float thickness;
uniform vec2 viewport;
uniform vec4 color;
out float v_distance;
out vec4 v_color;
void main() {
vec4 ndc_prev = transform(position(prev));
vec4 ndc_curr = transform(position(curr));
vec4 ndc_next = transform(position(next));
// screen space
vec2 screen_prev = viewport * ((ndc_prev.xy/ndc_prev.w) + 1.0)/2.0;
vec2 screen_curr = viewport * ((ndc_curr.xy/ndc_curr.w) + 1.0)/2.0;
vec2 screen_next = viewport * ((ndc_next.xy/ndc_next.w) + 1.0)/2.0;
vec2 P;
float w = thickness/2.0 + antialias;
if (prev.xy == curr.xy) {
vec2 v = normalize(screen_next.xy - screen_curr.xy);
vec2 normal = normalize(vec2(-v.y, v.x));
P = screen_curr.xy + w*normal*id;
} else if (curr.xy == next.xy) {
vec2 v = normalize(screen_curr.xy - screen_prev.xy);
vec2 normal = normalize(vec2(-v.y, v.x));
P = screen_curr.xy + w*normal*id;
} else {
vec2 v0 = normalize(screen_curr.xy - screen_prev.xy);
vec2 v1 = normalize(screen_next.xy - screen_curr.xy);
vec2 normal = normalize(vec2(-v0.y, v0.x));
vec2 tangent = normalize(v0 + v1);
vec2 miter = vec2(-tangent.y, tangent.x);
float l = abs(w/dot(miter,normal));
P = screen_curr.xy + l*miter*sign(id);
}
v_color = color;
if (abs(id) > 1.5) v_color.a = 0.0;
v_distance = w * id;
// Back to NDC coordinates
gl_Position = vec4(2.0*P/viewport-1.0, ndc_curr.z / ndc_curr.w, 1.0);
}
};
transform.extend(raw);
Self(transform)
}
}
impl AggPathFragment {
pub fn new() -> Self {
let raw = glsl! {
uniform float antialias;
uniform float thickness;
in float v_distance;
in vec4 v_color;
out vec4 fragColor;
vec4 stroke(float distance, float linewidth, float antialias, vec4 fg_color)
{
vec4 frag_color;
float t = linewidth/2.0 - antialias;
float signed_distance = distance;
float border_distance = abs(signed_distance) - t;
float alpha = border_distance/antialias;
alpha = exp(-alpha*alpha);
if( border_distance < 0.0 )
frag_color = fg_color;
else
frag_color = vec4(fg_color.rgb, fg_color.a * alpha);
return frag_color;
}
vec4 stroke(float distance, float linewidth, float antialias, vec4 fg_color, vec4 bg_color)
{
return stroke(distance, linewidth, antialias, fg_color);
}
void main() {
if (v_color.a == 0) {discard;}
fragColor = stroke(v_distance, thickness, antialias, v_color);
}
};
Self(raw)
}
}
impl_code_piece!(AggPathVertex, 0);
impl_code_piece!(AggPathFragment, 0);

50
src/shaders/colormap.rs Normal file
View File

@ -0,0 +1,50 @@
use super::CodePiece;
use crate::impl_code_piece;
use glsl::syntax::ShaderStage;
use glsl::syntax::TranslationUnit;
use glsl::transpiler::glsl::show_translation_unit;
use glsl_quasiquote::glsl;
pub struct ColorMap(pub ShaderStage);
impl ColorMap {
pub fn new() -> Self {
let raw = glsl! {
uniform sampler1D colormap;
uniform vec4 colormap_conf;
float find_idx(float ratio) {
float i = 0;
float count = colormap_conf.z - 1.0;
float sum = 0.0;
while (ratio > sum) {
sum += texture(colormap, float(i++) / count).w;
}
return i / count;
}
vec4 linear_colormap(float value)
{
float vmin = colormap_conf.x;
float vmax = colormap_conf.y;
float count = colormap_conf.z - 1.0;
float invalid = colormap_conf.w;
if (abs(value - invalid) < 0.0001) {
return vec4(0.0, 0.0, 0.0, 0.0);
}
float v = clamp((value - vmin), vmin, vmax) / (vmax - vmin);
float idx = find_idx(v);
vec4 result = texture(colormap, idx);
return result;
}
};
Self(raw)
}
}
impl_code_piece!(ColorMap, 0);

213
src/shaders/font.rs Normal file
View File

@ -0,0 +1,213 @@
use super::trackball::Trackball;
use super::CodePiece;
use crate::impl_code_piece;
use glsl::syntax::ShaderStage;
use glsl::syntax::TranslationUnit;
use glsl::transpiler::glsl::show_translation_unit;
use glsl_quasiquote::glsl;
pub struct FontVertex(pub ShaderStage);
pub struct FontFragment(pub ShaderStage);
pub struct FontGeometry(pub ShaderStage);
impl FontVertex {
pub fn new() -> Self {
let raw = glsl! {
// texcoord (left top, right bottom)
layout(location = 0) in vec4 texcoord;
// Relative position (x, y, width, height)
layout(location = 1) in vec4 relative_position;
out VS_OUT {
vec4 texcoord;
vec4 pos;
} vs_out;
void main() {
vs_out.texcoord = texcoord;
vs_out.pos = relative_position;
}
};
Self(raw)
}
}
impl FontGeometry {
pub fn new() -> Self {
let mut transform = Trackball::new().0;
let raw = glsl! {
layout(points) in;
layout(triangle_strip, max_vertices = 4) out;
// Uniforms
uniform vec3 location;
uniform vec3 anchor;
uniform vec2 viewport;
// Texture Shape
uniform vec2 atlas_shape;
out vec2 v_texCoord;
out vec2 UV_coord;
in VS_OUT {
vec4 texcoord;
vec4 pos;
} gs_in[];
void main() {
vec4 pos = gs_in[0].pos;
float x, y, width, hgt;
// Char position and size
x = pos.x;
y = pos.y;
width = pos.z;
hgt = pos.w;
// texcoord
vec2 tex_coord_lt = gs_in[0].texcoord.xy / atlas_shape;
vec2 tex_coord_rb = gs_in[0].texcoord.zw / atlas_shape;
// uv_coord
vec2 uv_coord_lt = gs_in[0].texcoord.xy;
vec2 uv_coord_rb = gs_in[0].texcoord.zw;
// Char Size
vec2 size = vec2(width, hgt);
// Base Text Location
vec3 base_pos = location + anchor;
// base_pos in NDC
vec4 ndc_base_pos = transform(position(base_pos));
// Screen Space
vec2 screen_base_pos = viewport * ((ndc_base_pos.xy / ndc_base_pos.w) + 1.0) / 2.0;
// Billboard Type
vec2 left_top = screen_base_pos + vec2(x, y);
vec2 right_bottom = left_top + size;
vec2 ndc_left_top = (2.0 * left_top / viewport - 1.0);
vec2 ndc_right_bottom = (2.0 * right_bottom / viewport - 1.0);
// Emit vertices
gl_Position = vec4(ndc_left_top.x, ndc_right_bottom.y, ndc_base_pos.z / ndc_base_pos.w, 1.0);
v_texCoord = vec2(tex_coord_lt.x, tex_coord_rb.y);
UV_coord = vec2(uv_coord_lt.x, uv_coord_rb.y);
EmitVertex();
gl_Position = vec4(ndc_right_bottom.xy, ndc_base_pos.z / ndc_base_pos.w, 1.0);
v_texCoord = tex_coord_rb;
UV_coord = uv_coord_rb;
EmitVertex();
gl_Position = vec4(ndc_left_top.xy, ndc_base_pos.z / ndc_base_pos.w, 1.0);
v_texCoord = tex_coord_lt;
UV_coord = uv_coord_lt;
EmitVertex();
gl_Position = vec4(ndc_right_bottom.x, ndc_left_top.y, ndc_base_pos.z / ndc_base_pos.w, 1.0);
v_texCoord = vec2(tex_coord_rb.x, tex_coord_lt.y);
UV_coord = vec2(uv_coord_rb.x, uv_coord_lt.y);
EmitVertex();
EndPrimitive();
}
};
transform.extend(raw);
Self(transform)
}
}
impl FontFragment {
pub fn new() -> Self {
let raw = glsl! {
uniform sampler2D atlas_data;
in vec2 v_texCoord;
in vec2 UV_coord;
uniform vec4 uClipUV;
uniform vec4 uSdfConfig;
uniform int uMode;
uniform vec4 uBorder;
uniform vec4 uStroke;
uniform vec4 uFill;
float getUVScale(vec2 sdfUV) {
float dx = dFdx(sdfUV.x);
float dy = dFdy(sdfUV.y);
return (sqrt(dx * dx + dy * dy) + sqrt(dy * dy + dx * dx)) * 0.5;
}
struct SDF {
float outer;
float inner;
};
vec4 getTexture(vec2 uv) {
return texture(atlas_data, uv);
}
vec4 getMask(vec4 color, vec2 uv, vec2 st) {
return color; // 默认不修改颜色
}
out vec4 fragColor;
void main() {
vec4 fillColor = uFill;
vec4 strokeColor = uStroke;
float scale = getUVScale(UV_coord);
vec4 texture = getTexture(v_texCoord);
float dis = texture.r;
// float sdfRaw = 0.0;
// float mark = 0.0;
// float sdf;
// float sdfRadius = uSdfConfig.x;
// float expand = uBorder.x;
// float bleed = uBorder.y;
// float d = (texture.r - 0.75) * sdfRadius;
// float s = (d + expand / uSdfConfig.y) / scale + 0.5 + bleed;
// sdf = s; // Assuming SDF returns a single float, adjust as necessary
// if (uMode == -2) {
// fillColor = vec4(texture.rgb, fillColor.a);
// }
// // Compute mask based on SDF
// float mask = clamp(sdf, 0.0, 1.0);
// Final color blending logic here
// fragColor = vec4(fillColor.rgb + mark, mask + mark);
fragColor = vec4(dis,dis,dis,dis);
}
};
Self(raw)
}
}
impl_code_piece!(FontVertex, 0);
impl_code_piece!(FontGeometry, 0);
impl_code_piece!(FontFragment, 0);

73
src/shaders/math.rs Normal file
View File

@ -0,0 +1,73 @@
use glsl::syntax::ShaderStage;
use glsl_quasiquote::glsl;
use super::CodePiece;
use glsl::syntax::TranslationUnit;
use glsl::transpiler::glsl::show_translation_unit;
use crate::impl_code_piece;
pub struct Constants {
pub raw: ShaderStage,
}
impl Constants {
pub fn new() -> Constants {
let raw = glsl! {
#ifndef _GLUMPY__CONSTANTS__
#define _GLUMPY__CONSTANTS__
// The base of natural logarithms (e)
const float M_E = 2.71828182845904523536028747135266250;
// The logarithm to base 2 of M_E (log2(e))
const float M_LOG2E = 1.44269504088896340735992468100189214;
// The logarithm to base 10 of M_E (log10(e))
const float M_LOG10E = 0.434294481903251827651128918916605082;
// The natural logarithm of 2 (loge(2))
const float M_LN2 = 0.693147180559945309417232121458176568;
// The natural logarithm of 10 (loge(10))
const float M_LN10 = 2.30258509299404568401799145468436421;
// Pi, the ratio of a circle's circumference to its diameter.
const float M_PI = 3.14159265358979323846264338327950288;
// Pi divided by two (pi/2)
const float M_PI_2 = 1.57079632679489661923132169163975144;
// Pi divided by four (pi/4)
const float M_PI_4 = 0.785398163397448309615660845819875721;
// The reciprocal of pi (1/pi)
const float M_1_PI = 0.318309886183790671537767526745028724;
// Two times the reciprocal of pi (2/pi)
const float M_2_PI = 0.636619772367581343075535053490057448;
// Two times the reciprocal of the square root of pi (2/sqrt(pi))
const float M_2_SQRTPI = 1.12837916709551257389615890312154517;
// The square root of two (sqrt(2))
const float M_SQRT2 = 1.41421356237309504880168872420969808;
// The reciprocal of the square root of two (1/sqrt(2))
const float M_SQRT1_2 = 0.707106781186547524400844362104849039;
// 1 degree in radians
const float degree = 180.0/M_PI;
// 1 radian in degrees
const float radian = M_PI/180.0;
#endif
};
Constants { raw }
}
}
impl_code_piece!(Constants, raw);

99
src/shaders/mod.rs Normal file
View File

@ -0,0 +1,99 @@
pub mod agg_path;
pub mod colormap;
pub mod font;
pub mod math;
pub mod polar;
pub mod ppi;
pub mod trackball;
use glsl::{
syntax::{ShaderStage, TranslationUnit},
transpiler::glsl::{show_struct, show_translation_unit},
};
use glsl_quasiquote::glsl;
use crate::components::Shader;
struct PPI {
vertex: ShaderStage,
}
impl PPI {
fn new() -> Self {
let vertex: ShaderStage = glsl! {
layout(location = 0) in vec3 position;
out float in_value;
void main() {
gl_Position = vec4(position.x, position.y, 0.5, 1.0);
in_value = position.z;
}
};
Self { vertex }
}
}
impl std::fmt::Display for PPI {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
show_translation_unit(f, &self.vertex);
Ok(())
}
}
#[derive(std::fmt::Debug)]
pub struct EmptyPiece(pub TranslationUnit);
pub trait CodePiece: std::fmt::Display {
fn raw(&self) -> &TranslationUnit;
fn include<T: CodePiece>(i: T, raw: TranslationUnit) -> TranslationUnit {
let mut s = i.raw().clone();
s.extend(raw);
s
}
}
impl CodePiece for EmptyPiece {
fn raw(&self) -> &TranslationUnit {
&self.0
}
}
impl std::fmt::Display for EmptyPiece {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ok(())
}
}
#[macro_export]
macro_rules! impl_code_piece {
($t:ty, $c:tt) => {
impl CodePiece for $t {
fn raw(&self) -> &TranslationUnit {
&self.$c
}
}
impl std::fmt::Display for $t {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
show_translation_unit(f, &self.$c);
Ok(())
}
}
};
}
mod test {
use glsl::transpiler::glsl::show_translation_unit;
use super::math::Constants;
use super::polar::PolarTransform;
use super::PPI;
#[test]
fn test() {
let polar = PolarTransform::new();
println!("{}", polar);
}
}

58
src/shaders/polar.rs Normal file
View File

@ -0,0 +1,58 @@
use super::CodePiece;
use crate::impl_code_piece;
use glsl::syntax::ShaderStage;
use glsl::syntax::TranslationUnit;
use glsl::transpiler::glsl::show_translation_unit;
use super::math::Constants;
use glsl_quasiquote::glsl;
pub struct PolarTransform {
pub raw: ShaderStage,
}
impl PolarTransform {
pub fn new() -> PolarTransform {
let raw = glsl! {
uniform float polar_origin;
vec4 forward(float rho, float theta, float z, float w)
{
return vec4(rho * cos(theta + polar_origin),
rho * sin(theta + polar_origin),
rho * sin(z), w);
}
vec4 forward(float x, float y) {return forward(x, y, 0.0, 1.0);}
vec4 forward(float x, float y, float z) {return forward(x, y, z, 1.0);}
vec4 forward(vec2 P) { return forward(P.x, P.y); }
vec4 forward(vec3 P) { return forward(P.x, P.y, P.z, 1.0); }
vec4 forward(vec4 P) { return forward(P.x, P.y, P.z, P.w); }
vec4 inverse(float x, float y, float z, float w)
{
float rho = length(vec2(x,y));
float theta = atan(y,x);
if( theta < 0.0 )
theta = 2.0*M_PI+theta;
return vec4(rho, theta-polar_origin, z, w);
}
vec4 inverse(float x, float y) {return inverse(x,y,0.0,1.0); }
vec4 inverse(float x, float y, float z) {return inverse(x,y,z,1.0); }
vec4 inverse(vec2 P) { return inverse(P.x, P.y, 0.0, 1.0); }
vec4 inverse(vec3 P) { return inverse(P.x, P.y, P.z, 1.0); }
vec4 inverse(vec4 P) { return inverse(P.x, P.y, P.z, P.w); }
};
let mut constant = Constants::new().raw;
constant.extend(raw);
PolarTransform { raw: constant }
}
}
impl_code_piece!(PolarTransform, raw);

147
src/shaders/ppi.rs Normal file
View File

@ -0,0 +1,147 @@
use super::trackball::Trackball;
use super::CodePiece;
use crate::impl_code_piece;
use glsl::syntax::ShaderStage;
use glsl::syntax::TranslationUnit;
use glsl::transpiler::glsl::show_translation_unit;
use glsl_quasiquote::glsl;
pub struct PPIVertex(pub ShaderStage);
pub struct PPIGeom(pub ShaderStage);
pub struct PPIFragment(pub ShaderStage);
impl PPIVertex {
pub fn new() -> Self {
let raw = glsl! {
layout(location = 0) in vec4 position;
out float in_value;
void main() {
gl_Position = vec4(position.x * 10.0, position.y, position.z, 1.0);
in_value = position.w;
}
};
Self(raw)
}
}
impl PPIGeom {
pub fn new() -> Self {
let mut trackball = Trackball::new().0;
let polar = super::polar::PolarTransform::new().raw;
let raw = glsl! {
layout(points) in;
layout(triangle_strip, max_vertices = 4) out;
// conf: Range, Elevation, Resolution, mode
uniform vec4 conf;
in float in_value[];
out float x;
out float y;
out float value;
flat out vec4 vrange;
void main() {
float rdpi = conf.x * 10.0;
float mode = conf.w;
vec4 reso = vec4(rdpi/2.0, conf.y/2.0 * radian, 0.0, 0.0);
vec4 loc;
float c = cos(reso.y);
vec4 po = gl_in[0].gl_Position;
po.y *= radian;
po.z *= radian * mode;
vrange = vec4(po.x - reso.x, po.y - reso.y, po.x + reso.x, po.y + reso.y);
value = in_value[0];
gl_Position = po - reso;
loc = forward(gl_Position);
x = loc.x;
y = loc.y;
gl_Position = transform(loc);
EmitVertex();
gl_Position = po + vec4(reso.x, -reso.y, 0.0, 0.0);
gl_Position.x = gl_Position.x / c;
loc = forward(gl_Position);
x = loc.x;
y = loc.y;
gl_Position = transform(loc);
EmitVertex();
gl_Position = po + vec4(-reso.x, reso.y, 0.0, 0.0);
loc = forward(gl_Position);
x = loc.x;
y = loc.y;
gl_Position = transform(loc);
EmitVertex();
gl_Position = po + reso;
gl_Position.x = gl_Position.x / c;
loc = forward(gl_Position);
x = loc.x;
y = loc.y;
gl_Position = transform(loc);
EmitVertex();
EndPrimitive();
}
};
trackball.extend(polar);
trackball.extend(raw);
Self(trackball)
}
}
impl PPIFragment {
pub fn new() -> Self {
use super::polar::PolarTransform;
let mut include = PolarTransform::new().raw;
let mut colormap = super::colormap::ColorMap::new().0;
let raw = glsl! {
in float x;
in float y;
in float value;
flat in vec4 vrange;
out vec4 FragColor;
void main() {
vec4 inversed = inverse(x, y);
if (inversed.x < vrange.x || inversed.x > vrange.z) {
discard;
}
vec4 result = linear_colormap(value);
if (result.w == 0.0) {
discard;
}
FragColor = result;
}
};
include.extend(colormap);
include.extend(raw);
Self(include)
}
}
impl_code_piece!(PPIVertex, 0);
impl_code_piece!(PPIGeom, 0);
impl_code_piece!(PPIFragment, 0);

70
src/shaders/trackball.rs Normal file
View File

@ -0,0 +1,70 @@
use super::CodePiece;
use glsl::syntax::ShaderStage;
use glsl::syntax::TranslationUnit;
use glsl::transpiler::glsl::show_translation_unit;
use crate::impl_code_piece;
pub struct Trackball(pub ShaderStage);
impl Trackball {
pub fn new() -> Self {
let raw = glsl_quasiquote::glsl! {
uniform mat4 trackball_view;
uniform mat4 trackball_model;
uniform mat4 trackball_projection;
vec4 transform(vec4 position)
{
return trackball_projection
* trackball_view
* trackball_model
* position;
}
vec4 position(float x)
{
return vec4(x, 0.0, 0.0, 1.0);
}
vec4 position(float x, float y)
{
return vec4(x, y, 0.0, 1.0);
}
vec4 position(vec2 xy)
{
return vec4(xy, 0.0, 1.0);
}
vec4 position(float x, float y, float z)
{
return vec4(x, y, z, 1.0);
}
vec4 position(vec3 xyz)
{
return vec4(xyz, 1.0);
}
vec4 position(vec4 xyzw)
{
return xyzw;
}
vec4 position(vec2 xy, float z)
{
return vec4(xy, z, 1.0);
}
vec4 position(float x, vec2 yz)
{
return vec4(x, yz, 1.0);
}
};
Self(raw)
}
}
impl_code_piece!(Trackball, 0);

215
src/support/_s.rs Normal file
View File

@ -0,0 +1,215 @@
use std::num::NonZeroU32;
use glow::HasContext;
use glutin::{
config::ConfigTemplateBuilder,
context::{ContextApi, ContextAttributesBuilder, NotCurrentGlContext, PossiblyCurrentContext},
display::{GetGlDisplay, GlDisplay},
surface::{GlSurface, Surface, SurfaceAttributesBuilder, SwapInterval, WindowSurface},
};
use imgui_winit_support::WinitPlatform;
use raw_window_handle::HasRawWindowHandle;
use winit::{
dpi::LogicalSize,
event_loop::EventLoop,
window::{Window, WindowBuilder},
};
pub fn create_window(
title: &str,
context_api: Option<ContextApi>,
) -> (
EventLoop<()>,
Window,
Surface<WindowSurface>,
PossiblyCurrentContext,
) {
let event_loop = EventLoop::new().unwrap();
let window_builder = WindowBuilder::new()
.with_title(title)
.with_inner_size(LogicalSize::new(1024, 768));
let (window, cfg) = glutin_winit::DisplayBuilder::new()
.with_window_builder(Some(window_builder))
.build(&event_loop, ConfigTemplateBuilder::new(), |mut configs| {
configs.next().unwrap()
})
.expect("Failed to create OpenGL window");
let window = window.unwrap();
let mut context_attribs = ContextAttributesBuilder::new();
if let Some(context_api) = context_api {
context_attribs = context_attribs.with_context_api(context_api);
}
let context_attribs = context_attribs.build(Some(window.raw_window_handle()));
let context = unsafe {
cfg.display()
.create_context(&cfg, &context_attribs)
.expect("Failed to create OpenGL context")
};
let surface_attribs = SurfaceAttributesBuilder::<WindowSurface>::new()
.with_srgb(Some(true))
.build(
window.raw_window_handle(),
NonZeroU32::new(1024).unwrap(),
NonZeroU32::new(768).unwrap(),
);
let surface = unsafe {
cfg.display()
.create_window_surface(&cfg, &surface_attribs)
.expect("Failed to create OpenGL surface")
};
let context = context
.make_current(&surface)
.expect("Failed to make OpenGL context current");
surface
.set_swap_interval(&context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
.expect("Failed to set swap interval");
(event_loop, window, surface, context)
}
pub fn glow_context(context: &PossiblyCurrentContext) -> glow::Context {
unsafe {
glow::Context::from_loader_function_cstr(|s| context.display().get_proc_address(s).cast())
}
}
pub fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) {
let mut imgui_context = imgui::Context::create();
imgui_context.set_ini_filename(None);
let mut winit_platform = WinitPlatform::init(&mut imgui_context);
winit_platform.attach_window(
imgui_context.io_mut(),
window,
imgui_winit_support::HiDpiMode::Rounded,
);
imgui_context
.fonts()
.add_font(&[imgui::FontSource::DefaultFontData { config: None }]);
imgui_context.io_mut().font_global_scale = (1.0 / winit_platform.hidpi_factor()) as f32;
(winit_platform, imgui_context)
}
pub struct Triangler {
pub program: <glow::Context as HasContext>::Program,
pub vertex_array: <glow::Context as HasContext>::VertexArray,
}
impl Triangler {
pub fn new(gl: &glow::Context, shader_header: &str) -> Self {
const VERTEX_SHADER_SOURCE: &str = r#"
const vec2 verts[3] = vec2[3](
vec2(0.5f, 1.0f),
vec2(0.0f, 0.0f),
vec2(1.0f, 0.0f)
);
out vec2 vert;
out vec4 color;
vec4 srgb_to_linear(vec4 srgb_color) {
// Calcuation as documented by OpenGL
vec3 srgb = srgb_color.rgb;
vec3 selector = ceil(srgb - 0.04045);
vec3 less_than_branch = srgb / 12.92;
vec3 greater_than_branch = pow((srgb + 0.055) / 1.055, vec3(2.4));
return vec4(
mix(less_than_branch, greater_than_branch, selector),
srgb_color.a
);
}
void main() {
vert = verts[gl_VertexID];
color = srgb_to_linear(vec4(vert, 0.5, 1.0));
gl_Position = vec4(vert - 0.5, 0.0, 1.0);
}
"#;
const FRAGMENT_SHADER_SOURCE: &str = r#"
in vec2 vert;
in vec4 color;
out vec4 frag_color;
vec4 linear_to_srgb(vec4 linear_color) {
vec3 linear = linear_color.rgb;
vec3 selector = ceil(linear - 0.0031308);
vec3 less_than_branch = linear * 12.92;
vec3 greater_than_branch = pow(linear, vec3(1.0/2.4)) * 1.055 - 0.055;
return vec4(
mix(less_than_branch, greater_than_branch, selector),
linear_color.a
);
}
void main() {
frag_color = linear_to_srgb(color);
}
"#;
let mut shaders = [
(glow::VERTEX_SHADER, VERTEX_SHADER_SOURCE, None),
(glow::FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE, None),
];
unsafe {
let vertex_array = gl
.create_vertex_array()
.expect("Cannot create vertex array");
let program = gl.create_program().expect("Cannot create program");
for (kind, source, handle) in &mut shaders {
let shader = gl.create_shader(*kind).expect("Cannot create shader");
gl.shader_source(shader, &format!("{}\n{}", shader_header, *source));
gl.compile_shader(shader);
if !gl.get_shader_compile_status(shader) {
panic!("{}", gl.get_shader_info_log(shader));
}
gl.attach_shader(program, shader);
*handle = Some(shader);
}
gl.link_program(program);
if !gl.get_program_link_status(program) {
panic!("{}", gl.get_program_info_log(program));
}
for &(_, _, shader) in &shaders {
gl.detach_shader(program, shader.unwrap());
gl.delete_shader(shader.unwrap());
}
Self {
program,
vertex_array,
}
}
}
pub fn render(&self, gl: &glow::Context) {
unsafe {
gl.clear_color(0.05, 0.05, 0.1, 1.0);
gl.clear(glow::COLOR_BUFFER_BIT);
gl.use_program(Some(self.program));
gl.bind_vertex_array(Some(self.vertex_array));
gl.draw_arrays(glow::TRIANGLES, 0, 3);
}
}
pub fn destroy(&self, gl: &glow::Context) {
unsafe {
gl.delete_program(self.program);
gl.delete_vertex_array(self.vertex_array);
}
}
}

View File

@ -1,18 +1,6 @@
use crate::camera::Camera;
use crate::graphics::colormap::linear::LinearColormap;
use crate::graphics::ppi::PPI;
use crate::graphics::transforms::{position, ChainedTransform};
use crate::graphics::{hello, AttaWithBuffer, AttaWithProgram, Graphics};
use crate::{
graphics::{
collections::agg_fast_path::AggFastPath,
transforms::{
polar::Polar, position::Position, trackball::Trackball, viewport::Viewport, Transform,
},
},
utils::Triangler,
};
use cgmath::InnerSpace;
use crate::{graphics::font, pg::App, ui::helper::Helper, utils::resources::GL};
use femtovg::renderer::OpenGl;
use glow::HasContext;
use glutin::{
config::ConfigTemplateBuilder,
@ -20,46 +8,46 @@ use glutin::{
display::{GetGlDisplay, GlDisplay},
surface::{GlSurface, Surface, SurfaceAttributesBuilder, WindowSurface},
};
use imgui::Ui;
use imgui::FontConfig;
use imgui_winit_support::{
winit::{
dpi::{LogicalSize, PhysicalPosition},
dpi::LogicalSize,
event_loop::EventLoop,
window::{Window, WindowBuilder},
},
HiDpiMode, WinitPlatform,
};
use nalgebra_glm::perspective;
use nalgebra_glm::Vec3;
use raw_window_handle::HasRawWindowHandle;
use std::cell::RefCell;
use std::num::NonZeroU32;
use std::rc::Rc;
use std::time::Instant;
pub fn init<FUi>(mut run_ui: FUi, mut register: Vec<Rc<RefCell<dyn Graphics>>>)
pub fn run<F>(mut app: F)
where
FUi: FnMut(&mut bool, &mut Ui, &Window, &glow::Context) + 'static,
F: for<'b> FnMut(&'b GL, Helper) -> App<'b>,
{
// Create Window
let (event_loop, window, surface, context) = create_window();
let (mut winit_platform, mut imgui_context) = imgui_init(&window);
let gl = unsafe {
// OpenGL context
let gl = glow_context(&context);
gl.enable(glow::DEPTH_TEST);
gl.enable(glow::BLEND);
gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
gl
};
// OpenGL renderer from this crate
let mut ig_renderer = imgui_glow_renderer::AutoRenderer::initialize(gl, &mut imgui_context)
.expect("failed to create renderer");
let gl_context = ig_renderer.gl_context().clone();
for r in register.iter_mut() {
r.borrow_mut().compile(&gl_context).unwrap();
}
let gl_app = ig_renderer.gl_context().clone();
let gl_context = ig_renderer.gl_context().clone();
let gl = GL::new(gl_app);
let helper = helper(&gl, &context);
// App instance
let mut app = app(&gl, helper);
let mut run = true;
// Prepare
app.prepare();
let mut last_frame = Instant::now();
@ -86,32 +74,19 @@ where
} => {
// Render your custom scene, note we need to borrow the OpenGL
// context from the `AutoRenderer`, which takes ownership of it.
unsafe {
gl_context.clear(glow::DEPTH_BUFFER_BIT);
gl_context.clear_color(0.0, 0.0, 0.0, 1.0);
gl_context.clear(glow::COLOR_BUFFER_BIT);
gl_context.clear(glow::COLOR_BUFFER_BIT);
}
// Render Offscreen
app.render();
let ui = imgui_context.frame();
app.render_ui(&ui, &window, &mut run);
if ui.is_mouse_pos_valid(ui.io().mouse_pos) {
let mouse_pos = ui.io().mouse_pos;
if ui.is_mouse_dragging(imgui::MouseButton::Right) {
}
}
for r in register.iter_mut() {
r.borrow_mut().draw(&gl_context).unwrap();
}
let mut run = true;
run_ui(&mut run, ui, &window, ig_renderer.gl_context());
if !run {
window_target.exit();
}
winit_platform.prepare_render(ui, &window);
let draw_data = imgui_context.render();
// Render imgui on top of it
@ -140,16 +115,12 @@ where
NonZeroU32::new(new_size.width).unwrap(),
NonZeroU32::new(new_size.height).unwrap(),
);
}
winit_platform.handle_event(imgui_context.io_mut(), &window, &event);
app.resize([new_size.width as f32, new_size.height as f32]);
}
winit::event::Event::LoopExiting => {
for r in register.iter_mut() {
r.borrow_mut().destroy(&gl_context).unwrap();
}
app.destroy();
}
event => {
winit_platform.handle_event(imgui_context.io_mut(), &window, &event);
@ -159,51 +130,6 @@ where
.unwrap()
}
fn mouse_callback(
last_position: &mut Option<PhysicalPosition<f64>>,
current_position: PhysicalPosition<f64>,
sensitivity: f64,
pitch: &mut f64,
yaw: &mut f64,
camera: &mut Camera,
) {
use cgmath::{Deg, Euler, Quaternion};
let xpos = current_position.x;
let ypos = current_position.y;
if last_position.is_none() {
*last_position = Some(PhysicalPosition::new(xpos, ypos));
}
let _last_position = last_position.unwrap();
let mut xoffset = xpos - _last_position.x;
let mut yoffset = _last_position.y - ypos; // reversed since y-coordinates go from bottom to top
*last_position = Some(PhysicalPosition::new(xpos, ypos));
xoffset *= sensitivity;
yoffset *= sensitivity;
*yaw += xoffset;
*pitch += yoffset;
if *pitch > 89.0 {
*pitch = 89.0;
}
if *pitch < -89.0 {
*pitch = -89.0;
}
let euler_deg = Euler::new(Deg(*pitch), Deg(*yaw), Deg(0.0));
let c = Quaternion::from(euler_deg).normalize();
camera.set_front(Vec3::new(c.v.x as f32, c.v.y as f32, c.v.z as f32));
}
fn create_window() -> (
EventLoop<()>,
Window,
@ -214,6 +140,7 @@ fn create_window() -> (
let window_builder = WindowBuilder::new()
.with_title("TEST")
.with_maximized(true)
.with_inner_size(LogicalSize::new(1024, 768));
let (window, cfg) = glutin_winit::DisplayBuilder::new()
@ -259,6 +186,14 @@ fn glow_context(context: &PossiblyCurrentContext) -> glow::Context {
}
}
fn helper<'a>(gl: &'a GL, context: &PossiblyCurrentContext) -> Helper {
let renderer = unsafe {
OpenGl::new_from_function_cstr(|s| context.display().get_proc_address(s).cast()).unwrap()
};
Helper::new(gl, renderer)
}
fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) {
let mut imgui_context = imgui::Context::create();
imgui_context.set_ini_filename(None);
@ -279,11 +214,18 @@ fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) {
winit_platform.attach_window(imgui_context.io_mut(), &window, dpi_mode);
}
let mut font_conf = FontConfig::default();
font_conf.size_pixels = 16.0;
imgui_context
.fonts()
.add_font(&[imgui::FontSource::DefaultFontData { config: None }]);
.add_font(&[imgui::FontSource::DefaultFontData {
config: Some(font_conf),
}]);
imgui_context.io_mut().font_global_scale = (1.0 / winit_platform.hidpi_factor()) as f32;
// println!("imgui global scale: {}", winit_platform.hidpi_factor());
imgui_context.io_mut().font_global_scale = (1.0 / 1.5) as f32;
(winit_platform, imgui_context)
}

52
src/ui/helper.rs Normal file
View File

@ -0,0 +1,52 @@
use crate::{
pg::{layout_type::ViewPort, ModulePackage},
utils::resources::GL,
};
use femtovg::{renderer::OpenGl, Canvas};
use glow::{NativeRenderbuffer, NativeTexture};
pub struct Helper {
canvas: Canvas<OpenGl>,
viewport: ViewPort<NativeTexture>,
}
impl Helper {
pub fn new(gl: &GL, mut renderer: OpenGl) -> Self {
let mut viewport = ViewPort::<NativeTexture>::new(gl, false);
renderer.set_screen_target(Some(viewport.fbo().native()));
let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
viewport.set_size([3840.0, 2160.0]);
canvas.set_size(3840, 2160, 1.0);
Self { canvas, viewport }
}
pub fn canvas(&mut self) -> &mut Canvas<OpenGl> {
&mut self.canvas
}
pub fn set_size(&mut self, size: [f32; 2], dpi: f32) {
self.viewport.set_size([size[0] * dpi, size[1] * dpi]);
self.canvas.set_size(size[0] as u32, size[1] as u32, dpi);
}
pub fn render_task(&mut self, gl: &glow::Context, f: impl FnOnce(&mut Canvas<OpenGl>)) {
self.viewport.bind(gl);
f(&mut self.canvas);
self.canvas.flush();
self.viewport.unbind();
}
pub fn viewport(&self) -> &ViewPort<NativeTexture> {
&self.viewport
}
pub fn draw_modules(&mut self, gl: &glow::Context, modules: &Vec<ModulePackage>) {
self.render_task(gl, |canvas| {
for module in modules {
module.helper_task(canvas);
}
});
}
}

View File

@ -0,0 +1,25 @@
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
pub fn colorbar(canvas: &mut Canvas<OpenGl>, colors: &Vec<[u8; 4]>, ticks: &Vec<String>) {
let width = canvas.width();
let height = canvas.height();
let mut path = Path::new();
path.rect(0.0, 200.0, (width / 2) as f32, 200.0);
let paint = Paint::color(femtovg::Color::from(Color::rgba(255, 0, 0, 255)));
canvas.fill_path(&mut path, &paint);
// for (i, color) in colors.iter().enumerate() {
// let y = i as f32 * 100.0 / colors.len() as f32;
// let y1 = (i + 1) as f32 * 100.0 / colors.len() as f32;
// let color = femtovg::Color::from(Color::rgba(color[0], color[1], color[2], color[3]));
// let paint = Paint::color(color);
// path.rect(20.0, y1, 50.0, 100.0 / colors.len() as f32);
// let paint = Paint::color(color);
// canvas.fill_path(&mut path, &paint);
// }
}

View File

@ -0,0 +1 @@
pub mod colorbar;

53
src/ui/io.rs Normal file
View File

@ -0,0 +1,53 @@
use imgui::Ui;
pub struct MouseIO {
pub position: [f32; 2], // 鼠标当前位置
pub drag_delta: Option<[f32; 2]>, // 拖动开始时的鼠标位置
pub is_dragging: bool, // 是否正在拖动
pub left_button_pressed: bool, // 左键是否被按下
pub right_button_pressed: bool, // 右键是否被按下
pub wheel_delta: f32, // 鼠标滚轮变化值
}
pub struct KeyboardIO {
pub keys: [bool; 652], // 键盘按键状态
}
pub struct IO {
pub mouse: MouseIO,
pub keyboard: KeyboardIO,
}
impl IO {
pub fn new(ui: &Ui) -> Self {
let io = ui.io();
let delta = if ui.is_mouse_dragging(imgui::MouseButton::Left) {
// let delta = ui.mouse_drag_delta_with_threshold(imgui::MouseButton::Left, 5.0);
let delta = ui.mouse_drag_delta_with_button(imgui::MouseButton::Left);
ui.reset_mouse_drag_delta(imgui::MouseButton::Left);
Some(delta)
} else {
None
};
let windows_position = ui.window_pos();
let inner_pad = ui.window_content_region_min();
let position = [
io.mouse_pos[0] - windows_position[0] - inner_pad[0],
io.mouse_pos[1] - windows_position[1] - inner_pad[1],
];
IO {
mouse: MouseIO {
position: position,
drag_delta: delta,
is_dragging: ui.is_mouse_dragging(imgui::MouseButton::Left),
left_button_pressed: io.mouse_down[0],
right_button_pressed: io.mouse_down[1],
wheel_delta: io.mouse_wheel,
},
keyboard: KeyboardIO { keys: io.keys_down },
}
}
}

165
src/ui/layout.rs Normal file
View File

@ -0,0 +1,165 @@
use imgui::{Condition, Ui};
pub struct Layout {
size: [f32; 2],
origin: [f32; 2],
}
impl Layout {
pub(super) fn new() -> Self {
Layout {
size: [0.0, 0.0],
origin: [0.0, 0.0],
}
}
pub(super) fn set_size(&mut self, size: [f32; 2]) {
self.size = size;
}
pub(super) fn set_origin(&mut self, origin: [f32; 2]) {
self.origin = origin;
}
pub(super) fn grid(&self, cols: usize, rows: usize, gap: [f32; 2]) -> GridLayout {
GridLayout::new(self.size, self.origin, cols, rows, gap)
}
}
pub enum Size {
Percent(f32),
Fixed(f32),
Fill,
}
#[macro_export]
macro_rules! size {
($float:literal %) => {
Size::Percent($float)
};
($float:literal px) => {
Size::Fixed($float)
};
(fill) => {
Size::Fill
};
}
pub struct GridLayout {
size: [f32; 2],
origin: [f32; 2],
cols: usize,
rows: usize,
gap: [f32; 2],
cursor: Vec<[f32; 2]>,
widths: Vec<f32>,
hgts: Vec<f32>,
}
impl GridLayout {
fn new(size: [f32; 2], origin: [f32; 2], cols: usize, rows: usize, gap: [f32; 2]) -> Self {
GridLayout {
size,
origin,
cols,
rows,
gap,
cursor: vec![[0.0, 0.0]; cols * rows],
widths: vec![0.0; cols],
hgts: vec![0.0; rows],
}
}
fn build(&mut self, sizes: &[Size], idx: usize) {
let mut start = 0.0;
let s = sizes
.iter()
.filter(|v| !matches!(v, Size::Fill))
.fold(0.0, |start, v| match v {
Size::Percent(p) => start + self.size[idx] * p / 100.0,
Size::Fixed(f) => start + *f,
_ => start,
});
let fill = self.size[idx] - s;
for (i, v) in sizes.iter().enumerate() {
let hgt = match v {
Size::Percent(p) => self.size[idx] * p / 100.0,
Size::Fixed(f) => *f,
Size::Fill => fill,
};
if idx == 0 {
for j in 0..self.rows {
self.cursor[j * self.cols + i][0] = start;
}
self.widths[i] = hgt;
} else {
self.cursor[i * self.cols..(i + 1) * self.cols]
.iter_mut()
.for_each(|v| {
v[1] = start;
});
self.hgts[i] = hgt;
}
start += hgt;
}
}
pub(super) fn set_row_size(&mut self, sizes: &[Size]) {
assert_eq!(sizes.len(), self.rows);
assert!(sizes.iter().filter(|v| matches!(v, Size::Fill)).count() <= 1);
self.build(sizes, 1);
}
pub(super) fn set_col_size(&mut self, sizes: &[Size]) {
assert_eq!(sizes.len(), self.cols);
assert!(sizes.iter().filter(|v| matches!(v, Size::Fill)).count() <= 1);
self.build(sizes, 0);
}
pub(super) fn with(&mut self, row: usize, col: usize, span: [usize; 2]) -> GridBlock {
let curr = self.cursor[row * self.cols + col];
let width: f32 = self.widths.iter().skip(col).take(span[1]).sum();
let hgt: f32 = self.hgts.iter().skip(row).take(span[0]).sum();
GridBlock {
size: [width, hgt],
origin: [curr[0] + self.origin[0], curr[1] + self.origin[1]],
}
}
}
pub struct GridBlock {
size: [f32; 2],
origin: [f32; 2],
}
impl GridBlock {
fn pos(&self, x: f32, y: f32) -> [f32; 2] {
[self.origin[0] + x, self.origin[1] + y]
}
pub fn build<F>(&self, ui: &Ui, f: F)
where
F: FnOnce(&Ui, [f32; 2], [f32; 2]),
{
f(ui, self.size, self.origin);
}
pub fn start_window<'a, Label: AsRef<str>>(
&self,
ui: &'a Ui,
title: Label,
) -> imgui::Window<'a, 'a, Label> {
ui.window(title)
.size(self.size, Condition::Always)
.position(self.origin, Condition::Always)
}
}

105
src/ui/mod.rs Normal file
View File

@ -0,0 +1,105 @@
use std::path::PathBuf;
use log::*;
use crate::data_loader::Data;
use crate::errors::*;
use crate::pg::layout_type::Layout;
use crate::pg::{ModulePackage, Programs};
use crate::utils::cache::Cache;
use crate::utils::resources::{RcGlRcFramebuffer, RcGlRcRenderbuffer, RcGlRcResource, GL};
use glow::HasContext;
use glow::{NativeFramebuffer, NativeRenderbuffer};
use imgui::*;
#[macro_use]
mod layout;
pub mod helper;
pub mod io;
mod state;
pub mod typ;
pub use layout::*;
pub use state::State;
pub mod helper_components;
pub mod operation;
pub struct GUI {
state: State,
layout_manager: layout::Layout,
renderer_size: [f32; 2],
}
impl GUI {
pub fn new(state: State) -> Self {
GUI {
state,
layout_manager: layout::Layout::new(),
renderer_size: [0.0, 0.0],
}
}
pub fn layout<'a, 'gl>(&'a mut self, gl: &'gl GL) -> Layout<'gl> {
self.state.app_type.init_apptype(gl)
}
pub fn render<'b, 'gl>(
&mut self,
ui: &Ui,
context: &'b mut crate::pg::Context<'gl>,
layout_atta: &'b mut Layout<'gl>,
) -> bool {
// Menu bar
self.menu_bar(ui, context, layout_atta);
let menu_bar_height = ui.frame_height();
let display_size = ui.io().display_size;
// Layout Size Reset
self.layout_manager.set_origin([0.0, menu_bar_height]);
self.layout_manager
.set_size([display_size[0], display_size[1] - menu_bar_height]);
// Render
self.state
.app_type
.render(ui, context, &mut self.layout_manager, layout_atta);
true
}
fn menu_bar<'b, 'gl>(
&self,
ui: &Ui,
context: &'b mut crate::pg::Context<'gl>,
layout_atta: &mut Layout<'gl>,
) {
if let Some(top_menu) = ui.begin_main_menu_bar() {
if let Some(menu) = ui.begin_menu("File") {
if ui.menu_item("Open") {
match tinyfiledialogs::open_file_dialog("Open", "password.txt", None) {
Some(file) => match context.load_data(file.clone()) {
Ok(data) => {
info!("Data loaded: {:?}", file);
layout_atta.append_package(data);
}
Err(e) => {
error!("Failed to get or insert data: {:?}", e);
}
},
None => {
error!("No file selected");
}
}
}
if ui.menu_item("Save") {
println!("Save clicked");
}
if ui.menu_item("Exit") {}
menu.end();
}
top_menu.end();
}
}
}

137
src/ui/operation.rs Normal file
View File

@ -0,0 +1,137 @@
use super::typ::CameraOP;
use crate::{
camera::Camera,
components::Program,
graphics::{AttaWithProgram, AttachWithIO},
pg::layout_type::ViewPort,
};
pub struct Operation<T: AttachWithIO> {
camera: Camera,
projection: Projection,
operation: T,
need_update: bool,
}
impl<T: AttachWithIO + AttaWithProgram> Operation<T> {
pub fn new(operation: T, aspect: f32, fov: f32, z_near: f32, z_far: f32) -> Self {
let projection = Projection::new(aspect, fov, z_near, z_far);
Self {
projection,
camera: operation.init_camera(),
operation,
need_update: true,
}
}
pub fn deal_io(&mut self, viewport: &ViewPort, io: &super::io::IO) {
self.need_update = self.operation.attach_with_mouse(
&T::State::from_context(io),
&mut self.camera,
&mut self.projection,
viewport,
);
}
pub fn set_camera(&mut self, f: impl Fn(&mut Camera)) {
f(&mut self.camera);
self.need_update = true;
}
pub fn set_projection(&mut self, f: impl Fn(&mut Projection)) {
f(&mut self.projection);
self.need_update = true;
}
pub fn attach_with_program(&self, gl: &glow::Context, program: &mut Program) {
use glow::HasContext;
unsafe {
let projection = program.get_uniform_location(gl, "trackball_projection");
gl.uniform_matrix_4_f32_slice(projection.as_ref(), false, self.projection.as_slice());
let view = program.get_uniform_location(gl, "trackball_view");
gl.uniform_matrix_4_f32_slice(
view.as_ref(),
false,
self.camera.get_view_matrix().as_slice(),
);
}
self.operation.attach_with_program(gl, program);
}
pub fn is_need_update(&self) -> bool {
self.need_update
}
pub fn reset(&mut self) {
self.operation.reset();
self.need_update = true;
}
pub fn clean(&mut self) {
self.need_update = false;
}
}
pub struct Projection {
fov: f32,
z_near: f32,
z_far: f32,
aspect: f32,
projection: nalgebra_glm::Mat4,
}
impl Projection {
fn new(aspect: f32, fov: f32, z_near: f32, z_far: f32) -> Self {
let projection = nalgebra_glm::perspective(aspect, fov.to_radians(), z_near, z_far);
Self {
aspect,
fov,
z_far,
z_near,
projection,
}
}
fn new_perspective(&self) -> nalgebra_glm::Mat4 {
nalgebra_glm::perspective(self.aspect, self.fov.to_radians(), self.z_near, self.z_far)
}
pub fn set_aspect(&mut self, aspect: f32) {
self.aspect = aspect;
self.projection = self.new_perspective();
}
pub fn set_fov(&mut self, fov: f32) {
self.fov = fov;
self.projection = self.new_perspective();
}
pub fn set_z_near(&mut self, z_near: f32) {
self.z_near = z_near;
self.projection = self.new_perspective();
}
pub fn set_z_far(&mut self, z_far: f32) {
self.z_far = z_far;
self.projection = self.new_perspective();
}
pub fn fov(&self) -> f32 {
self.fov
}
pub fn z_far(&self) -> f32 {
self.z_far
}
pub fn z_near(&self) -> f32 {
self.z_near
}
pub fn aspect(&self) -> f32 {
self.aspect
}
fn as_slice(&self) -> &[f32] {
self.projection.as_slice()
}
}

60
src/ui/state.rs Normal file
View File

@ -0,0 +1,60 @@
use super::typ::{LayoutMod, MainLoad};
use crate::pg::ModulePackage;
use crate::utils::resources::GL;
macro_rules! uit {
($s:ident,$({$b: tt => $exp:ident($($gl: ident),+)},)*) => {
{
match $s {
$(
Self::$b(v) => v.$exp($($gl),+),
)*
_ => {
panic!("UiType not found");
}
}
}
}
}
#[derive(Default)]
pub struct State {
pub example: u32,
pub i32_value: i32,
pub u8_value: u8,
pub f32_value: f32,
pub f64_formatted: f64,
pub array: [u8; 4],
pub render_closable: bool,
pub notify_text: &'static str,
pub app_type: UiType,
}
pub enum UiType {
Mainload(MainLoad),
Other,
}
impl UiType {
pub(super) fn render<'b, 'gl: 'b>(
&mut self,
ui: &imgui::Ui,
context: &mut crate::pg::Context<'gl>,
layout: &mut crate::ui::layout::Layout,
app_typ: &mut crate::pg::layout_type::Layout<'gl>,
) {
let gl = context.gl;
let app_typ = app_typ.into();
uit!(self, {Mainload => render(ui,context,layout,app_typ)},);
}
pub fn init_apptype<'gl>(&mut self, gl: &'gl GL) -> crate::pg::layout_type::Layout<'gl> {
uit!(self, {Mainload => init(gl)},).into()
}
}
impl Default for UiType {
fn default() -> Self {
Self::Mainload(MainLoad::new())
}
}

233
src/ui/typ/main_load.rs Normal file
View File

@ -0,0 +1,233 @@
use std::thread::panicking;
use super::{CameraOP, Layout, LayoutAttach, LayoutMod, Size};
use crate::camera::Camera;
use crate::graphics::threed::Trackball;
use crate::graphics::transforms::plane::PlaneTrans;
use crate::graphics::{AttaWithProgram, AttachWithIO, MouseKeyboardState, MouseState};
use crate::pg::ModulePackage;
use crate::pg::{_ModulePackage, layout_type::ViewPort};
use crate::ui::helper::Helper;
use crate::ui::io::IO;
use crate::ui::operation::Operation;
use crate::utils::resources::GL;
use bytemuck::Contiguous;
use glow::{HasContext, NativeRenderbuffer};
use imgui::{Condition, TextureId, Ui, WindowFlags};
use nalgebra_glm::Vec3;
pub struct MainLoad {}
impl LayoutMod for MainLoad {
type Attach<'gl> = MainLoadAttach<'gl>;
fn render<'b, 'gl>(
&mut self,
ui: &imgui::Ui,
context: &mut crate::pg::Context<'gl>,
layout: &mut Layout,
apptyp: &mut Self::Attach<'gl>,
) where
'gl: 'b,
{
let mut changed = true;
let mut grid = layout.grid(2, 1, [0.0, 0.0]);
grid.set_col_size(&[size!(20.0%), size!(fill)]);
grid.set_row_size(&[size!(fill)]);
let io = ui.io();
let window_flag = WindowFlags::empty()
| WindowFlags::NO_MOVE
| WindowFlags::NO_RESIZE
| WindowFlags::NO_COLLAPSE
| WindowFlags::NO_TITLE_BAR;
grid.with(0, 0, [1, 1])
.start_window(ui, "MainLoad")
.build(|| {
for package in 0..apptyp.packages.len() {
if ui.button(format!("Delete Package {}", package)) {
apptyp.packages.remove(package);
}
}
ui.separator();
for package in apptyp.packages.iter_mut() {
if package.ui_build(ui).unwrap() {
package.dirty();
}
}
ui.separator();
if ui.button("ON TOP") {
apptyp
.operation
.set_camera(|c| c.set_position(Vec3::new(0.0, 0.0, 20.0)));
apptyp.operation.reset();
}
});
grid.with(0, 1, [1, 1])
.build(ui, |ui, window_size, origin| {
let window_size = [window_size[0], window_size[0] / 16.0 * 9.0];
ui.window("Renderer")
.size(window_size, Condition::Always)
.flags(window_flag)
.position(origin, Condition::Always)
.build(|| {
let gl = context.gl.gl_rc();
let scale = ui.io().display_framebuffer_scale;
let pos = ui.cursor_screen_pos();
let dsp_size = ui.io().display_size;
let content_size = ui.content_region_avail();
// Change the size of the viewport
apptyp.main_viewport.set_size(content_size);
// context.helper.set_size(content_size, scale[0]);
let draws = ui.get_window_draw_list();
draws.channels_split(2, |channels| {
channels.set_current(0);
let fbo = apptyp.main_viewport.fbo();
let main_fbo = fbo.native();
draws
.add_callback({
move || unsafe {
let x = pos[0] * scale[0];
let y = (dsp_size[1] - pos[1]) * scale[1];
gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(main_fbo));
gl.blit_framebuffer(
0,
0,
(content_size[0]) as i32,
(content_size[1]) as i32,
x as i32,
y as i32,
(x + content_size[0] * scale[0]) as i32,
(y - content_size[1] * scale[1]) as i32,
glow::COLOR_BUFFER_BIT,
glow::NEAREST,
);
gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None);
}
})
.build();
channels.set_current(1);
let helper = context.helper.viewport();
let texture = helper.texture().native();
let gl = context.gl.gl_rc();
draws
.add_image(
TextureId::new(texture.0.into_integer() as usize),
pos,
[
pos[0] + content_size[0] * scale[0],
pos[1] + content_size[1] * scale[1],
],
)
.build();
});
if ui.is_window_hovered() {
let cio = IO::new(ui);
apptyp.operation.deal_io(&apptyp.main_viewport, &cio);
}
});
});
}
fn init<'gl>(&mut self, gl: &'gl GL) -> Self::Attach<'gl> {
let typ = MainLoadAttach {
main_viewport: ViewPort::<NativeRenderbuffer>::new(gl, true),
packages: Vec::new(),
operation: Operation::new(PlaneTrans::default(), 16.0 / 9.0, 45.0, 0.1, 1000.0),
};
typ
}
}
impl MainLoad {
pub fn new() -> Self {
MainLoad {}
}
}
pub struct MainLoadAttach<'gl> {
operation: Operation<PlaneTrans>,
main_viewport: ViewPort,
packages: Vec<ModulePackage<'gl>>,
}
impl CameraOP for MouseState {
fn from_context(context: &IO) -> Self {
if context.mouse.is_dragging {
Self::Drag {
from: context.mouse.position,
delta: context.mouse.drag_delta.unwrap(),
}
} else {
if context.mouse.wheel_delta != 0.0 {
Self::Wheel(context.mouse.wheel_delta)
} else {
Self::None
}
}
}
}
impl CameraOP for MouseKeyboardState {
fn from_context(context: &IO) -> Self {
Self {
mouse_state: MouseState::from_context(context),
keyboard_state: context.keyboard.keys,
}
}
}
impl<'gl> LayoutAttach<'gl> for MainLoadAttach<'gl> {
fn append(&mut self, package: ModulePackage<'gl>) {
self.packages.push(package);
}
fn render_task(
&mut self,
gl: &glow::Context,
programs: &mut crate::pg::Programs<'gl>,
helper: &mut Helper,
) -> crate::errors::Result<()> {
// helper.draw_modules(gl, &self.packages);
let need_launch =
self.packages.iter().any(|v| v.need_update) || self.operation.is_need_update();
if need_launch {
// Helper task
self.main_viewport.bind(gl);
unsafe {
gl.clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT);
}
for package in self.packages.iter_mut() {
programs.draw_modules(package, &self.operation, &self.main_viewport)?;
}
self.operation.clean();
}
if self.packages.is_empty() {
unsafe {
gl.clear(glow::COLOR_BUFFER_BIT);
}
}
self.main_viewport.unbind();
Ok(())
}
}

81
src/ui/typ/mod.rs Normal file
View File

@ -0,0 +1,81 @@
use std::fmt::Debug;
use super::helper;
use super::io::IO;
use super::layout::{Layout, Size};
mod main_load;
use crate::pg::layout_type::Layout as _Layout;
use crate::pg::{self, ModuleCursor, ModulePackage, Programs};
use crate::utils::resources::GL;
pub use main_load::*;
macro_rules! app_type_into {
($({$t: ty => $b:tt},)+) => {
$(
impl<'gl> From<$t> for _Layout<'gl> {
fn from(t: $t) -> Self {
Self::$b(t)
}
}
impl<'a,'gl> From<&'a _Layout<'gl>> for &'a $t {
fn from(t: &'a _Layout<'gl>) -> Self {
if let _Layout::$b(t) = t {
t
} else {
panic!("_Layout is not {}", stringify!($b));
}
}
}
impl<'a,'gl> From<&'a mut _Layout<'gl>> for &'a mut $t {
fn from(t: &'a mut _Layout<'gl>) -> Self {
if let _Layout::$b(t) = t {
t
} else {
panic!("_Layout is not {}", stringify!($b));
}
}
}
)+
};
}
pub trait LayoutMod {
type Attach<'gl>: Into<_Layout<'gl>> + LayoutAttach<'gl>;
fn render<'dt, 'b, 'gl>(
&mut self,
ui: &imgui::Ui,
context: &mut crate::pg::Context<'gl>,
layout: &mut Layout,
apptyp: &mut Self::Attach<'gl>,
) where
'gl: 'b;
fn init<'dt, 'gl>(&mut self, gl: &'gl GL) -> Self::Attach<'gl>;
}
pub trait LayoutAttach<'gl> {
fn append(&mut self, package: ModulePackage<'gl>);
fn render_task(
&mut self,
gl: &glow::Context,
programs: &mut Programs<'gl>,
helper: &mut helper::Helper,
) -> crate::errors::Result<()>;
}
pub trait CameraOP {
fn from_context(context: &IO) -> Self;
}
impl CameraOP for () {
fn from_context(context: &IO) -> Self {
()
}
}
app_type_into!({
MainLoadAttach<'gl> => MainLoad
},);

61
src/utils/cache.rs Normal file
View File

@ -0,0 +1,61 @@
use crate::errors::*;
use std::{cell::RefCell, hash::Hash, num::NonZeroUsize, rc::Rc};
use lru::LruCache;
use crate::data_loader::Data;
pub struct Cache<K: Hash + Eq, V> {
cache: LruCache<K, V>,
}
impl<K: Hash + Eq, V> Cache<K, V> {
pub fn new() -> Self {
Self {
cache: LruCache::new(NonZeroUsize::new(10).unwrap()),
}
}
pub fn get(&mut self, key: &K) -> Option<&V> {
self.cache.get(key)
}
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
self.cache.get_mut(key)
}
}
impl<K, V> Cache<K, V>
where
K: AsRef<std::path::Path> + Hash + Eq + Clone,
V: CacheData,
{
pub fn insert(&mut self, key: K) -> Result<()> {
let value = V::from_path(&key)?;
self.cache.put(key, value);
Ok(())
}
pub fn get_or_insert(&mut self, key: K) -> Result<&V> {
if !self.cache.contains(&key) {
self.insert(key.clone())?;
}
Ok(self.cache.get(&key).unwrap())
}
}
pub type CachedData<T> = Rc<RefCell<T>>;
pub trait CacheData: Sized {
fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self>;
}
impl CacheData for Data {
fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self> {
Data::from_path(path)
}
}
impl<T: CacheData> CacheData for CachedData<T> {
fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self> {
Ok(Rc::new(RefCell::new(T::from_path(path)?)))
}
}

18
src/utils/color_tools.rs Normal file
View File

@ -0,0 +1,18 @@
pub fn hex_to_rgba_u8(hex: &str) -> Result<[u8; 4], String> {
let hex = hex.trim_start_matches('#');
if hex.len() != 6 && hex.len() != 8 {
return Err("Hex color should be in #RRGGBB or #RRGGBBAA format".to_string());
}
let r = u8::from_str_radix(&hex[0..2], 16).map_err(|e| e.to_string())?;
let g = u8::from_str_radix(&hex[2..4], 16).map_err(|e| e.to_string())?;
let b = u8::from_str_radix(&hex[4..6], 16).map_err(|e| e.to_string())?;
let a = if hex.len() == 8 {
u8::from_str_radix(&hex[6..8], 16).map_err(|e| e.to_string())?
} else {
255 // 默认不透明
};
Ok([r, g, b, a])
}

View File

@ -1,7 +1,10 @@
pub mod color_tools;
mod parser;
pub mod resources;
use include_dir::{include_dir, Dir};
pub use parser::*;
use std::{num::NonZeroU32, path::Path};
pub mod cache;
pub mod geo_tools;
use glow::HasContext;

View File

@ -514,6 +514,9 @@ impl Code {
Code::PreprocessorDirective(Rc::new(RefCell::new(pre)), Box::new(inner))
},
),
map(terminated(parse_struct, pair(space0, char(';'))), |v| {
Code::Struct(Rc::new(RefCell::new(v)))
}),
map(parse_macro, |v| Code::Macro(Rc::new(RefCell::new(v)))),
map(include, |v| Code::Include(Rc::new(RefCell::new(v)))),
map(function, |v| Code::Function(Rc::new(RefCell::new(v)))),
@ -1037,6 +1040,7 @@ fn parse_struct_member_access(input: &str) -> IResult<&str, Expression> {
// parse_struct_pointer_access,
map(hook, Expression::Hook),
parse_array_access,
parse_function_call,
map(identifier, |v| Expression::Variable(v.to_string())),
)),
char('.'),
@ -1244,6 +1248,7 @@ pub struct SnippetCode {
pub includes: Vec<RFC<Include>>,
pub variables: Vec<RFC<ShareVariable>>,
pub functions: Vec<RFC<Function>>,
pub structs: Vec<RFC<Struct>>,
pub preprocessor_directives: HashMap<SnippetPreprocessorKey, Box<SnippetCode>>,
}
@ -1562,6 +1567,14 @@ impl SnippetCode {
})
.collect();
let structs = codes
.iter()
.filter_map(|v| match v {
Code::Struct(v) => Some(v.clone()),
_ => None,
})
.collect();
let mut preprocessor_directives = vec![];
for code in codes.into_iter() {
@ -1579,6 +1592,7 @@ impl SnippetCode {
includes: includes,
variables: variables,
functions: functions,
structs: structs,
preprocessor_directives: preprocessor_directives.into_iter().collect(),
})
}
@ -1838,60 +1852,173 @@ impl SnippetCode {
#[test]
fn test() {
let input = r#"
#include "transform/polar.glsl"
uniform sampler2D atlas_data;
uniform vec2 v_texCoord;
layout(points) in;
layout(triangle_strip, max_vertices = 4) out;
// conf: Elevation, Range, Resolution, 0.0
uniform vec4 conf;
out float eth;
out float rng;
out vec4 vrange;
void main() {
// Resolution
vec4 reso = vec4(conf.x/2.0, conf.y/2.0, 0.0, 0.0);
float c = cos(reso.x);
vec4 po = gl_in[0].gl_Position;
vrange = vec4(po.x - reso.x, po.y - reso.y, po.x + reso.x, po.y + reso.y);
gl_Position = po - reso;
eth = gl_Position.x;
rng = gl_Position.y;
gl_Position = <transform(forward(gl_Position))>;
EmitVertex();
gl_Position = po + vec4(-reso.x, reso.y, 0.0, 0.0);
gl_Position.y = gl_Position.y / c;
eth = gl_Position.x;
rng = gl_Position.y;
gl_Position = <transform(forward(gl_Position))>;
EmitVertex();
gl_Position = po + reso;
gl_Position.y = gl_Position.y / c;
eth = gl_Position.x;
rng = gl_Position.y;
gl_Position = <transform(forward(gl_Position))>;
EmitVertex();
gl_Position = po + vec4(reso.x, -reso.y, 0.0, 0.0);
eth = gl_Position.x;
rng = gl_Position.y;
gl_Position = <transform(forward(gl_Position))>;
EmitVertex();
EndPrimitive();
uniform vec4 uClipUV;
uniform vec2 uSdfUV;
uniform vec4 uSdfConfig;
uniform int uRepeat;
uniform int uMode;
uniform vec4 uShape;
uniform vec4 uRadius;
uniform vec4 uBorder;
uniform vec4 uStroke;
uniform vec4 uFill;
float getUVScale(vec2 sdfUV) {
float dx = dFdx(sdfUV.x);
float dy = dFdy(sdfUV.y);
return (sqrt(dx * dx + dy * dy) + sqrt(dy * dy + dx * dx)) * 0.5;
}
struct SDF {
float outer;
float inner;
};
float getBoxCorner(vec2 xy, vec2 offset) {
vec2 clip = xy - offset;
return min(clip.x, clip.y);
}
float getCircleCorner(vec2 xy, float r) {
vec2 clip = max(vec2(0.0), r - xy);
float neg = min(0.0, max(-xy.x, -xy.y) + r);
return r - length(clip) - neg;
}
SDF getBoxSDF(vec2 box, vec2 uv, float scale) {
vec2 nearest = round(uv);
vec2 xy = (abs(uv - 0.5) - 0.5) * box;
float d1 = max(xy.x, xy.y);
float outer = -d1;
float o = outer / scale;
return SDF(o, o);
}
SDF getBorderBoxSDF(vec2 box, vec4 border, vec2 uv, float scale) {
vec2 nearest = round(uv);
vec2 xy = (abs(uv - 0.5) - 0.5) * box;
float d1 = max(xy.x, xy.y);
float outer = -d1;
vec4 flipUV = vec4(uv, 1.0 - uv);
vec2 tl = flipUV.xy * box;
vec2 tr = flipUV.zy * box;
vec2 bl = flipUV.xw * box;
vec2 br = flipUV.zw * box;
vec4 i4 = vec4(
getBoxCorner(tl, vec2(border.x, border.y)),
getBoxCorner(tr, vec2(border.z, border.y)),
getBoxCorner(bl, vec2(border.x, border.w)),
getBoxCorner(br, vec2(border.z, border.w))
);
float inner = min(min(i4.x, i4.y), min(i4.z, i4.w));
return SDF(outer / scale, inner / scale);
}
SDF getRoundedBorderBoxSDF(vec2 box, vec4 border, vec4 radius, vec2 uv, float scale) {
vec4 flipUV = vec4(uv, 1.0 - uv);
vec2 tl = flipUV.xy * box;
vec2 tr = flipUV.zy * box;
vec2 bl = flipUV.xw * box;
vec2 br = flipUV.zw * box;
vec4 o4 = vec4(
getCircleCorner(tl, radius.x),
getCircleCorner(tr, radius.y),
getCircleCorner(bl, radius.w),
getCircleCorner(br, radius.z)
);
float outer = min(min(o4.x, o4.y), min(o4.z, o4.w));
tl = tl - border.xy;
tr = tl - border.zy;
bl = tl - border.xw;
br = tl - border.zw;
vec4 innerRadius = max(
vec4(0.0),
radius - vec4(
max(border.x, border.y),
max(border.z, border.y),
max(border.x, border.w),
max(border.z, border.w)
)
);
vec4 i4 = vec4(
getCircleCorner(tl, innerRadius.x),
getCircleCorner(tr, innerRadius.y),
getCircleCorner(bl, innerRadius.w),
getCircleCorner(br, innerRadius.z)
);
float inner = min(min(i4.x, i4.y), min(i4.z, i4.w));
return SDF(outer / scale, inner / scale);
}
vec4 getTexture(vec2 uv) {
return texture(uTexture, uv);
}
vec4 getMask(vec4 color, vec2 uv, vec2 st) {
return color; // 默认不修改颜色
}
out vec4 fragColor;
void main() {
vec4 fillColor = uFill;
vec4 strokeColor = uStroke;
float scale = getUVScale(uSdfUV);
vec4 texture = getTexture(uTextureUV);
float sdfRaw = 0.0;
float mark = 0.0;
if (uMode == -1 || uMode == -2) {
float sdfRadius = uSdfConfig.x;
float expand = uBorder.x;
float bleed = uBorder.y;
float d = (texture.a - 0.75) * sdfRadius;
float s = (d + expand / uSdfConfig.y) / scale + 0.5 + bleed;
float sdf = s; // Assuming SDF returns a single float, adjust as necessary
if (uMode == -2) {
fillColor = vec4(texture.rgb, fillColor.a);
}
} else {
// More complex logic for other modes
if (texture.a > 0.0) {
if (((uRepeat == 0 || uRepeat == 1) && (uTextureUV.x < 0.0 || uTextureUV.x > 1.0)) ||
((uRepeat == 0 || uRepeat == 2) && (uTextureUV.y < 0.0 || uTextureUV.y > 1.0))) {
texture.a = 0.0;
}
if (texture.a > 0.0) {
fillColor = vec4(mix(fillColor.rgb * (1.0 - texture.a), texture.rgb, texture.a), mix(fillColor.a, 1.0, texture.a));
}
} else {
fillColor = fillColor; // Use premultiply function if needed
}
}
if (gl_FragCoord.x < uClipUV.x || gl_FragCoord.y < uClipUV.y || gl_FragCoord.x > uClipUV.z || gl_FragCoord.y > uClipUV.w) {
discard;
}
// Compute mask based on SDF
float mask = clamp(sdf, 0.0, 1.0);
// Final color blending logic here
fragColor = vec4(fillColor.rgb + mark, fillColor.a * mask + mark); // Simplified for clarity
}
"#;
let result = CodeBlock::new(input).unwrap();
@ -1974,14 +2101,48 @@ fn test_shared_variable() {
#[test]
fn test_function() {
let input = r#"
float norm_rad(float rad) {
float result = mod(angle, M_2_PI);
if(result < 0.0) {
result = M_2_PI + result;
SDF getRoundedBorderBoxSDF(vec2 box, vec4 border, vec4 radius, vec2 uv, float scale) {
vec4 flipUV = vec4(uv, 1.0 - uv);
vec2 tl = flipUV.xy * box;
vec2 tr = flipUV.zy * box;
vec2 bl = flipUV.xw * box;
vec2 br = flipUV.zw * box;
vec4 o4 = vec4(
getCircleCorner(tl, radius.x),
getCircleCorner(tr, radius.y),
getCircleCorner(bl, radius.w),
getCircleCorner(br, radius.z)
);
float outer = min(min(o4.x, o4.y), min(o4.z, o4.w));
tl = tl - border.xy;
tr = tl - border.zy;
bl = tl - border.xw;
br = tl - border.zw;
vec4 innerRadius = max(
vec4(0.0),
radius - vec4(
max(border.x, border.y),
max(border.z, border.y),
max(border.x, border.w),
max(border.z, border.w)
)
);
vec4 i4 = vec4(
getCircleCorner(tl, innerRadius.x),
getCircleCorner(tr, innerRadius.y),
getCircleCorner(bl, innerRadius.w),
getCircleCorner(br, innerRadius.z)
);
float inner = min(min(i4.x, i4.y), min(i4.z, i4.w));
return SDF(outer / scale, inner / scale);
}
return result;
}
"#;

314
src/utils/resources.rs Normal file
View File

@ -0,0 +1,314 @@
use glow::{
HasContext, NativeBuffer, NativeFramebuffer, NativeRenderbuffer, NativeTexture,
NativeVertexArray,
};
use log::info;
use std::{borrow::Borrow, ops::Deref, rc::Rc};
pub type RcGlFramebuffer<'a> = RcGlResource<'a, NativeFramebuffer>;
pub type RcGlRenderbuffer<'a> = RcGlResource<'a, NativeRenderbuffer>;
pub type RcGlRcFramebuffer = RcGlRcResource<NativeFramebuffer>;
pub type RcGlRcRenderbuffer = RcGlRcResource<NativeRenderbuffer>;
pub type RcGlTexture<'a> = RcGlResource<'a, NativeTexture>;
pub type RcGlVertexArray<'a> = RcGlResource<'a, NativeVertexArray>;
pub type RcGlBuffer<'a> = RcGlResource<'a, NativeBuffer>;
// pub type RcGlResource<'a, T> = Rc<GlResource<'a, T>>;
pub trait ManagedResource {
fn bind(&self, target: u32);
fn unbind(&self, target: u32);
}
#[derive(Debug, Clone)]
pub struct RcGlResource<'a, T: Resource>(Rc<GlResource<'a, T>>);
#[derive(Clone)]
pub struct RcGlRcResource<T: Resource>(Rc<GlRcResource<T>>);
impl<'a, T: Resource> RcGlResource<'a, T> {
pub fn new(gl: &'a glow::Context, resource: T) -> Self {
Self(Rc::new(GlResource::new(resource, gl)))
}
pub fn native(&self) -> T {
self.0.resource.clone()
}
}
impl<'a, T> From<&'a GlRcResource<T>> for GlResource<'a, T>
where
T: Resource,
{
fn from(value: &'a GlRcResource<T>) -> Self {
GlResource::new(value.resource.clone(), &value.gl)
}
}
impl<'a, T: Resource> Deref for RcGlResource<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl<T: Resource> RcGlRcResource<T> {
pub fn new(gl: Rc<glow::Context>, resource: T) -> Self {
Self(Rc::new(GlRcResource::new(resource, gl)))
}
pub fn native(&self) -> T {
self.0.resource.clone()
}
}
impl<T: Resource> Deref for RcGlRcResource<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
pub trait Resource: Clone + std::fmt::Debug {
fn drop_self(&self, gl: &glow::Context);
fn create(gl: &glow::Context) -> Self;
fn bind(&self, gl: &glow::Context, target: u32);
fn unbind(&self, gl: &glow::Context, target: u32);
}
impl Resource for NativeVertexArray {
fn drop_self(&self, gl: &glow::Context) {
unsafe {
gl.delete_vertex_array(*self);
}
}
fn create(gl: &glow::Context) -> Self {
unsafe { gl.create_vertex_array().unwrap() }
}
fn bind(&self, gl: &glow::Context, target: u32) {
unsafe {
gl.bind_vertex_array(Some(*self));
}
}
fn unbind(&self, gl: &glow::Context, target: u32) {
unsafe {
gl.bind_vertex_array(Some(*self));
}
}
}
impl Resource for NativeBuffer {
fn drop_self(&self, gl: &glow::Context) {
unsafe {
gl.delete_buffer(*self);
}
}
fn create(gl: &glow::Context) -> Self {
unsafe { gl.create_buffer().unwrap() }
}
fn bind(&self, gl: &glow::Context, target: u32) {
unsafe {
gl.bind_buffer(target, Some(*self));
}
}
fn unbind(&self, gl: &glow::Context, target: u32) {
unsafe {
gl.bind_buffer(target, None);
}
}
}
impl Resource for NativeFramebuffer {
fn drop_self(&self, gl: &glow::Context) {
unsafe {
gl.delete_framebuffer(*self);
}
}
fn create(gl: &glow::Context) -> Self {
unsafe { gl.create_framebuffer().unwrap() }
}
fn bind(&self, gl: &glow::Context, target: u32) {
unsafe {
gl.bind_framebuffer(target, Some(*self));
}
}
fn unbind(&self, gl: &glow::Context, target: u32) {
unsafe {
gl.bind_framebuffer(target, None);
}
}
}
impl Resource for NativeRenderbuffer {
fn drop_self(&self, gl: &glow::Context) {
unsafe {
gl.delete_renderbuffer(*self);
}
}
fn create(gl: &glow::Context) -> Self {
unsafe { gl.create_renderbuffer().unwrap() }
}
fn bind(&self, gl: &glow::Context, target: u32) {
unsafe {
gl.bind_renderbuffer(target, Some(*self));
}
}
fn unbind(&self, gl: &glow::Context, target: u32) {
unsafe {
gl.bind_renderbuffer(target, None);
}
}
}
impl Resource for NativeTexture {
fn drop_self(&self, gl: &glow::Context) {
unsafe {
gl.delete_texture(*self);
}
}
fn create(gl: &glow::Context) -> Self {
unsafe { gl.create_texture().unwrap() }
}
fn bind(&self, gl: &glow::Context, target: u32) {
unsafe {
gl.bind_texture(target, Some(*self));
}
}
fn unbind(&self, gl: &glow::Context, target: u32) {
unsafe {
gl.bind_texture(target, None);
}
}
}
#[derive(Debug)]
struct GlResource<'a, T: Resource> {
gl: &'a glow::Context,
resource: T,
}
struct GlRcResource<T: Resource> {
gl: Rc<glow::Context>,
resource: T,
}
impl<T: Resource> Deref for GlRcResource<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.resource
}
}
impl<'a, T> AsRef<GlResource<'a, T>> for RcGlResource<'a, T>
where
T: Resource,
{
fn as_ref(&self) -> &GlResource<'a, T> {
&self.0
}
}
impl<T: Resource> GlRcResource<T> {
pub fn new(resource: T, gl: Rc<glow::Context>) -> Self {
Self { resource, gl }
}
}
impl<T: Resource> Drop for GlRcResource<T> {
fn drop(&mut self) {
self.resource.drop_self(&*self.gl);
info!("Dropping resource: {:?}", self.resource);
}
}
impl<T: Resource> Deref for GlResource<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.resource
}
}
impl<'a, T: Resource> GlResource<'a, T> {
pub fn new(resource: T, gl: &'a glow::Context) -> Self {
Self { resource, gl }
}
}
impl<T: Resource> Drop for GlResource<'_, T> {
fn drop(&mut self) {
self.resource.drop_self(self.gl);
info!("Dropping resource: {:?}", self.resource);
}
}
impl<'a> RcGlResource<'a, NativeTexture> {
pub fn native2imguiid(&self) -> imgui::TextureId {
imgui::TextureId::new(self.native().0.get() as usize)
}
}
#[derive(Debug, Clone)]
pub struct GL {
gl: Rc<glow::Context>,
}
impl GL {
pub fn new(gl: Rc<glow::Context>) -> Self {
Self { gl }
}
pub fn gl<'a>(&'a self) -> &'a glow::Context {
&self.gl
}
pub fn gl_rc(&self) -> Rc<glow::Context> {
Rc::clone(&self.gl)
}
pub fn create_resource<'a, T: Resource>(&'a self) -> RcGlResource<'a, T> {
RcGlResource::new(self.gl(), T::create(self.gl()))
}
pub fn create_resource_rc<T: Resource>(&self) -> RcGlRcResource<T> {
RcGlRcResource::new(self.gl_rc(), T::create(self.gl()))
}
}
impl Deref for GL {
type Target = glow::Context;
fn deref(&self) -> &Self::Target {
&self.gl
}
}
impl<T: Resource> ManagedResource for RcGlResource<'_, T> {
fn bind(&self, target: u32) {
self.0.resource.bind(self.0.gl, target);
}
fn unbind(&self, target: u32) {
self.0.resource.unbind(&self.0.gl, target);
}
}
impl<T: Resource> ManagedResource for RcGlRcResource<T> {
fn bind(&self, target: u32) {
self.0.resource.bind(&self.0.gl, target);
}
fn unbind(&self, target: u32) {
self.0.resource.unbind(&self.0.gl, target);
}
}

138
test.wgsl Normal file
View File

@ -0,0 +1,138 @@
use '@use-gpu/wgsl/fragment/sdf-2d'::{ SDF, getUVScale, getBoxSDF, getBorderBoxSDF, getRoundedBorderBoxSDF };
use '@use-gpu/wgsl/use/color'::{ premultiply };
@optional @link fn getTexture(uv: vec2<f32>) -> vec4<f32> { return vec4<f32>(0.0, 0.0, 0.0, 0.0); };
@optional @link fn getMask(color: vec4<f32>, uv: vec4<f32>, st: vec4<f32>) -> vec4<f32> { return color; }
@export fn getSDFRectangleFragment(
uv: vec2<f32>,
textureUV: vec2<f32>,
textureST: vec2<f32>,
clipUV: vec4<f32>,
sdfUV: vec2<f32>,
sdfConfig: vec4<f32>,
repeat: i32,
mode: i32,
shape: vec4<f32>,
radius: vec4<f32>,
border: vec4<f32>,
stroke: vec4<f32>,
fill: vec4<f32>,
) -> vec4<f32> {
var fillColor = fill;
var strokeColor = stroke;
var scale = getUVScale(sdfUV);
var sdf: SDF;
var texture = getTexture(textureUV);
var sdfRaw = 0.0;
var mark = 0.0;
if (mode == -1 || mode == -2) {
// SDF Glyph
let sdfRadius = sdfConfig.x;
var expand = border.x;
var bleed = border.y;
var d = (texture.a - 0.75) * sdfRadius;
var s = (d + expand / sdfConfig.y) / scale + 0.5 + bleed;
sdf = SDF(s, s);
if (mode == -2) {
fillColor = vec4<f32>(texture.rgb, fillColor.a);
}
}
else {
// Textured box
if (texture.a > 0.0) {
if (
((repeat == 0 || repeat == 1) && (textureUV.x < 0.0 || textureUV.x > 1.0)) ||
((repeat == 0 || repeat == 2) && (textureUV.y < 0.0 || textureUV.y > 1.0))
) {
texture.a = 0.0;
}
if (texture.a > 0.0) {
fillColor = vec4<f32>(
premultiply(fillColor).rgb * (1.0 - texture.a) + texture.rgb,
mix(fillColor.a, 1.0, texture.a),
);
}
}
else {
fillColor = premultiply(fillColor);
}
// Get appropriate SDF
if (mode == 0) {
if (fillColor.a <= 0.0) { discard; }
sdf = getBoxSDF(shape.xy, uv, scale);
}
else if (mode == 1) { sdf = getBorderBoxSDF(shape.xy, border, uv, scale); }
else { sdf = getRoundedBorderBoxSDF(shape.xy, border, radius, uv, scale); }
// Bleed by 0.5px to account for filter radius
let bleed = 0.5;
sdf.outer += bleed;
sdf.inner += bleed;
}
// Clipping this late because sooner than this causes issues in edge pixels
if (uv.x < clipUV.x || uv.y < clipUV.y || uv.x > clipUV.z || uv.y > clipUV.w) { discard; }
var mask = clamp(sdf.outer, 0.0, 1.0);
// SDF iso-contours
if (DEBUG_SDF) {
var s = 4.0;
var b = 0.0;
if (mode == -1 || mode == -2) {
s = 1.0;
b = 6.0;
}
let o = (sdf.outer - 0.5) * scale / s;
var m = (max(0.0, o + 0.5 + b) % 1.0) - 0.5;
if (o < -b) { m = 1.0 + (o + b - 1.0); }
mark = clamp(1.0 - abs(m / scale) * s, 0.0, 1.0);
if ((border.x != border.y) || (border.z != border.w) || (border.x != border.z)) {
let o = (sdf.inner - 0.5) * scale / s;
let m = ((o + 0.5 + b) % 1.0) - 0.5;
let mark2 = 1.0 * clamp(1.0 - abs(m / scale) * s, 0.0, 1.0);
if (sdf.inner > -0.5) { mark = mark2 + mark * 0.5; }
}
}
if (mask == 0.0 && mark == 0.0) { discard; }
// Blend stroke/fill
var color = fillColor;
if (sdf.outer != sdf.inner) {
// If less than 1px border, render 1px with opacity instead
var reduce = 1.0;
if (sdf.outer - sdf.inner < 1.0) {
reduce = sdf.outer - sdf.inner;
sdf.inner = sdf.outer - 1.0;
}
color = mix(fillColor, strokeColor, reduce * clamp(1.0 - sdf.inner, 0.0, 1.0));
}
if (HAS_MASK) {
color = getMask(color, vec4<f32>(textureUV, 0.0, 0.0), vec4<f32>(textureST, 0.0, 0.0));
}
if (!HAS_ALPHA_TO_COVERAGE) {
color = vec4<f32>((color.rgb + mark) * color.a * mask, color.a * mask + mark);
}
else {
color = vec4<f32>(color.rgb + mark, color.a * mask);
}
if (DEBUG_SDF) {
return vec4<f32>(mix(color.rgb, vec3<f32>(mark), 0.5), color.a);
}
return color;
}