feat: implement branch node (almost finsihed)
This commit is contained in:
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"id": "max/plantarium/array",
|
||||
"outputs": [
|
||||
"float"
|
||||
],
|
||||
"inputs": {
|
||||
"value": {
|
||||
"type": "float",
|
||||
"value": 4.2
|
||||
},
|
||||
"amount": {
|
||||
"type": "integer",
|
||||
"value": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
use macros::include_definition_file;
|
||||
use utils::{evaluate_int, get_args};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::console;
|
||||
|
||||
include_definition_file!("src/input.json");
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
utils::set_panic_hook();
|
||||
|
||||
let args = get_args(input);
|
||||
|
||||
let value_encoded = evaluate_int(args[0]);
|
||||
let length = evaluate_int(args[1]) as usize;
|
||||
|
||||
console::log_1(&format!("WASM(array): input: {:?} -> {:?}", args, value_encoded).into());
|
||||
|
||||
// construct array of length
|
||||
let mut res: Vec<i32> = Vec::with_capacity(length + 4);
|
||||
res.push(0);
|
||||
res.push(length as i32 + 4);
|
||||
for _ in 0..length {
|
||||
res.push(value_encoded);
|
||||
}
|
||||
res.push(1);
|
||||
res.push(1);
|
||||
|
||||
res
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use macros::include_definition_file;
|
||||
use utils::{
|
||||
encode_float, evaluate_float, geometry::calculate_normals, get_args, set_panic_hook, wrap_arg,
|
||||
encode_float, evaluate_float, geometry::calculate_normals, set_panic_hook, split_args, wrap_arg,
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::console;
|
||||
@@ -13,7 +13,7 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
|
||||
set_panic_hook();
|
||||
|
||||
let args = get_args(input);
|
||||
let args = split_args(input);
|
||||
|
||||
console::log_1(&format!("WASM(cube): input: {:?} -> {:?}", input, args ).into());
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "array"
|
||||
name = "branch"
|
||||
version = "0.1.0"
|
||||
authors = ["Max Richter <jim-x@web.de>"]
|
||||
edition = "2018"
|
||||
@@ -19,10 +19,11 @@ wasm-bindgen = "0.2.84"
|
||||
# code size when deploying.
|
||||
utils = { version = "0.1.0", path = "../../../../packages/utils" }
|
||||
macros = { version = "0.1.0", path = "../../../../packages/macros" }
|
||||
web-sys = { version = "0.3.69", features = ["console"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde-wasm-bindgen = "0.4"
|
||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||
web-sys = { version = "0.3.69", features = ["console"] }
|
||||
glam = "0.27.0"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.34"
|
||||
@@ -15,7 +15,7 @@
|
||||
"step": 0.05,
|
||||
"value": 0.8
|
||||
},
|
||||
"thiccness": {
|
||||
"thickness": {
|
||||
"type": "float",
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
@@ -47,7 +47,7 @@
|
||||
"value": 1
|
||||
},
|
||||
"depth": {
|
||||
"type": "float",
|
||||
"type": "integer",
|
||||
"hidden": true,
|
||||
"min": 1,
|
||||
"value": 1,
|
||||
116
nodes/max/plantarium/branch/src/lib.rs
Normal file
116
nodes/max/plantarium/branch/src/lib.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use std::f32::consts::PI;
|
||||
|
||||
use glam::Vec3;
|
||||
use macros::include_definition_file;
|
||||
use utils::{
|
||||
concat_arg_vecs, evaluate_float, evaluate_int,
|
||||
geometry::{create_path, get_direction_at_path, get_point_at_path, wrap_path, wrap_path_mut},
|
||||
log, set_panic_hook, split_args,
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
include_definition_file!("src/input.json");
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
set_panic_hook();
|
||||
|
||||
let args = split_args(input);
|
||||
|
||||
let paths = split_args(args[0]);
|
||||
|
||||
let mut output: Vec<Vec<i32>> = Vec::new();
|
||||
|
||||
let resolution = evaluate_int(args[8]).max(4) as usize;
|
||||
let depth = evaluate_int(args[6]);
|
||||
|
||||
let mut max_depth = 0;
|
||||
for path_data in paths.iter() {
|
||||
if path_data[2] != 0 {
|
||||
continue;
|
||||
}
|
||||
max_depth = max_depth.max(path_data[3]);
|
||||
}
|
||||
|
||||
for path_data in paths.iter() {
|
||||
// if this is not a path don't modify it
|
||||
if path_data[2] != 0 || path_data[3] < (max_depth - depth) {
|
||||
output.push(path_data.to_vec());
|
||||
continue;
|
||||
}
|
||||
|
||||
let path = wrap_path(path_data);
|
||||
|
||||
let branch_amount = evaluate_int(args[7]).max(1);
|
||||
|
||||
let lowest_branch = evaluate_float(args[4]);
|
||||
let highest_branch = evaluate_float(args[5]);
|
||||
|
||||
for i in 0..branch_amount {
|
||||
let a = i as f32 / (branch_amount - 1).max(1) as f32;
|
||||
|
||||
let length = evaluate_float(args[1]);
|
||||
let thickness = evaluate_float(args[2]);
|
||||
let offset_single = evaluate_float(args[3]);
|
||||
|
||||
// log!("a: {}, length: {}, thickness: {}, offset_single: {}, lowest_branch: {}, highest_branch: {}", a, length, thickness, offset_single, lowest_branch, highest_branch);
|
||||
|
||||
// log!("a: {}, length: {}, thickness: {}, offset_single: {}, lowest_branch: {}, highest_branch: {}", a, length, thickness, offset_single, lowest_branch, highest_branch);
|
||||
let root_alpha = (a * (highest_branch - lowest_branch) + lowest_branch)
|
||||
.min(1.0)
|
||||
.max(0.0);
|
||||
|
||||
let is_left = i % 2 == 0;
|
||||
|
||||
let branch_origin = get_point_at_path(path.points, root_alpha);
|
||||
//const [_vx, , _vz] = interpolateSkeletonVec(stem.skeleton, a);
|
||||
let direction_slice = get_direction_at_path(path.points, root_alpha);
|
||||
let direction = Vec3::from_slice(&direction_slice).normalize();
|
||||
|
||||
let rotation_angle = if is_left { PI } else { -PI };
|
||||
|
||||
// check if diration contains NaN
|
||||
if direction[0].is_nan() || direction[1].is_nan() || direction[2].is_nan() {
|
||||
log!(
|
||||
"BRANCH direction contains NaN: {:?}, slice: {:?} branch_origin: {:?}, branch: {}",
|
||||
direction,
|
||||
direction_slice,
|
||||
branch_origin,
|
||||
i
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let branch_direction = Vec3::from_slice(&[
|
||||
direction[0] * rotation_angle.cos() - direction[2] * rotation_angle.sin(),
|
||||
0.0,
|
||||
direction[0] * rotation_angle.sin() + direction[2] * rotation_angle.cos(),
|
||||
])
|
||||
.normalize();
|
||||
|
||||
log!(
|
||||
"BRANCH depth: {}, branch_origin: {:?}, direction_at: {:?}, branch_direction: {:?}",
|
||||
depth,
|
||||
branch_origin,
|
||||
direction,
|
||||
branch_direction
|
||||
);
|
||||
let mut branch_data = create_path(resolution, depth + 1);
|
||||
let branch = wrap_path_mut(&mut branch_data);
|
||||
|
||||
for j in 0..resolution {
|
||||
let _a = j as f32 / (resolution - 1) as f32;
|
||||
branch.points[j * 4] = branch_origin[0] + branch_direction[0] * _a * length;
|
||||
branch.points[j * 4 + 1] = branch_origin[1] + branch_direction[1] * _a * length;
|
||||
branch.points[j * 4 + 2] = branch_origin[2] + branch_direction[2] * _a * length;
|
||||
branch.points[j * 4 + 3] = branch_origin[3] * thickness * (1.0 - _a);
|
||||
}
|
||||
|
||||
output.push(branch_data);
|
||||
}
|
||||
|
||||
output.push(path_data.to_vec());
|
||||
}
|
||||
|
||||
concat_arg_vecs(output)
|
||||
}
|
||||
6
nodes/max/plantarium/branches/.gitignore
vendored
6
nodes/max/plantarium/branches/.gitignore
vendored
@@ -1,6 +0,0 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
bin/
|
||||
pkg/
|
||||
wasm-pack.log
|
||||
@@ -1,28 +0,0 @@
|
||||
[package]
|
||||
name = "template"
|
||||
version = "0.1.0"
|
||||
authors = ["Max Richter <jim-x@web.de>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2.84"
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||
# code size when deploying.
|
||||
utils = { version = "0.1.0", path = "../../../../packages/utils" }
|
||||
macros = { version = "0.1.0", path = "../../../../packages/macros" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde-wasm-bindgen = "0.4"
|
||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||
web-sys = { version = "0.3.69", features = ["console"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.34"
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "wasm-pack build --release --out-name index --no-default-features",
|
||||
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
use macros::include_definition_file;
|
||||
use utils::{concat_args, decode_float, encode_float, get_args, set_panic_hook, wrap_arg};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
include_definition_file!("src/input.json");
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[wasm_bindgen]
|
||||
pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
|
||||
set_panic_hook();
|
||||
|
||||
let args = get_args(input);
|
||||
|
||||
let paths = get_args(args[0]);
|
||||
|
||||
concat_args(paths)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
//! Test suite for the Web and headless browsers.
|
||||
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
extern crate wasm_bindgen_test;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn pass() {
|
||||
assert_eq!(1 + 1, 2);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use macros::include_definition_file;
|
||||
use utils::{concat_args, get_args, set_panic_hook};
|
||||
use utils::{concat_args, set_panic_hook, split_args};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
include_definition_file!("src/input.json");
|
||||
@@ -7,6 +7,6 @@ include_definition_file!("src/input.json");
|
||||
#[wasm_bindgen]
|
||||
pub fn execute(args: &[i32]) -> Vec<i32> {
|
||||
set_panic_hook();
|
||||
let args = get_args(args);
|
||||
let args = split_args(args);
|
||||
concat_args(vec![&[0], args[0], args[1], args[2]])
|
||||
}
|
||||
|
||||
@@ -36,6 +36,13 @@
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
"depth": {
|
||||
"type": "integer",
|
||||
"min": 1,
|
||||
"max": 10,
|
||||
"value": 1,
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ use glam::Vec3;
|
||||
use macros::include_definition_file;
|
||||
use noise::{core::open_simplex::open_simplex_2d, permutationtable::PermutationTable, Vector2};
|
||||
use utils::{
|
||||
concat_args, evaluate_float, evaluate_vec3, geometry::wrap_path, get_args, reset_call_count,
|
||||
set_panic_hook,
|
||||
concat_args, evaluate_float, evaluate_int, evaluate_vec3, geometry::wrap_path_mut,
|
||||
reset_call_count, set_panic_hook, split_args,
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@@ -19,9 +19,9 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
|
||||
reset_call_count();
|
||||
|
||||
let args = get_args(input);
|
||||
let args = split_args(input);
|
||||
|
||||
let plants = get_args(args[0]);
|
||||
let plants = split_args(args[0]);
|
||||
let scale = (evaluate_float(args[1]) * 0.1) as f64;
|
||||
let strength = evaluate_float(args[2]);
|
||||
let fix_bottom = evaluate_float(args[3]);
|
||||
@@ -30,8 +30,18 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
|
||||
let directional_strength = evaluate_vec3(args[5]);
|
||||
|
||||
let depth = evaluate_int(args[6]);
|
||||
|
||||
let hasher = PermutationTable::new(seed as u32);
|
||||
|
||||
let mut max_depth = 0;
|
||||
for path_data in plants.iter() {
|
||||
if path_data[2] != 0 {
|
||||
continue;
|
||||
}
|
||||
max_depth = max_depth.max(path_data[3]);
|
||||
}
|
||||
|
||||
let output: Vec<Vec<i32>> = plants
|
||||
.iter()
|
||||
.enumerate()
|
||||
@@ -43,7 +53,11 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
return path_data;
|
||||
}
|
||||
|
||||
let path = wrap_path(&mut path_data);
|
||||
if path_data[3] < (max_depth - depth + 1) {
|
||||
return path_data;
|
||||
}
|
||||
|
||||
let path = wrap_path_mut(&mut path_data);
|
||||
|
||||
let p0 = Vec3::new(path.points[0], path.points[1], path.points[2]);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use macros::include_definition_file;
|
||||
use utils::{
|
||||
concat_args, evaluate_int,
|
||||
geometry::{extrude_path, wrap_path},
|
||||
get_args, log,
|
||||
log, split_args,
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@@ -12,9 +12,11 @@ include_definition_file!("src/inputs.json");
|
||||
pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
utils::set_panic_hook();
|
||||
|
||||
let args = get_args(input);
|
||||
let args = split_args(input);
|
||||
|
||||
let inputs = get_args(args[0]);
|
||||
assert_eq!(args.len(), 2, "Expected 2 arguments, got {}", args.len());
|
||||
|
||||
let inputs = split_args(args[0]);
|
||||
|
||||
let resolution = evaluate_int(args[1]) as usize;
|
||||
|
||||
@@ -28,10 +30,9 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
|
||||
if arg_type == 0 {
|
||||
// this is path
|
||||
let mut vec = arg.to_vec();
|
||||
let vec = arg.to_vec();
|
||||
output.push(vec.clone());
|
||||
let path_data = wrap_path(&mut vec);
|
||||
log!("{:?}", path_data);
|
||||
let path_data = wrap_path(arg);
|
||||
let geometry = extrude_path(path_data, resolution);
|
||||
output.push(geometry);
|
||||
} else if arg_type == 1 {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use macros::include_definition_file;
|
||||
use utils::{concat_args, get_args, set_panic_hook};
|
||||
use utils::{concat_args, set_panic_hook, split_args};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
include_definition_file!("src/definition.json");
|
||||
@@ -7,6 +7,6 @@ include_definition_file!("src/definition.json");
|
||||
#[wasm_bindgen]
|
||||
pub fn execute(args: &[i32]) -> Vec<i32> {
|
||||
set_panic_hook();
|
||||
let args = get_args(args);
|
||||
let args = split_args(args);
|
||||
concat_args(vec![&[1], args[0], args[1], args[2]])
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use macros::include_definition_file;
|
||||
use utils::{
|
||||
evaluate_float, evaluate_int, evaluate_vec3,
|
||||
geometry::{create_multiple_paths, wrap_multiple_paths},
|
||||
get_args, log, reset_call_count, set_panic_hook,
|
||||
log, reset_call_count, set_panic_hook, split_args,
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@@ -14,7 +14,7 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
|
||||
reset_call_count();
|
||||
|
||||
let args = get_args(input);
|
||||
let args = split_args(input);
|
||||
|
||||
let amount = evaluate_int(args[1]) as usize;
|
||||
let path_resolution = evaluate_int(args[4]) as usize;
|
||||
|
||||
6
nodes/max/plantarium/sum/.gitignore
vendored
6
nodes/max/plantarium/sum/.gitignore
vendored
@@ -1,6 +0,0 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
bin/
|
||||
pkg/
|
||||
wasm-pack.log
|
||||
@@ -1,28 +0,0 @@
|
||||
[package]
|
||||
name = "max-plantarium-sum"
|
||||
version = "0.1.0"
|
||||
authors = ["Max Richter <jim-x@web.de>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2.84"
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||
# code size when deploying.
|
||||
utils = { version = "0.1.0", path = "../../../../packages/utils" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde-wasm-bindgen = "0.4"
|
||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||
macros = { version = "0.1.0", path = "../../../../packages/macros" }
|
||||
web-sys = { version = "0.3.69", features = ["console"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.34"
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "wasm-pack build --release --out-name index --no-default-features",
|
||||
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"id": "max/plantarium/sum",
|
||||
"outputs": [
|
||||
"float"
|
||||
],
|
||||
"inputs": {
|
||||
"array": {
|
||||
"type": "float",
|
||||
"value": 2,
|
||||
"external": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
use macros::include_definition_file;
|
||||
use utils::{decode_float, encode_float};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
include_definition_file!("src/input.json");
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
utils::set_panic_hook();
|
||||
|
||||
let mut sum = 0.0;
|
||||
|
||||
// console::log_1(&format!("WASM(sum_node): args: {:?}", input).into());
|
||||
|
||||
let length = (input.len() - 2) / 2;
|
||||
|
||||
(0..length).for_each(|i| {
|
||||
// console::log_1(&format!("WASM(sum_node): i: {} sum: {:?}", i, sum).into());
|
||||
sum += decode_float(input[2 + i * 2]);
|
||||
});
|
||||
|
||||
let encoded_sum = encode_float(sum);
|
||||
|
||||
// console::log_1(&format!("WASM(sum_node): result: {:?}", sum).into());
|
||||
|
||||
vec![0, 2, encoded_sum]
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
//! Test suite for the Web and headless browsers.
|
||||
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
extern crate wasm_bindgen_test;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn pass() {
|
||||
assert_eq!(1 + 1, 2);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use macros::include_definition_file;
|
||||
use utils::{decode_float, encode_float, evaluate_int, get_args, wrap_arg};
|
||||
use utils::{decode_float, encode_float, evaluate_int, split_args, wrap_arg};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::console;
|
||||
|
||||
@@ -11,7 +11,7 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
|
||||
utils::set_panic_hook();
|
||||
|
||||
let args = get_args(input);
|
||||
let args = split_args(input);
|
||||
|
||||
let size = evaluate_int(args[0]);
|
||||
let decoded = decode_float(size);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use macros::include_definition_file;
|
||||
use utils::{concat_args, get_args, log};
|
||||
use utils::{concat_args, log, split_args};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
include_definition_file!("src/input.json");
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn execute(input: &[i32]) -> Vec<i32> {
|
||||
let args = get_args(input);
|
||||
let args = split_args(input);
|
||||
log!("vec3 input: {:?}", input);
|
||||
log!("vec3 args: {:?}", args);
|
||||
concat_args(args)
|
||||
|
||||
Reference in New Issue
Block a user