draw connections
This commit is contained in:
parent
9faef57203
commit
88bb954676
5 changed files with 166 additions and 30 deletions
|
@ -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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ model StarSystem {
|
||||||
universeId String
|
universeId String
|
||||||
universe Universe @relation(fields: [universeId], references: [id])
|
universe Universe @relation(fields: [universeId], references: [id])
|
||||||
|
|
||||||
|
genIndex Int
|
||||||
coordX Float
|
coordX Float
|
||||||
coordY Float
|
coordY Float
|
||||||
|
|
||||||
|
@ -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])
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<_>>()
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue