use bevy::prelude::*;
use web_sys::wasm_bindgen::JsValue;
use crate::{
core::{parameters::Stick, spawner::material_from_descriptor},
plugins::{info::plugin::ActiveInfoTarget, modification::plugin::LineConnections},
prelude::{MaterialType, Point},
};
pub fn perge_info_target(
point_query: &Query<(Entity, &Point), With<ActiveInfoTarget>>,
commands: &mut Commands,
) {
for (entity, _) in point_query.iter() {
commands.entity(entity).remove::<ActiveInfoTarget>();
}
}
pub fn point_info(
cast_ray: Ray3d,
point_query: &Query<(Entity, &Point)>,
commands: &mut Commands,
modification_radius: f32,
) {
for (entity, point) in point_query.iter() {
if point_on_ray(&cast_ray, point.position, modification_radius) {
commands.entity(entity).insert(ActiveInfoTarget);
}
}
}
pub fn purge_line(line_query: &Query<(Entity, &mut LineConnections)>, commands: &mut Commands) {
for (entity, _) in line_query.iter() {
commands.entity(entity).despawn();
}
commands.spawn(LineConnections { p0: None, p1: None });
}
pub fn lock_affected_points(
cast_ray: Ray3d,
points: &mut Query<(&mut MeshMaterial3d<StandardMaterial>, &mut Point)>,
materials: &mut ResMut<Assets<StandardMaterial>>,
modification_radius: f32,
) {
for (mut material, mut pt) in points {
if point_on_ray(&cast_ray, pt.position, modification_radius) {
pt.locked = !pt.locked;
let color = if pt.locked {
Color::srgb(1., 0., 0.)
} else {
Color::WHITE
};
let new_handle = materials.add(StandardMaterial::from(color));
material.0 = new_handle;
}
}
}
pub fn spawn_stick(
cast_ray: Ray3d,
line_query: &mut Query<(Entity, &mut LineConnections)>,
points: &Query<(Entity, &Point)>,
commands: &mut Commands,
meshes: &mut ResMut<Assets<Mesh>>,
materials: &mut ResMut<Assets<StandardMaterial>>,
material: MaterialType,
modification_radius: f32,
) {
let (_, mut line) = match line_query.single_mut() {
Ok(query_item) => query_item,
Err(_) => {
return;
}
};
let stick_mesh = meshes.add(Cuboid::default());
let material = material_from_descriptor(&material, materials);
for (entity, point) in points {
if point_on_ray(&cast_ray, point.position, modification_radius) {
match (line.p0, line.p1) {
(None, None) => {
line.p0 = Some(entity);
}
(Some(_), None) => {
line.p1 = Some(entity);
}
(None, Some(_)) => {
line.p0 = Some(entity);
}
(Some(_), Some(_)) => {
web_sys::console::log_1(&JsValue::from_str(
"Err: Line connection wasnt cleaned up.",
));
}
}
match (line.p0, line.p1) {
(Some(p0_id), Some(p1_id)) => {
if let Ok([p1, p2]) = points.get_many([p0_id, p1_id]) {
let spacial_point_1 = p1.1.position;
let spacial_point_2 = p2.1.position;
let diff = spacial_point_2 - spacial_point_1;
let rot = Quat::from_rotation_arc(Vec3::X, diff.normalize());
commands.spawn((
Mesh3d(stick_mesh.clone()),
MeshMaterial3d(material.clone()),
Transform {
translation: (spacial_point_1 + spacial_point_2) * 0.5,
rotation: rot,
scale: Vec3::new(diff.length(), 0.01, 0.01),
},
Stick::new(p0_id, p1_id, spacial_point_1.distance(spacial_point_2)),
));
line.p0 = None;
line.p1 = None;
}
}
_ => (),
}
}
}
}
pub fn cut_sticks(
cast_ray: Ray3d,
sticks: &mut Query<(Entity, &mut Stick)>,
points: &Query<&Point>,
commands: &mut Commands,
modification_radius: f32,
) {
for (entity, stick) in sticks {
if let Ok([p1, p2]) = points.get_many([stick.point1, stick.point2]) {
let sample_points =
sample_points_along_line(p1.position, p2.position, modification_radius);
for point in sample_points {
if point_on_ray(&cast_ray, point, modification_radius) {
commands.entity(entity).despawn();
break;
}
}
}
}
}
pub fn sample_points_along_line(start: Vec3, end: Vec3, spacing: f32) -> Vec<Vec3> {
assert!(spacing > 0.0, "spacing must be positive");
let delta = end - start;
let total_length = delta.length();
if total_length == 0.0 {
return vec![start];
}
let direction = delta / total_length; let mut points = Vec::new();
points.push(start);
let mut traveled = spacing;
while traveled < total_length {
points.push(start + direction * traveled);
traveled += spacing;
}
points.push(end);
points
}
pub fn ray_coords_at(ray: Ray3d, target_z: f32) -> Option<Vec3> {
let origin = ray.origin;
let direction = ray.direction;
if direction.z.abs() < f32::EPSILON {
return None;
}
let t = (target_z - origin.z) / direction.z;
Some(origin + direction * t)
}
pub fn point_on_ray(ray: &Ray3d, point: Vec3, tolerance: f32) -> bool {
let origin_to_point = point - ray.origin;
let direction: Vec3 = ray.direction.into();
let dot = origin_to_point.dot(direction);
if dot < 0.0 {
return false;
}
let projected = ray.origin + direction * dot;
(projected - point).length_squared() <= tolerance * tolerance
}
pub fn grab_point(mut points: Query<(Entity, &mut Point)>, ray: Ray3d) {
let closest_ent = {
let dir: Vec3 = ray.direction.into();
let mut best: Option<(Entity, f32)> = None;
for (entity, point) in points.iter_mut() {
if point.locked {
continue;
}
let v = point.position - ray.origin;
let t = v.dot(dir);
if t < 0.0 {
continue;
}
let proj = ray.origin + dir * t;
let dist2 = (proj - point.position).length_squared();
match best {
None => best = Some((entity, dist2)),
Some((_, best_dist2)) if dist2 < best_dist2 => best = Some((entity, dist2)),
_ => {}
}
}
best.map(|(entity, _)| entity)
};
if let Some(ent) = closest_ent {
if let Ok((_, mut pt)) = points.get_mut(ent) {
let new_coords = match ray_coords_at(ray, 0.) {
Some(coords) => coords,
None => return,
};
pt.position = new_coords;
pt.prev_position = new_coords;
}
}
}