use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::fmt;
use std::sync::Arc;
const MAX_FIELD_JSON_BYTES: usize = 1024 * 1024;
const MAX_FBM_OCTAVES: u32 = 8;
const MAX_SAFE_LATTICE_COORD: f64 = i32::MAX as f64 - 1024.0;
const FIELD_GRAPH_VERSION: u32 = 1;
const MAX_GRAPH_NODES: usize = 5_000;
const MAX_GRAPH_ROOTS: usize = 256;
const MAX_ROOT_NAME_BYTES: usize = 128;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FieldError {
InputTooLarge,
InvalidArgument,
Parse,
}
impl fmt::Display for FieldError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FieldError::InputTooLarge => f.write_str("field JSON exceeds the size limit"),
FieldError::InvalidArgument => f.write_str("invalid field argument"),
FieldError::Parse => f.write_str("invalid field JSON"),
}
}
}
impl std::error::Error for FieldError {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(
tag = "type",
rename_all = "camelCase",
rename_all_fields = "camelCase"
)]
enum Field3Node {
ValueNoiseFbm {
frequency: f32,
seed: i32,
octaves: u32,
},
}
impl Field3Node {
fn validate(&self) -> Result<(), FieldError> {
match self {
Field3Node::ValueNoiseFbm {
frequency, octaves, ..
} => {
if !frequency.is_finite()
|| *frequency <= 0.0
|| !(1..=MAX_FBM_OCTAVES).contains(octaves)
|| (*frequency as f64) * 2.0_f64.powi((*octaves - 1) as i32)
> MAX_SAFE_LATTICE_COORD
{
return Err(FieldError::InvalidArgument);
}
}
}
Ok(())
}
fn eval(&self, x: f32, y: f32, z: f32) -> f32 {
match self {
Field3Node::ValueNoiseFbm {
frequency,
seed,
octaves,
} => {
let max_frequency = (*frequency as f64) * 2.0_f64.powi((*octaves - 1) as i32);
if [x, y, z].into_iter().any(|coordinate| {
(coordinate as f64).abs() * max_frequency > MAX_SAFE_LATTICE_COORD
}) {
return 0.0;
}
crate::sdf::noise::fbm3(x, y, z, *seed, *frequency, *octaves)
}
}
}
fn output_range(&self) -> Option<[f32; 2]> {
match self {
Field3Node::ValueNoiseFbm { .. } => Some([-1.0, 1.0]),
}
}
}
#[derive(Debug, Clone, Serialize)]
#[serde(transparent)]
pub struct Field3(Arc<Field3Node>);
impl<'de> Deserialize<'de> for Field3 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let node = Field3Node::deserialize(deserializer)?;
node.validate().map_err(serde::de::Error::custom)?;
Ok(Self(Arc::new(node)))
}
}
impl Field3 {
pub(crate) fn validate(&self) -> Result<(), FieldError> {
self.0.validate()
}
pub fn value_noise_fbm(frequency: f32, seed: i32, octaves: u32) -> Result<Self, FieldError> {
let node = Field3Node::ValueNoiseFbm {
frequency,
seed,
octaves,
};
node.validate()?;
Ok(Self(Arc::new(node)))
}
pub fn eval(&self, x: f32, y: f32, z: f32) -> f32 {
if !x.is_finite() || !y.is_finite() || !z.is_finite() {
return f32::INFINITY;
}
let value = self.0.eval(x, y, z);
match self.output_range() {
Some([lo, hi]) if value.is_finite() => value.clamp(lo, hi),
_ => 0.0,
}
}
pub fn output_range(&self) -> Option<[f32; 2]> {
self.0.output_range()
}
pub fn shares_storage_with(&self, other: &Field3) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
pub fn from_json(json: &str) -> Result<Self, FieldError> {
if json.len() > MAX_FIELD_JSON_BYTES {
return Err(FieldError::InputTooLarge);
}
let node: Field3Node = serde_json::from_str(json).map_err(|_| FieldError::Parse)?;
node.validate()?;
Ok(Self(Arc::new(node)))
}
pub fn to_json(&self) -> Result<String, FieldError> {
serde_json::to_string(&*self.0).map_err(|_| FieldError::Parse)
}
}
#[derive(Debug, Serialize, Deserialize)]
struct Field3GraphDocument {
version: u32,
nodes: Vec<Field3Node>,
roots: BTreeMap<String, usize>,
}
#[derive(Debug, Clone)]
pub struct Field3Graph {
roots: BTreeMap<String, Field3>,
}
impl Field3Graph {
pub fn from_roots<I>(roots: I) -> Result<Self, FieldError>
where
I: IntoIterator<Item = (String, Field3)>,
{
let mut result = BTreeMap::new();
for (name, field) in roots {
if name.is_empty()
|| name.len() > MAX_ROOT_NAME_BYTES
|| result.len() >= MAX_GRAPH_ROOTS
|| field.validate().is_err()
|| result.insert(name, field).is_some()
{
return Err(FieldError::InvalidArgument);
}
}
if result.is_empty() {
return Err(FieldError::InvalidArgument);
}
Ok(Self { roots: result })
}
pub fn root(&self, name: &str) -> Option<Field3> {
self.roots.get(name).cloned()
}
pub fn from_json(json: &str) -> Result<Self, FieldError> {
if json.len() > MAX_FIELD_JSON_BYTES {
return Err(FieldError::InputTooLarge);
}
let document: Field3GraphDocument =
serde_json::from_str(json).map_err(|_| FieldError::Parse)?;
if document.version != FIELD_GRAPH_VERSION
|| document.nodes.is_empty()
|| document.nodes.len() > MAX_GRAPH_NODES
|| document.roots.is_empty()
|| document.roots.len() > MAX_GRAPH_ROOTS
{
return Err(FieldError::InvalidArgument);
}
let mut nodes = Vec::with_capacity(document.nodes.len());
for node in document.nodes {
node.validate()?;
nodes.push(Arc::new(node));
}
let mut roots = BTreeMap::new();
for (name, node_id) in document.roots {
if name.is_empty() || name.len() > MAX_ROOT_NAME_BYTES {
return Err(FieldError::InvalidArgument);
}
let node = nodes
.get(node_id)
.ok_or(FieldError::InvalidArgument)?
.clone();
roots.insert(name, Field3(node));
}
Ok(Self { roots })
}
pub fn to_json(&self) -> Result<String, FieldError> {
let mut node_ids: HashMap<usize, usize> = HashMap::new();
let mut nodes = Vec::new();
let mut roots = BTreeMap::new();
for (name, field) in &self.roots {
let pointer = Arc::as_ptr(&field.0) as usize;
let node_id = *node_ids.entry(pointer).or_insert_with(|| {
let id = nodes.len();
nodes.push((*field.0).clone());
id
});
roots.insert(name.clone(), node_id);
}
let document = Field3GraphDocument {
version: FIELD_GRAPH_VERSION,
nodes,
roots,
};
serde_json::to_string(&document).map_err(|_| FieldError::Parse)
}
}