mirror of
https://github.com/hannobraun/Fornjot
synced 2025-05-28 05:31:37 +00:00
Document code of current experiment
This commit is contained in:
parent
cf7c1f6d52
commit
53b3a55250
@ -1,3 +1,8 @@
|
||||
//! # [winit] event loop
|
||||
//!
|
||||
//! Nothing interesting to see here! This is all pretty standard stuff, as far
|
||||
//! as winit apps are concerned.
|
||||
|
||||
use std::{collections::BTreeSet, sync::Arc};
|
||||
|
||||
use winit::{
|
||||
|
@ -1,3 +1,8 @@
|
||||
//! # Exporting geometry to 3MF
|
||||
//!
|
||||
//! Nothing interesting to see here! This is just a thin layer on top of
|
||||
//! [3mf-rs](threemf).
|
||||
|
||||
use std::{collections::BTreeMap, fs::File};
|
||||
|
||||
use crate::object::Object;
|
||||
|
@ -1 +1,10 @@
|
||||
//! # Extra stuff that didn't seem to fit anywhere else
|
||||
//!
|
||||
//! I'm not sure why this module needs to exist, honestly. Maybe it could be
|
||||
//! merged into [`geometry`](crate::geometry), but it's different from the code
|
||||
//! there, in that its mostly concerned with interfacing with an external
|
||||
//! library.
|
||||
//!
|
||||
//! Not sure. Maybe in the next prototype, this code will land somewhere else.
|
||||
|
||||
pub mod triangulate;
|
||||
|
@ -1,3 +1,7 @@
|
||||
//! # Converting faces into triangle meshes
|
||||
//!
|
||||
//! See [triangulate].
|
||||
|
||||
use std::{
|
||||
collections::{BTreeSet, VecDeque},
|
||||
mem,
|
||||
@ -12,6 +16,16 @@ use crate::{
|
||||
topology::face::Face,
|
||||
};
|
||||
|
||||
/// # Convert a face into a triangle mesh
|
||||
///
|
||||
/// So far, this is pretty limited, since all faces are assumed to be flat, and
|
||||
/// the half-edges that bound them are all straight. But it does support concave
|
||||
/// faces (and this ability is also used to support holes).
|
||||
///
|
||||
/// Once surfaces learn to generate a bunch of points to approximate their
|
||||
/// geometry, it shouldn't be too hard to expand the code here to adapt that,
|
||||
/// which would add support for curved surfaces. This is already using Delaunay
|
||||
/// under the hood, so it wouldn't be a big step.
|
||||
pub fn triangulate(face: &Face) -> TriMesh {
|
||||
let points = points(face);
|
||||
let triangles = triangles(&points);
|
||||
|
@ -1,3 +1,8 @@
|
||||
//! # Various geometry tools
|
||||
//!
|
||||
//! These are distinct from the core b-rep representation, for which this module
|
||||
//! is a dependency.
|
||||
|
||||
mod sketch;
|
||||
mod surface;
|
||||
mod tri_mesh;
|
||||
|
@ -10,11 +10,28 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
/// # A 2D sketch, which one way to create faces
|
||||
///
|
||||
/// So far, sketches are pretty limited: They are just a bunch of ordered
|
||||
/// points. Those points can be converted into the straight half-edges that
|
||||
/// bound a face.
|
||||
///
|
||||
/// You could create this struct manually, but there's also a [`From`]
|
||||
/// implementation that can create an instance of this struct from any iterator
|
||||
/// that yields points.
|
||||
///
|
||||
/// The next step here, would be to add support for curved edges. But this would
|
||||
/// need to be supported on the topology side first.
|
||||
pub struct Sketch {
|
||||
pub points: Vec<Point<2>>,
|
||||
}
|
||||
|
||||
impl Sketch {
|
||||
/// # Convert the sketch into a face
|
||||
///
|
||||
/// The `surface` parameter defines the plane which is then used to convert
|
||||
/// the 2D sketch into a 3D face. In the future, more surfaces than just
|
||||
/// planes would be supported, but we're not there yet.
|
||||
pub fn to_face(&self, surface: Handle<Surface>) -> Face {
|
||||
let mut vertices_by_local_point: BTreeMap<_, Vec<_>> = BTreeMap::new();
|
||||
let vertices = self
|
||||
|
@ -1,9 +1,30 @@
|
||||
use crate::math::{Plane, Point, Vector};
|
||||
|
||||
/// # A trait for encoding surface geometry
|
||||
///
|
||||
/// So far, this is mostly cosmetic, as the only implementor is [`Plane`]. I've
|
||||
/// started extracting the interface of that into this trait though, as a first
|
||||
/// step towards eventually supporting other kinds of surfaces.
|
||||
///
|
||||
/// I'd expect that this trait would need to be expanded before that can be
|
||||
/// fully realized.
|
||||
pub trait SurfaceGeometry {
|
||||
/// # Convert a surface-local point to 3D
|
||||
fn point_from_local(&self, point: Point<2>) -> Point<3>;
|
||||
|
||||
/// # Project a 3D point into the surface
|
||||
fn project_point(&self, point: Point<3>) -> Point<2>;
|
||||
|
||||
/// # Flip the surface
|
||||
///
|
||||
/// Maybe this can later merge with [`SurfaceGeometry::translate`] into a
|
||||
/// more general `transform` method.
|
||||
fn flip(&self) -> Box<dyn SurfaceGeometry>;
|
||||
|
||||
/// # Translate the surface
|
||||
///
|
||||
/// I expect this to transform into a more general `transform` method at
|
||||
/// some point. But so far, I haven't needed much more than this.
|
||||
fn translate(&self, offset: Vector<3>) -> Box<dyn SurfaceGeometry>;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,15 @@
|
||||
use super::Triangle;
|
||||
|
||||
/// # A triangle mesh
|
||||
///
|
||||
/// Triangle meshes are the uniform intermediate representation for geometry.
|
||||
/// The idea here is to have a single representation that is both (relatively)
|
||||
/// easy to generate and to operate on.
|
||||
///
|
||||
/// This is only intended as an _intermediate_ representation though! This isn't
|
||||
/// fully worked out yet in this experiment, but the idea is to keep the
|
||||
/// original objects that generated the triangle mesh around, so you can always
|
||||
/// generate a more accurate triangle mesh, if needed.
|
||||
#[derive(Debug)]
|
||||
pub struct TriMesh {
|
||||
pub triangles: Vec<MeshTriangle>,
|
||||
@ -12,15 +22,24 @@ impl TriMesh {
|
||||
}
|
||||
}
|
||||
|
||||
/// # Merge this triangle mesh with another
|
||||
///
|
||||
/// This just creates a new triangle mesh that has all the triangles from
|
||||
/// both meshes. Nothing more fancy than that, so far!
|
||||
pub fn merge(mut self, other: Self) -> Self {
|
||||
self.triangles.extend(other.triangles);
|
||||
self
|
||||
}
|
||||
|
||||
/// # Iterate over all the triangles in the mesh
|
||||
pub fn all_triangles(&self) -> impl Iterator<Item = Triangle<3>> {
|
||||
self.triangles.iter().map(|triangle| triangle.inner)
|
||||
}
|
||||
|
||||
/// # Iterate over the triangles in the mesh that are not marked internal
|
||||
///
|
||||
/// See [`MeshTriangle`] for an explanation of internal and external
|
||||
/// triangles.
|
||||
pub fn external_triangles(&self) -> impl Iterator<Item = Triangle<3>> {
|
||||
self.triangles.iter().filter_map(|triangle| {
|
||||
(!triangle.is_internal).then_some(triangle.inner)
|
||||
@ -28,6 +47,18 @@ impl TriMesh {
|
||||
}
|
||||
}
|
||||
|
||||
/// # A triangle in a triangle mesh
|
||||
///
|
||||
/// This is just a regular triangle, with an additional flag to mark it as
|
||||
/// internal.
|
||||
///
|
||||
/// Faces only ever have a single boundary. Holes are realized by having this
|
||||
/// boundary touch itself in one place, where it connects the inside and the
|
||||
/// outside.
|
||||
///
|
||||
/// The half-edges where that happens are marked as "internal", and so are any
|
||||
/// triangles created from them. This method can be used to filter those out,
|
||||
/// for example for export to external file formats.
|
||||
#[derive(Debug)]
|
||||
pub struct MeshTriangle {
|
||||
pub inner: Triangle<3>,
|
||||
|
@ -7,12 +7,16 @@ use crate::{
|
||||
|
||||
use super::{MeshTriangle, TriMesh};
|
||||
|
||||
/// # A triangle
|
||||
///
|
||||
/// This should probably move to [`math`](crate::math). Not sure!
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Triangle<const D: usize> {
|
||||
pub points: [Point<D>; 3],
|
||||
}
|
||||
|
||||
impl<const D: usize> Triangle<D> {
|
||||
/// # Compute the center point of the triangle
|
||||
pub fn center(&self) -> Point<D> {
|
||||
let [a, b, c] = self.points;
|
||||
let coords = (a.coords + b.coords + c.coords) / 3.;
|
||||
|
@ -1,3 +1,13 @@
|
||||
//! # Fornjot - Experiment 2024-12-09
|
||||
//!
|
||||
//! Please check the accompanying `README.md` for context and high-level
|
||||
//! documentation.
|
||||
//!
|
||||
//! As for the details, you're in the right place! I'd start with the [`model`],
|
||||
//! [`topology`], [`operations`], and [`geometry`] modules, roughly in that
|
||||
//! order. Those are core to the CAD stuff, while the rest is mostly there for
|
||||
//! support.
|
||||
|
||||
#![allow(clippy::module_inception)]
|
||||
|
||||
mod app;
|
||||
|
@ -1,3 +1,8 @@
|
||||
//! # Generic math types
|
||||
//!
|
||||
//! I'm not go document the types in here any further. It's just regular old
|
||||
//! math, nothing special.
|
||||
|
||||
mod bivector;
|
||||
mod plane;
|
||||
mod point;
|
||||
|
@ -6,6 +6,10 @@ use crate::{
|
||||
topology::surface::Surface,
|
||||
};
|
||||
|
||||
/// # The function that creates the current test model, a cube
|
||||
///
|
||||
/// Nothing really special about this. It's just the current test case that I'm
|
||||
/// using to develop the rest.
|
||||
pub fn model() -> HandleAny {
|
||||
let top = {
|
||||
let sketch = Sketch::from([
|
||||
|
@ -2,11 +2,30 @@ use std::{cmp::Ordering, fmt, ops::Deref, rc::Rc};
|
||||
|
||||
use super::{HandleAny, Object};
|
||||
|
||||
/// # A typed handle to an object
|
||||
///
|
||||
/// Handles provide a layer of identity to objects, enabling the same object to
|
||||
/// be shared from multiple locations in the object graph.
|
||||
///
|
||||
/// Right now, this doesn't make much of a difference, but eventually it's going
|
||||
/// to be important for various validation checks. (See the validation stuff in
|
||||
/// the current mainline code for more information on that.)
|
||||
///
|
||||
/// The longer-term idea here, is to use this as a reference to an object that
|
||||
/// is stored in a way that makes this object performant to access. Right now,
|
||||
/// we just allocate all objects within [`Rc`] though, as a placeholder.
|
||||
pub struct Handle<T> {
|
||||
inner: Rc<T>,
|
||||
}
|
||||
|
||||
impl<T> Handle<T> {
|
||||
/// # Create a new handle
|
||||
///
|
||||
/// Eventually, this type probably won't have a public constructor, and
|
||||
/// you'll create a `Handle` via some kind of collection/arena thing.
|
||||
///
|
||||
/// For now, objects just live on the heap, in reference-counted ([`Rc`])
|
||||
/// allocations.
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(inner),
|
||||
@ -18,10 +37,12 @@ impl<T> Handle<T>
|
||||
where
|
||||
T: Object + 'static,
|
||||
{
|
||||
/// # Create an untyped handle that refers to the same object
|
||||
pub fn to_any(&self) -> HandleAny {
|
||||
self.clone().into_any()
|
||||
}
|
||||
|
||||
/// # Convert this handle into an untyped one that refers to the same object
|
||||
pub fn into_any(self) -> HandleAny {
|
||||
HandleAny { inner: self.inner }
|
||||
}
|
||||
|
@ -4,6 +4,12 @@ use crate::geometry::TriMesh;
|
||||
|
||||
use super::Object;
|
||||
|
||||
/// # An untyped handle that can be used to abstract over objects
|
||||
///
|
||||
/// Can be used wherever you need to iterate over objects of various types.
|
||||
///
|
||||
/// See documentation of `Handle` for more context on handles and object
|
||||
/// storage.
|
||||
#[derive(Clone)]
|
||||
pub struct HandleAny {
|
||||
pub(super) inner: Rc<dyn Object>,
|
||||
|
@ -4,6 +4,16 @@ use crate::geometry::TriMesh;
|
||||
|
||||
use super::HandleAny;
|
||||
|
||||
/// # A trait that is implemented by all "objects", whatever those are
|
||||
///
|
||||
/// This trait is the problem child of this experiment. I wanted to use it to
|
||||
/// create a much more detailed and interactive view of objects, but this ended
|
||||
/// up as just a simple tree that is rendered next to the object.
|
||||
///
|
||||
/// It's probably safe to ignore most of the stuff here. My current plan is to
|
||||
/// strip this down to its essentials, completely remove the object tree from
|
||||
/// the debug view, and experiment with other means of providing visibility into
|
||||
/// how shapes are structured and constructed.
|
||||
pub trait Object {
|
||||
fn display(&self, f: &mut fmt::Formatter) -> fmt::Result;
|
||||
fn tri_mesh(&self) -> TriMesh;
|
||||
|
@ -6,6 +6,11 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
/// # Extension trait for objects that can be connected
|
||||
///
|
||||
/// At this point, this is only implemented for faces, to connect to of them,
|
||||
/// creating a solid. It's conceivable to also implement it for half-edges, for
|
||||
/// example, to connect those into a face.
|
||||
pub trait ConnectExt {
|
||||
/// # Connect two faces by creating a side wall of faces from their vertices
|
||||
///
|
||||
|
@ -3,7 +3,12 @@ use crate::{
|
||||
topology::{face::Face, surface::Surface},
|
||||
};
|
||||
|
||||
/// # Extension trait for objects that can be flipped
|
||||
pub trait FlipExt {
|
||||
/// # Flip a face or surface
|
||||
///
|
||||
/// This might be subsumed by a more general "transform" operation later.
|
||||
/// Not sure!
|
||||
fn flip(&self) -> Self;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! # Various operations that can create and transform objects
|
||||
|
||||
pub mod connect;
|
||||
pub mod flip;
|
||||
pub mod sweep;
|
||||
|
@ -6,6 +6,10 @@ use crate::{
|
||||
|
||||
use super::{connect::ConnectExt, flip::FlipExt, translate::TranslateExt};
|
||||
|
||||
/// # Extension trait for sweeping things
|
||||
///
|
||||
/// Right now, this is only implemented for faces, but it could also get
|
||||
/// implemented for half-edges or solids later.
|
||||
pub trait SweepExt {
|
||||
/// # Sweep a face along a path, creating a solid
|
||||
///
|
||||
|
@ -6,6 +6,12 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
/// # Extension trait for things that can be translated
|
||||
///
|
||||
/// This is the most versatile operation right now, as it's implemented for many
|
||||
/// different types of objects.
|
||||
///
|
||||
/// I expect this to morph into a more general "transform" operation over time.
|
||||
pub trait TranslateExt {
|
||||
fn translate(&self, offset: impl Into<Vector<3>>) -> Self;
|
||||
}
|
||||
|
@ -1,3 +1,29 @@
|
||||
//! # Rendering infrastructure
|
||||
//!
|
||||
//! This has mostly been inherited from the previous experiment, with some
|
||||
//! extensions. The remainder of this documtation is mostly copied from that
|
||||
//! previous experiment.
|
||||
//!
|
||||
//! Even though most of the work for this prototype went into the renderer, it
|
||||
//! is not the most interesting aspect, and I'm not going to document it in
|
||||
//! detail. It's a pretty basic architecture, optimized for the speed of having
|
||||
//! written it, not speed of rendering.
|
||||
//!
|
||||
//! The most interesting aspect in terms of what this experiment could mean for
|
||||
//! Fornjot, is that this renderer has a direct dependency on
|
||||
//! [`geometry`](crate::geometry). Versus the current Fornjot renderer, which
|
||||
//! only communicates with the CAD core through another interop crate.
|
||||
//!
|
||||
//! I'm pretty sure that whatever happens with these experiments, I'll go with
|
||||
//! the simpler approach going forward. I'm not even sure anymore what the
|
||||
//! thinking behind the original design was (it's been years).
|
||||
//!
|
||||
//! I probably overestimated the importance of making things pluggable, and
|
||||
//! making parts of Fornjot usable in isolation. Going forward, I'm viewing the
|
||||
//! renderer as something that is very purpose-built for the needs of developing
|
||||
//! Fornjot. Not something I'd expect anybody building on top of Fornjot would
|
||||
//! want to use, except maybe to get started.
|
||||
|
||||
mod geometry;
|
||||
mod pipeline;
|
||||
mod renderer;
|
||||
|
@ -10,6 +10,17 @@ use crate::{
|
||||
|
||||
use super::{half_edge::HalfEdge, surface::Surface, vertex::Vertex};
|
||||
|
||||
/// # A face
|
||||
///
|
||||
/// Faces are defined by a surface (which, so far, is always a plane) and a
|
||||
/// cycle of half-edges that bound the face on that surface.
|
||||
///
|
||||
/// Faces are the boundary of any solid. Solids can touch themselves, however,
|
||||
/// to connect their external boundary to cavities on the inside, or enclose a
|
||||
/// hole through the solid.
|
||||
///
|
||||
/// The faces in parts of the boundary where solids touch themselves are called
|
||||
/// "internal".
|
||||
#[derive(Debug)]
|
||||
pub struct Face {
|
||||
pub surface: Handle<Surface>,
|
||||
@ -18,6 +29,10 @@ pub struct Face {
|
||||
}
|
||||
|
||||
impl Face {
|
||||
/// # Create a new face from its component parts
|
||||
///
|
||||
/// The more interesting way to create a face would be via a
|
||||
/// [`Sketch`](crate::geometry::Sketch).
|
||||
pub fn new(
|
||||
surface: Handle<Surface>,
|
||||
half_edges: impl IntoIterator<Item = Handle<HalfEdge>>,
|
||||
@ -30,6 +45,11 @@ impl Face {
|
||||
}
|
||||
}
|
||||
|
||||
/// # Iterate over the half-edges of the face
|
||||
///
|
||||
/// In addition to the [`HalfEdge`] itself, which contains the vertex where
|
||||
/// it starts, the vertex where the half-edge ends (the start vertex of the
|
||||
/// next half-edge) is also provided.
|
||||
pub fn half_edges_with_end_vertex(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&Handle<HalfEdge>, &Handle<Vertex>)> {
|
||||
|
@ -7,6 +7,16 @@ use crate::{
|
||||
|
||||
use super::vertex::Vertex;
|
||||
|
||||
/// # A half-edge
|
||||
///
|
||||
/// Half-edges bound faces. A half-edge only contains the vertex where it
|
||||
/// starts. The end vertex is implicit (it is the start vertex of the next
|
||||
/// half-edge in the same face). By doing it like this, each half-edge "owns"
|
||||
/// its vertex, which simplifies the object graph, making it easier to change.
|
||||
///
|
||||
/// Since a face only has a single boundary, that boundary needs to touch itself
|
||||
/// to connect the outside of the face with any holes on the inside. The
|
||||
/// half-edges that touch other half-edges are marked as "internal".
|
||||
pub struct HalfEdge {
|
||||
pub start: Handle<Vertex>,
|
||||
pub is_internal: bool,
|
||||
|
@ -1,3 +1,9 @@
|
||||
//! # Topological b-rep primitives
|
||||
//!
|
||||
//! These are just some basics so far, to get something started with flat faces
|
||||
//! and straight edges. I expect this to grow over the next experiments, as more
|
||||
//! advanced geometry starts being supported.
|
||||
|
||||
pub mod face;
|
||||
pub mod half_edge;
|
||||
pub mod solid;
|
||||
|
@ -7,12 +7,19 @@ use crate::{
|
||||
|
||||
use super::face::Face;
|
||||
|
||||
/// # A solid
|
||||
///
|
||||
/// Solids are 3D objects that are bounded by faces.
|
||||
#[derive(Clone)]
|
||||
pub struct Solid {
|
||||
faces: Vec<Handle<Face>>,
|
||||
}
|
||||
|
||||
impl Solid {
|
||||
/// # Create a solid from its component parts
|
||||
///
|
||||
/// Check out [`operations`](crate::operations) for more interesting ways to
|
||||
/// create solids.
|
||||
pub fn new(faces: impl IntoIterator<Item = Handle<Face>>) -> Self {
|
||||
Self {
|
||||
faces: faces.into_iter().collect(),
|
||||
|
@ -2,6 +2,13 @@ use std::fmt;
|
||||
|
||||
use crate::geometry::SurfaceGeometry;
|
||||
|
||||
/// # A surface
|
||||
///
|
||||
/// Surfaces are infinite 2D objects in 3D space. They are what defines faces,
|
||||
/// which are bounded sections on a surface.
|
||||
///
|
||||
/// Surfaces own a reference to an implementation of `SurfaceGeometry`, which is
|
||||
/// what defines them. So far, only planes are supported though.
|
||||
pub struct Surface {
|
||||
pub geometry: Box<dyn SurfaceGeometry>,
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use crate::{
|
||||
object::{HandleAny, Object},
|
||||
};
|
||||
|
||||
/// # A vertex
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Vertex {
|
||||
pub point: Point<3>,
|
||||
|
@ -5,6 +5,10 @@ use crate::{
|
||||
object::{HandleAny, Object},
|
||||
};
|
||||
|
||||
/// # This is just some connecting tissue between CAD objects and the renderer
|
||||
///
|
||||
/// This is part of the code that didn't work out as I hoped. I expect to remove
|
||||
/// it in future experiments.
|
||||
#[derive(Clone)]
|
||||
pub struct OperationView {
|
||||
operation: HandleAny,
|
||||
|
Loading…
Reference in New Issue
Block a user