Skip to content

Commit b6bfe22

Browse files
patowenRalith
authored andcommitted
Rename "length" to "depth" and make "parent" more useful
The term "depth" is arguably more intuitive when expressing how far away from the root a graph's node is, so this should hopefully reduce the barrier to entry for learning the Hypermine codebase. As for "parent", previously, it was often interpreted as though every node (except the root) has one parent, even though most logic took all "shorter neighbors" into account, so "parent" now refers to any neighbor node closer to the root. The old meaning of parent is renamed to "primary parent", which is the parent with the first side in enum order.
1 parent ac863a4 commit b6bfe22

File tree

5 files changed

+81
-95
lines changed

5 files changed

+81
-95
lines changed

client/src/graphics/voxels/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ impl Voxels {
181181
}
182182
}
183183
}
184-
let node_is_odd = sim.graph.length(node) & 1 != 0;
184+
let node_is_odd = sim.graph.depth(node) & 1 != 0;
185185
extractions.push(ExtractTask {
186186
index: scratch_slot,
187187
indirect_offset: self.surfaces.indirect_offset(slot.0),

common/src/graph.rs

Lines changed: 72 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,22 @@ impl Graph {
4646

4747
/// Node and vertex that the cube around a certain vertex is canonically assigned to.
4848
///
49-
/// Each cube is said to be canonically assigned to the shortest of the nodes it touches.
50-
///
51-
/// A node's length is defined as a its distance from the root node.
49+
/// Each cube is said to be canonically assigned to node it touches that is closest to the root.
5250
pub fn canonicalize(&self, mut chunk: ChunkId) -> Option<ChunkId> {
5351
for side in chunk.vertex.canonical_sides().into_iter() {
5452
// missing neighbors are always longer
5553
if let Some(neighbor) = self.neighbor(chunk.node, side) {
56-
if self.length(neighbor) < self.length(chunk.node) {
54+
if self.depth(neighbor) < self.depth(chunk.node) {
5755
chunk.node = neighbor;
5856
}
5957
}
6058
}
6159
Some(chunk)
6260
}
6361

64-
/// Returns all of the sides between the provided node and its shorter neighbors.
65-
///
66-
/// A node's length is its distance from the root node.
67-
pub fn descenders(
68-
&self,
69-
node: NodeId,
70-
) -> impl ExactSizeIterator<Item = (Side, NodeId)> + use<> {
71-
let node_length = self.length(node);
62+
/// Returns the given node's parents along with the side each shares with the node.
63+
pub fn parents(&self, node: NodeId) -> impl ExactSizeIterator<Item = (Side, NodeId)> + use<> {
64+
let node_depth = self.depth(node);
7265

7366
let mut results = [None; 3];
7467
let mut len = 0;
@@ -77,7 +70,7 @@ impl Graph {
7770
// filtering out not-yet-allocated neighbors is fine since
7871
// they have to be longer than us not to be allocated yet
7972
if let Some(neighbor_node) = self.neighbor(node, side) {
80-
if self.length(neighbor_node) < node_length {
73+
if self.depth(neighbor_node) < node_depth {
8174
results[len] = Some((side, neighbor_node));
8275
len += 1;
8376
}
@@ -92,10 +85,10 @@ impl Graph {
9285
self.nodes[&node].neighbors[which as usize]
9386
}
9487

95-
/// The number of steps required to get from the origin to the given node.
88+
/// The number of steps required to get from the root to the given node.
9689
#[inline]
97-
pub fn length(&self, node: NodeId) -> u32 {
98-
self.nodes[&node].length
90+
pub fn depth(&self, node: NodeId) -> u32 {
91+
self.nodes[&node].depth
9992
}
10093

10194
/// Given a `transform` relative to a `reference` node, computes the node
@@ -127,58 +120,55 @@ impl Graph {
127120
}
128121

129122
#[inline]
130-
pub fn parent(&self, node: NodeId) -> Option<Side> {
131-
self.nodes[&node].parent_side
123+
pub fn primary_parent_side(&self, node: NodeId) -> Option<Side> {
124+
self.nodes[&node].primary_parent_side
132125
}
133126

134127
/// Iterate over every node except the root via a breadth-first search in
135128
/// the form of ordered pairs `(Side, NodeId)` where `NodeId` is the ID of
136-
/// its parent node, and `Side` is the side shared by the node and the
137-
/// parent node. This can be used to construct a copy of the graph with the
129+
/// its primary parent node, and `Side` is the side shared by the node and the
130+
/// primary parent node. This can be used to construct a copy of the graph with the
138131
/// same set of nodes.
139132
pub fn tree(&self) -> TreeIter<'_> {
140133
TreeIter::new(self)
141134
}
142135

143136
/// Ensures that the neighbour node at a particular side of a particular node exists in the graph,
144-
/// as well as the nodes from the origin to the neighbour node.
137+
/// as well as the nodes from the root to the neighbour node.
145138
pub fn ensure_neighbor(&mut self, node: NodeId, side: Side) -> NodeId {
146-
self.nodes[&node].neighbors[side as usize]
147-
.unwrap_or_else(|| self.insert_neighbor(node, side))
139+
// A node cannot be created before any of its parents, so if a new node is created, it is guaranteed
140+
// to be a child node.
141+
self.nodes[&node].neighbors[side as usize].unwrap_or_else(|| self.insert_child(node, side))
148142
}
149143

150-
/// Whether `node`'s neighbor along `side` is closer than it to the origin
151-
fn is_descender(&self, node: NodeId, side: Side) -> bool {
144+
/// Whether `node`'s neighbor along `side` is closer than it to the root
145+
fn is_parent_side(&self, node: NodeId, side: Side) -> bool {
152146
let v = &self.nodes[&node];
153-
v.neighbors[side as usize].is_some_and(|x| self.nodes[&x].length < v.length)
147+
v.neighbors[side as usize].is_some_and(|x| self.nodes[&x].depth < v.depth)
154148
}
155149

156-
/// Inserts the neighbor of the given node at the given side into the graph, ensuring that all
157-
/// shorter nodes it depends on are created first.
158-
pub fn insert_neighbor(&mut self, node: NodeId, side: Side) -> NodeId {
159-
// To help improve readability, we use the term "subject" to refer to the not-yet-created neighbor node, since the term.
160-
// "neighbor" is already used in many other places.
161-
162-
// An assumption made by this function is that `side` is not a descender of `node`. This is a safe assumption to make
163-
// because a node cannot be constructed before its shorter neighbors. The call to `populate_shorter_neighbors_of_neighbor`
164-
// guarantees this.
165-
let shorter_neighbors_of_subject = self.populate_shorter_neighbors_of_subject(node, side);
150+
/// Inserts the child of the given node at the given side into the graph, ensuring that all
151+
/// its parents are created first.
152+
pub fn insert_child(&mut self, node: NodeId, side: Side) -> NodeId {
153+
// To help improve readability, we use the term "subject" to refer to the not-yet-created child node, since the term
154+
// "child" can be ambiguous.
155+
let parents_of_subject = self.populate_parents_of_subject(node, side);
166156

167-
// Select the side along the canonical path from the origin to this node. This is guaranteed
168-
// to be the first entry of the `shorter_neighbors_of_subject` iterator.
169-
let (parent_side, parent) = shorter_neighbors_of_subject.clone().next().unwrap();
157+
// Select the side along the canonical path from the root to this node. This is guaranteed
158+
// to be the first entry of the `parents_of_subject` iterator.
159+
let (primary_parent_side, primary_parent) = parents_of_subject.clone().next().unwrap();
170160
let mut hasher = Hasher::new();
171-
hasher.update(&parent.0.to_le_bytes());
172-
hasher.update(&[parent_side as u8]);
161+
hasher.update(&primary_parent.0.to_le_bytes());
162+
hasher.update(&[primary_parent_side as u8]);
173163
let mut xof = hasher.finalize_xof();
174164
let mut hash = [0; 16];
175165
xof.fill(&mut hash);
176166
let id = NodeId(u128::from_le_bytes(hash));
177167

178-
let length = self.nodes[&node].length + 1;
168+
let depth = self.nodes[&node].depth + 1;
179169
self.nodes
180-
.insert(id, NodeContainer::new(Some(parent_side), length));
181-
for (side, neighbor) in shorter_neighbors_of_subject {
170+
.insert(id, NodeContainer::new(Some(primary_parent_side), depth));
171+
for (side, neighbor) in parents_of_subject {
182172
self.link_neighbors(id, neighbor, side);
183173
}
184174
id
@@ -194,47 +184,45 @@ impl Graph {
194184
NodeId(hash)
195185
}
196186

197-
/// Ensure all shorter neighbors of a not-yet-created neighbor node (which we call the "subject") exist, and return them
198-
/// (including the given node) in the form of ordered pairs containing the side they share with this not-yet-created
199-
/// neighbor node, and their node ID. These ordered pairs will be sorted by side, based on enum order.
200-
fn populate_shorter_neighbors_of_subject(
187+
/// Ensure all parents of a not-yet-created child node (which we call the "subject") exist, and return them
188+
/// (including the given node) in the form of ordered pairs containing the side they share with this "subject",
189+
/// and their node ID. These ordered pairs will be sorted by side, based on enum order.
190+
fn populate_parents_of_subject(
201191
&mut self,
202192
node: NodeId,
203193
side: Side,
204194
) -> impl Iterator<Item = (Side, NodeId)> + Clone + use<> {
205-
let mut shorter_neighbors_of_subject = [None; 3]; // Maximum number of shorter neighbors is 3
195+
let mut parents_of_subject = [None; 3]; // Maximum number of parents is 3
206196
let mut count = 0;
207-
for candidate_descender in Side::iter() {
208-
if candidate_descender == side {
197+
for candidate_parent_side in Side::iter() {
198+
if candidate_parent_side == side {
209199
// The given node is included in the list of returned nodes.
210-
shorter_neighbors_of_subject[count] = Some((side, node));
200+
parents_of_subject[count] = Some((side, node));
211201
count += 1;
212-
} else if candidate_descender.adjacent_to(side)
213-
&& self.is_descender(node, candidate_descender)
202+
} else if candidate_parent_side.adjacent_to(side)
203+
&& self.is_parent_side(node, candidate_parent_side)
214204
{
215-
// This branch covers shorter neighbors of the subject other than the given node.
216-
// This is non-obvious, as it relies on the fact that a side is a descender of the subject
217-
// exactly when it is a descender of the given node. This is not true in general, but it is true
218-
// when the descender in question is adjacent to the side shared by the given node and the subject.
219-
// That is what allows the `self.is_descender(node, candidate_descender)` condition to behave as desired.
220-
221-
// We would like to return (and recursively create if needed) the shorter neighbor of the subject. This means that
222-
// if we label the shared side A and the descender B, the path we would like to follow from the given node is AB,
223-
// since A will take us to the subject, and then B will take us to its shorter neighbor. However, taking
205+
// This branch covers parents of the subject other than the given node.
206+
// This is non-obvious, as it relies on the fact that a side is a parent side of the subject
207+
// exactly when it is a parent side of the given node. This is not true in general, but it is true
208+
// when the parent side in question is adjacent to the side shared by the given node and the subject.
209+
// That is what allows the `self.is_parent_side(node, candidate_parent_side)` condition to behave as desired.
210+
211+
// We would like to return (and recursively create if needed) the parent of the subject. This means that
212+
// if we label the shared side A and the parent side B, the path we would like to follow from the given node is AB,
213+
// since A will take us to the subject, and then B will take us to its parent. However, taking
224214
// the path AB is impossible because it would require the subject to already be in the graph. Fortuantely,
225215
// we can take the path BA instead because that will reach the same node, thanks to the fact that each edge
226216
// is shared by 4 dodecas.
227-
let shorter_neighbor_of_node = self.neighbor(node, candidate_descender).unwrap();
228-
let shorter_neighbor_of_subject =
229-
self.ensure_neighbor(shorter_neighbor_of_node, side);
230-
shorter_neighbors_of_subject[count] =
231-
Some((candidate_descender, shorter_neighbor_of_subject));
217+
let parent_of_node = self.neighbor(node, candidate_parent_side).unwrap();
218+
let parent_of_subject = self.ensure_neighbor(parent_of_node, side);
219+
parents_of_subject[count] = Some((candidate_parent_side, parent_of_subject));
232220
count += 1;
233221
} else {
234-
// The `candidate_descender` is not a descender of the subject, so no action is necessary.
222+
// The `candidate_parent_side` is not a parent side of the subject, so no action is necessary.
235223
}
236224
}
237-
shorter_neighbors_of_subject.into_iter().flatten()
225+
parents_of_subject.into_iter().flatten()
238226
}
239227

240228
/// Register `a` and `b` as adjacent along `side`
@@ -280,18 +268,18 @@ impl NodeId {
280268

281269
struct NodeContainer {
282270
value: Node,
283-
parent_side: Option<Side>,
284-
/// Distance to origin via parents
285-
length: u32,
271+
primary_parent_side: Option<Side>,
272+
/// Distance to root via parents
273+
depth: u32,
286274
neighbors: [Option<NodeId>; Side::VALUES.len()],
287275
}
288276

289277
impl NodeContainer {
290-
fn new(parent_side: Option<Side>, length: u32) -> Self {
278+
fn new(primary_parent_side: Option<Side>, depth: u32) -> Self {
291279
Self {
292280
value: Node::default(),
293-
parent_side,
294-
length,
281+
primary_parent_side,
282+
depth,
295283
neighbors: [None; Side::VALUES.len()],
296284
}
297285
}
@@ -318,7 +306,7 @@ impl<'a> TreeIter<'a> {
318306
result
319307
}
320308

321-
// Returns the next Node in the traversal. The iterator returns its parent and parent side.
309+
// Returns the next Node in the traversal. The iterator returns its primary parent and primary parent side.
322310
fn next_node(&mut self) -> Option<&NodeContainer> {
323311
let node_id = self.queue.pop_front()?;
324312
let node = &self.nodes[&node_id];
@@ -339,7 +327,7 @@ impl Iterator for TreeIter<'_> {
339327

340328
fn next(&mut self) -> Option<Self::Item> {
341329
let node = self.next_node()?;
342-
let side = node.parent_side.unwrap();
330+
let side = node.primary_parent_side.unwrap();
343331
Some((side, node.neighbors[side as usize].unwrap()))
344332
}
345333
}
@@ -352,7 +340,7 @@ mod tests {
352340
use approx::*;
353341

354342
#[test]
355-
fn shorter_longer_neighbor_relationships() {
343+
fn parent_child_relationships() {
356344
let mut graph = Graph::new(1);
357345
assert_eq!(graph.len(), 1);
358346
let a = graph.ensure_neighbor(NodeId::ROOT, Side::A);
@@ -361,18 +349,18 @@ mod tests {
361349
assert_eq!(graph.len(), 2);
362350
assert_eq!(a, a2);
363351
assert_eq!(graph.ensure_neighbor(a, Side::A), NodeId::ROOT);
364-
assert_eq!(graph.nodes[&a].length, 1);
352+
assert_eq!(graph.nodes[&a].depth, 1);
365353
let b = graph.ensure_neighbor(NodeId::ROOT, Side::B);
366354
assert_eq!(graph.len(), 3);
367355
assert_eq!(graph.ensure_neighbor(b, Side::B), NodeId::ROOT);
368356
let c = graph.ensure_neighbor(a, Side::C);
369357
assert!(graph.len() > 4);
370358
assert_eq!(graph.ensure_neighbor(c, Side::C), a);
371-
assert_eq!(graph.nodes[&c].length, 2);
359+
assert_eq!(graph.nodes[&c].depth, 2);
372360
}
373361

374362
#[test]
375-
fn longer_nodes_have_common_neighbor() {
363+
fn children_have_common_neighbor() {
376364
let mut graph = Graph::new(1);
377365
let a = graph.ensure_neighbor(NodeId::ROOT, Side::A);
378366
let b = graph.ensure_neighbor(NodeId::ROOT, Side::B);
@@ -395,7 +383,7 @@ mod tests {
395383
);
396384
assert!(common.contains(&NodeId::ROOT));
397385
let other = common.into_iter().find(|&x| x != NodeId::ROOT).unwrap();
398-
assert_eq!(graph.nodes[&other].length, 2);
386+
assert_eq!(graph.nodes[&other].depth, 2);
399387
}
400388

401389
#[test]
@@ -420,7 +408,7 @@ mod tests {
420408
ensure_nearby(&mut a, &Position::origin(), 3.0);
421409
let mut b = Graph::new(1);
422410
for (side, parent) in a.tree() {
423-
b.insert_neighbor(parent, side);
411+
b.insert_child(parent, side);
424412
}
425413
assert_eq!(a.len(), b.len());
426414
for (c, d) in a.tree().zip(b.tree()) {

common/src/node.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ impl Graph {
4141
return;
4242
}
4343

44-
for (_, parent) in self.descenders(node_id) {
44+
for (_, parent) in self.parents(node_id) {
4545
self.ensure_node_state(parent);
4646
}
4747

4848
let node_state = self
49-
.parent(node_id)
49+
.primary_parent_side(node_id)
5050
.map(|i| {
5151
let parent_state = self.node_state(self.neighbor(node_id, i).unwrap());
5252
parent_state.child(self, node_id, i)

common/src/worldgen.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,10 @@ impl NodeState {
8686
}
8787

8888
pub fn child(&self, graph: &Graph, node: NodeId, side: Side) -> Self {
89-
let mut d = graph
90-
.descenders(node)
91-
.map(|(s, n)| (s, graph.node_state(n)));
89+
let mut d = graph.parents(node).map(|(s, n)| (s, graph.node_state(n)));
9290
let enviro = match (d.next(), d.next()) {
9391
(Some(_), None) => {
94-
let parent_side = graph.parent(node).unwrap();
92+
let parent_side = graph.primary_parent_side(node).unwrap();
9593
let parent_node = graph.neighbor(node, parent_side).unwrap();
9694
let parent_state = graph.node_state(parent_node);
9795
let spice = graph.hash_of(node) as u64;

server/src/sim.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ impl Sim {
8181
pub fn save(&mut self, save: &mut save::Save) -> Result<(), save::DbError> {
8282
fn path_from_origin(graph: &Graph, mut node: NodeId) -> Vec<u8> {
8383
let mut result = Vec::new();
84-
while let Some(parent) = graph.parent(node) {
85-
result.push(parent as u8);
86-
node = graph.neighbor(node, parent).unwrap();
84+
while let Some(primary_parent) = graph.primary_parent_side(node) {
85+
result.push(primary_parent as u8);
86+
node = graph.neighbor(node, primary_parent).unwrap();
8787
}
8888
result.reverse();
8989
result
@@ -790,7 +790,7 @@ impl AccumulatedChanges {
790790
.fresh_nodes
791791
.iter()
792792
.filter_map(|&id| {
793-
let side = graph.parent(id)?;
793+
let side = graph.primary_parent_side(id)?;
794794
Some(FreshNode {
795795
side,
796796
parent: graph.neighbor(id, side).unwrap(),

0 commit comments

Comments
 (0)