draw connections

This commit is contained in:
Michael Zhang 2023-12-30 02:36:34 -05:00
parent 9faef57203
commit 88bb954676
5 changed files with 166 additions and 30 deletions

View file

@ -6,7 +6,7 @@ pub mod prisma;
mod routes;
pub mod state;
use std::{f64::consts::PI, sync::Arc};
use std::{collections::HashMap, f64::consts::PI, sync::Arc};
use anyhow::Result;
use axum::{routing::get, Router};
@ -16,7 +16,7 @@ use prisma_client_rust::serde_json::json;
use routes::{universe_list, universe_map};
use triangle::{triangulate, Point, TrianglulateOpts};
use crate::prisma::star_system;
use crate::prisma::{star_system, star_system_edges};
#[derive(Debug, Parser)]
struct Opt {
@ -73,6 +73,7 @@ async fn main() -> Result<()> {
TrianglulateOpts::builder()
.point_list(point_list)
.voronoi(true)
.maximum_triangle_area(4000.0)
.build()?,
)?
};
@ -87,9 +88,11 @@ async fn main() -> Result<()> {
result
.point_list
.into_iter()
.map(|point| {
.enumerate()
.map(|(idx, point)| {
star_system::create_unchecked(
universe.id.clone(),
idx as i32,
point.x,
point.y,
vec![],
@ -100,6 +103,56 @@ async fn main() -> Result<()> {
.exec()
.await?;
// Get the idx -> star system id mapping
let star_system_map = client
.star_system()
.find_many(vec![])
.select(star_system::select!({ id gen_index }))
.exec()
.await?
.into_iter()
.map(|star_system| (star_system.gen_index as usize, star_system.id))
.collect::<HashMap<_, _>>();
// Insert edges into database
macro_rules! create_edge {
($from:expr, $to:expr) => {{
println!("WTF? {}, {}", $from, $to);
star_system_edges::create_unchecked(
universe.id.clone(),
star_system_map.get(&$from).unwrap().to_owned(),
$from as i32,
star_system_map.get(&$to).unwrap().to_owned(),
$to as i32,
vec![],
)
}};
}
client
.star_system_edges()
.create_many(
result
.triangle_list
.into_iter()
.flat_map(|points| {
let a = points[0];
let b = points[1];
let c = points[2];
[
create_edge!(a, b),
create_edge!(a, c),
create_edge!(b, a),
create_edge!(b, c),
create_edge!(c, a),
create_edge!(c, b),
]
})
.collect(),
)
.skip_duplicates()
.exec()
.await?;
println!("Done.");
}
}

View file

@ -2,9 +2,13 @@ use axum::{
extract::{Path, State},
Json,
};
use prisma_client_rust::Direction;
use serde_json::Value;
use crate::{prisma::star_system, AppState};
use crate::{
prisma::{star_system, star_system_edges},
AppState,
};
pub async fn universe_list(state: State<AppState>) -> Json<Value> {
let universes = state
@ -30,12 +34,25 @@ pub async fn universe_map(
.prisma
.star_system()
.find_many(vec![star_system::universe_id::equals(universe_id.clone())])
.select(star_system::select!({ coord_x coord_y }))
.order_by(star_system::gen_index::order(Direction::Asc))
.select(star_system::select!({ id coord_x coord_y }))
.exec()
.await
.unwrap();
let edges = state
.prisma
.star_system_edges()
.find_many(vec![star_system_edges::universe_id::equals(
universe_id.clone(),
)])
.select(star_system_edges::select!({ from_system_id to_system_id from_index to_index }))
.exec()
.await
.unwrap();
Json(json!({
"points": points,
"edges": edges,
}))
}

View file

@ -28,7 +28,6 @@ export default function MapView({}) {
<main className={styles.main}>
<Canvas camera={{ position: [0, 20, 0] }}>
<ambientLight />
{/* <pointLight position={[0, 0, 0]} /> */}
<MapControls />
<mesh>
@ -39,25 +38,28 @@ export default function MapView({}) {
{data.points.map((point) => {
const x = point.coordX / 80;
const y = point.coordY / 80;
console.log("point", x, y);
return <Box key={JSON.stringify([x, y])} position={[x, 0, y]} />;
return <StarSystem key={point.id} position={[x, 0, y]} />;
})}
{data.edges.map((edge) => {
return (
<Hyperlane
key={`${edge.fromIndex}:${edge.toIndex}`}
edge={edge}
points={data.points}
/>
);
})}
</Canvas>
</main>
);
}
function Box({ position, ...props }: MeshProps) {
function StarSystem({ position, ...props }: MeshProps) {
const meshRef = useRef();
const [hovered, setHover] = useState(false);
const [active, setActive] = useState(false);
// Subscribe this component to the render-loop, rotate the mesh every frame
useFrame((_state, delta) => {
meshRef.current.rotation.x += delta;
});
// Return view, these are regular three.js elements expressed in JSX
return (
<mesh
{...props}
@ -68,11 +70,39 @@ function Box({ position, ...props }: MeshProps) {
onPointerOver={(_event) => setHover(true)}
onPointerOut={(_event) => setHover(false)}
>
<sphereGeometry args={[0.5]} />
<meshStandardMaterial
color={hovered ? "hotpink" : "orange"}
emissive={"white"}
/>
<sphereGeometry args={[0.05]} />
<meshStandardMaterial color={hovered ? "white" : "skyblue"} />
</mesh>
);
}
function Hyperlane({ edge, points, ...props }) {
const fromPoint = points[edge.fromIndex];
const toPoint = points[edge.toIndex];
const avgX = (fromPoint.coordX + toPoint.coordX) / 160;
const avgY = (fromPoint.coordY + toPoint.coordY) / 160;
const length = Math.sqrt(
((fromPoint.coordX - toPoint.coordX) / 80) ** 2 +
((fromPoint.coordY - toPoint.coordY) / 80) ** 2,
);
const angle =
Math.PI -
Math.atan2(
toPoint.coordY - fromPoint.coordY,
toPoint.coordX - fromPoint.coordX,
);
return (
<mesh
{...props}
position={[avgX, 0, avgY]}
rotation={[0, angle, Math.PI / 2]}
>
<cylinderGeometry args={[0.005, 0.005, length]} />
<meshStandardMaterial color={"skyblue"} />
</mesh>
);
}

View file

@ -59,8 +59,9 @@ model StarSystem {
universeId String
universe Universe @relation(fields: [universeId], references: [id])
coordX Float
coordY Float
genIndex Int
coordX Float
coordY Float
inbound StarSystemEdges[] @relation("inbound")
outbound StarSystemEdges[] @relation("outbound")
@ -72,9 +73,11 @@ model StarSystemEdges {
fromSystemId String
from StarSystem @relation(fields: [fromSystemId], references: [id], name: "outbound")
fromIndex Int
toSystemId String
to StarSystem @relation(fields: [toSystemId], references: [id], name: "inbound")
toIndex Int
@@id([universeId, fromSystemId, toSystemId])
}

View file

@ -13,6 +13,12 @@ use anyhow::Result;
pub struct TrianglulateOpts<P = Vec<Point>> {
point_list: P,
/// Imposes a maximum triangle area constraint. A fixed area constraint (that
/// applies to every triangle) may be specified after the `a', or varying area
/// constraints may be read from a .poly file or .area file.
#[builder(default, setter(strip_option))]
maximum_triangle_area: Option<f64>,
/// Generates a Voronoi diagram
#[builder(default)]
voronoi: bool,
@ -33,6 +39,9 @@ pub struct Point {
#[derive(Debug)]
pub struct TriangulateResult {
pub point_list: Vec<Point>,
/// A list of triangles
pub triangle_list: Vec<Vec<usize>>,
pub voronoi_point_list: Vec<Point>,
}
@ -41,14 +50,18 @@ where
P: IntoIterator<Item = Point>,
{
let mut switches = Vec::new();
switches.push("p");
switches.push("c");
switches.push("z");
switches.push("A");
switches.push("e");
switches.push("n");
switches.push("p".to_owned());
switches.push("c".to_owned());
switches.push("z".to_owned());
switches.push("A".to_owned());
if let Some(maximum_triangle_area) = opts.maximum_triangle_area {
switches.push(format!("a{maximum_triangle_area}"));
}
switches.push("q".to_owned());
switches.push("e".to_owned());
switches.push("n".to_owned());
if opts.voronoi {
switches.push("v");
switches.push("v".to_owned());
}
let switches = CString::new(switches.join(""))?;
@ -92,6 +105,13 @@ where
output.numberofpoints as usize * 2,
);
let triangle_list = triangle_list_from_flat_point_list(
output.trianglelist,
output.numberofcorners as usize,
output.numberoftriangles as usize * output.numberofcorners as usize,
);
println!("aa {}", triangle_list.len());
let voronoi_point_list = point_list_from_flat_point_list(
vorout.pointlist,
vorout.numberofpoints as usize * 2,
@ -99,6 +119,7 @@ where
Ok(TriangulateResult {
point_list,
triangle_list,
voronoi_point_list,
})
}
@ -106,10 +127,22 @@ where
fn point_list_from_flat_point_list(ptr: *mut f64, len: usize) -> Vec<Point> {
let flat_point_list = unsafe { Vec::from_raw_parts(ptr, len, len) };
flat_point_list
.chunks(2)
.chunks_exact(2)
.map(|points| Point {
x: points[0],
y: points[1],
})
.collect::<Vec<_>>()
}
fn triangle_list_from_flat_point_list(
ptr: *mut i32,
num_corners: usize,
len: usize,
) -> Vec<Vec<usize>> {
let flat_point_list = unsafe { Vec::from_raw_parts(ptr, len, len) };
flat_point_list
.chunks_exact(num_corners)
.map(|points| points.iter().map(|point| *point as usize).collect())
.collect::<Vec<_>>()
}