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; mod routes;
pub mod state; pub mod state;
use std::{f64::consts::PI, sync::Arc}; use std::{collections::HashMap, f64::consts::PI, sync::Arc};
use anyhow::Result; use anyhow::Result;
use axum::{routing::get, Router}; use axum::{routing::get, Router};
@ -16,7 +16,7 @@ use prisma_client_rust::serde_json::json;
use routes::{universe_list, universe_map}; use routes::{universe_list, universe_map};
use triangle::{triangulate, Point, TrianglulateOpts}; use triangle::{triangulate, Point, TrianglulateOpts};
use crate::prisma::star_system; use crate::prisma::{star_system, star_system_edges};
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
struct Opt { struct Opt {
@ -73,6 +73,7 @@ async fn main() -> Result<()> {
TrianglulateOpts::builder() TrianglulateOpts::builder()
.point_list(point_list) .point_list(point_list)
.voronoi(true) .voronoi(true)
.maximum_triangle_area(4000.0)
.build()?, .build()?,
)? )?
}; };
@ -87,9 +88,11 @@ async fn main() -> Result<()> {
result result
.point_list .point_list
.into_iter() .into_iter()
.map(|point| { .enumerate()
.map(|(idx, point)| {
star_system::create_unchecked( star_system::create_unchecked(
universe.id.clone(), universe.id.clone(),
idx as i32,
point.x, point.x,
point.y, point.y,
vec![], vec![],
@ -100,6 +103,56 @@ async fn main() -> Result<()> {
.exec() .exec()
.await?; .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."); println!("Done.");
} }
} }

View file

@ -2,9 +2,13 @@ use axum::{
extract::{Path, State}, extract::{Path, State},
Json, Json,
}; };
use prisma_client_rust::Direction;
use serde_json::Value; 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> { pub async fn universe_list(state: State<AppState>) -> Json<Value> {
let universes = state let universes = state
@ -30,12 +34,25 @@ pub async fn universe_map(
.prisma .prisma
.star_system() .star_system()
.find_many(vec![star_system::universe_id::equals(universe_id.clone())]) .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() .exec()
.await .await
.unwrap(); .unwrap();
Json(json!({ Json(json!({
"points": points, "points": points,
"edges": edges,
})) }))
} }

View file

@ -28,7 +28,6 @@ export default function MapView({}) {
<main className={styles.main}> <main className={styles.main}>
<Canvas camera={{ position: [0, 20, 0] }}> <Canvas camera={{ position: [0, 20, 0] }}>
<ambientLight /> <ambientLight />
{/* <pointLight position={[0, 0, 0]} /> */}
<MapControls /> <MapControls />
<mesh> <mesh>
@ -39,25 +38,28 @@ export default function MapView({}) {
{data.points.map((point) => { {data.points.map((point) => {
const x = point.coordX / 80; const x = point.coordX / 80;
const y = point.coordY / 80; const y = point.coordY / 80;
console.log("point", x, y); return <StarSystem key={point.id} position={[x, 0, y]} />;
return <Box key={JSON.stringify([x, y])} position={[x, 0, y]} />; })}
{data.edges.map((edge) => {
return (
<Hyperlane
key={`${edge.fromIndex}:${edge.toIndex}`}
edge={edge}
points={data.points}
/>
);
})} })}
</Canvas> </Canvas>
</main> </main>
); );
} }
function Box({ position, ...props }: MeshProps) { function StarSystem({ position, ...props }: MeshProps) {
const meshRef = useRef(); const meshRef = useRef();
const [hovered, setHover] = useState(false); const [hovered, setHover] = useState(false);
const [active, setActive] = 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 ( return (
<mesh <mesh
{...props} {...props}
@ -68,11 +70,39 @@ function Box({ position, ...props }: MeshProps) {
onPointerOver={(_event) => setHover(true)} onPointerOver={(_event) => setHover(true)}
onPointerOut={(_event) => setHover(false)} onPointerOut={(_event) => setHover(false)}
> >
<sphereGeometry args={[0.5]} /> <sphereGeometry args={[0.05]} />
<meshStandardMaterial <meshStandardMaterial color={hovered ? "white" : "skyblue"} />
color={hovered ? "hotpink" : "orange"} </mesh>
emissive={"white"} );
/> }
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> </mesh>
); );
} }

View file

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

View file

@ -13,6 +13,12 @@ use anyhow::Result;
pub struct TrianglulateOpts<P = Vec<Point>> { pub struct TrianglulateOpts<P = Vec<Point>> {
point_list: P, 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 /// Generates a Voronoi diagram
#[builder(default)] #[builder(default)]
voronoi: bool, voronoi: bool,
@ -33,6 +39,9 @@ pub struct Point {
#[derive(Debug)] #[derive(Debug)]
pub struct TriangulateResult { pub struct TriangulateResult {
pub point_list: Vec<Point>, pub point_list: Vec<Point>,
/// A list of triangles
pub triangle_list: Vec<Vec<usize>>,
pub voronoi_point_list: Vec<Point>, pub voronoi_point_list: Vec<Point>,
} }
@ -41,14 +50,18 @@ where
P: IntoIterator<Item = Point>, P: IntoIterator<Item = Point>,
{ {
let mut switches = Vec::new(); let mut switches = Vec::new();
switches.push("p"); switches.push("p".to_owned());
switches.push("c"); switches.push("c".to_owned());
switches.push("z"); switches.push("z".to_owned());
switches.push("A"); switches.push("A".to_owned());
switches.push("e"); if let Some(maximum_triangle_area) = opts.maximum_triangle_area {
switches.push("n"); 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 { if opts.voronoi {
switches.push("v"); switches.push("v".to_owned());
} }
let switches = CString::new(switches.join(""))?; let switches = CString::new(switches.join(""))?;
@ -92,6 +105,13 @@ where
output.numberofpoints as usize * 2, 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( let voronoi_point_list = point_list_from_flat_point_list(
vorout.pointlist, vorout.pointlist,
vorout.numberofpoints as usize * 2, vorout.numberofpoints as usize * 2,
@ -99,6 +119,7 @@ where
Ok(TriangulateResult { Ok(TriangulateResult {
point_list, point_list,
triangle_list,
voronoi_point_list, voronoi_point_list,
}) })
} }
@ -106,10 +127,22 @@ where
fn point_list_from_flat_point_list(ptr: *mut f64, len: usize) -> Vec<Point> { 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) }; let flat_point_list = unsafe { Vec::from_raw_parts(ptr, len, len) };
flat_point_list flat_point_list
.chunks(2) .chunks_exact(2)
.map(|points| Point { .map(|points| Point {
x: points[0], x: points[0],
y: points[1], y: points[1],
}) })
.collect::<Vec<_>>() .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<_>>()
}