Getting signals across

- Fixed signaling edge groups not being cleared when a signal migrates graphs
- Graphs without any signals now create a fallback group
- Fixed removed signals not clearing signal groups of previously affected edges
- Signaling sections now use a fixed set of colours
- Colours of signaling sections now try to be distinct from neighbouring sections
- Colours of signal sections now get synched to the client
- Edges now keep track of any intersections with bezier turns that cannot be part of the underlying graph structure
- Signaling sections now consider all other intersecting sections when checking whether they are occupied
- Holding a signal block now highlights nearby tracks with their respective section colour
- Graphs now keep track of a boundary box
- Track edges now carry back-references to their nodes
- Graph debugger moved from keypress to f3
This commit is contained in:
simibubi 2022-04-25 17:53:45 +02:00
parent 423bb407f7
commit 3848221712
30 changed files with 1198 additions and 508 deletions

View file

@ -57,7 +57,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
} }
public BezierConnection secondary() { public BezierConnection secondary() {
return new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(), normals.swap(), false, hasGirder); return new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(), normals.swap(), !primary, hasGirder);
} }
public BezierConnection(CompoundTag compound, BlockPos localTo) { public BezierConnection(CompoundTag compound, BlockPos localTo) {

View file

@ -13,23 +13,20 @@ import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.mutable.MutableObject;
import org.lwjgl.glfw.GLFW;
import com.simibubi.create.AllKeys;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.KineticDebugger;
import com.simibubi.create.content.logistics.trains.entity.Train; import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.entity.TrainPacket; import com.simibubi.create.content.logistics.trains.entity.TrainPacket;
import com.simibubi.create.content.logistics.trains.management.display.GlobalTrainDisplayData; import com.simibubi.create.content.logistics.trains.management.display.GlobalTrainDisplayData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.AllPackets;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.DistExecutor;
@ -56,7 +53,14 @@ public class GlobalRailwayManager {
loadTrackData(serverPlayer.getServer()); loadTrackData(serverPlayer.getServer());
trackNetworks.values() trackNetworks.values()
.forEach(g -> sync.sendFullGraphTo(g, serverPlayer)); .forEach(g -> sync.sendFullGraphTo(g, serverPlayer));
sync.sendEdgeGroups(signalEdgeGroups.keySet(), serverPlayer); ArrayList<SignalEdgeGroup> asList = new ArrayList<>(signalEdgeGroups.values());
sync.sendEdgeGroups(asList.stream()
.map(g -> g.id)
.toList(),
asList.stream()
.map(g -> g.color)
.toList(),
serverPlayer);
for (Train train : trains.values()) for (Train train : trains.values())
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> serverPlayer), AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> serverPlayer),
new TrainPacket(train, true)); new TrainPacket(train, true));
@ -119,19 +123,32 @@ public class GlobalRailwayManager {
return trackNetworks.computeIfAbsent(graphID, uid -> new TrackGraph(graphID)); return trackNetworks.computeIfAbsent(graphID, uid -> new TrackGraph(graphID));
} }
public void putGraphWithDefaultGroup(TrackGraph graph) {
SignalEdgeGroup group = new SignalEdgeGroup(graph.id);
signalEdgeGroups.put(graph.id, group);
sync.edgeGroupCreated(graph.id, group.color);
putGraph(graph);
}
public void putGraph(TrackGraph graph) { public void putGraph(TrackGraph graph) {
trackNetworks.put(graph.id, graph); trackNetworks.put(graph.id, graph);
markTracksDirty(); markTracksDirty();
} }
public void removeGraph(TrackGraph railGraph) { public void removeGraphAndGroup(TrackGraph graph) {
trackNetworks.remove(railGraph.id); signalEdgeGroups.remove(graph.id);
sync.edgeGroupRemoved(graph.id);
removeGraph(graph);
}
public void removeGraph(TrackGraph graph) {
trackNetworks.remove(graph.id);
markTracksDirty(); markTracksDirty();
} }
public void updateSplitGraph(TrackGraph graph) { public void updateSplitGraph(TrackGraph graph) {
Set<TrackGraph> disconnected = graph.findDisconnectedGraphs(null); Set<TrackGraph> disconnected = graph.findDisconnectedGraphs(null);
disconnected.forEach(this::putGraph); disconnected.forEach(this::putGraphWithDefaultGroup);
if (!disconnected.isEmpty()) { if (!disconnected.isEmpty()) {
sync.graphSplit(graph, disconnected); sync.graphSplit(graph, disconnected);
markTracksDirty(); markTracksDirty();
@ -159,10 +176,7 @@ public class GlobalRailwayManager {
} }
public void tick(Level level) { public void tick(Level level) {
ResourceLocation location2 = DimensionType.OVERWORLD_LOCATION.location(); if (level.dimension() != Level.OVERWORLD)
ResourceLocation location = level.dimension()
.location();
if (!location.equals(location2))
return; return;
for (SignalEdgeGroup group : signalEdgeGroups.values()) { for (SignalEdgeGroup group : signalEdgeGroups.values()) {
@ -170,8 +184,10 @@ public class GlobalRailwayManager {
group.reserved = null; group.reserved = null;
} }
for (TrackGraph graph : trackNetworks.values()) for (TrackGraph graph : trackNetworks.values()) {
graph.tickPoints(true); graph.tickPoints(true);
graph.resolveIntersectingEdgeGroups(level);
}
tickTrains(level); tickTrains(level);
@ -182,12 +198,12 @@ public class GlobalRailwayManager {
if (GlobalTrainDisplayData.updateTick) if (GlobalTrainDisplayData.updateTick)
GlobalTrainDisplayData.refresh(); GlobalTrainDisplayData.refresh();
// if (AllKeys.isKeyDown(GLFW.GLFW_KEY_K)) // if (AllKeys.isKeyDown(GLFW.GLFW_KEY_H) && AllKeys.altDown())
// trackNetworks.values() // for (TrackGraph trackGraph : trackNetworks.values())
// .forEach(TrackGraph::debugViewReserved); // TrackGraphVisualizer.debugViewSignalData(trackGraph);
// if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && AllKeys.altDown()) // if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && AllKeys.altDown())
// trackNetworks.values() // for (TrackGraph trackGraph : trackNetworks.values())
// .forEach(TrackGraph::debugViewNodes); // TrackGraphVisualizer.debugViewNodes(trackGraph);
} }
private void tickTrains(Level level) { private void tickTrains(Level level) {
@ -218,13 +234,16 @@ public class GlobalRailwayManager {
} }
} }
public void tickSignalOverlay() {
if (!KineticDebugger.isActive())
for (TrackGraph trackGraph : trackNetworks.values())
TrackGraphVisualizer.visualiseSignalEdgeGroups(trackGraph);
}
public void clientTick() { public void clientTick() {
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_H) && !AllKeys.altDown()) if (KineticDebugger.isActive())
trackNetworks.values() for (TrackGraph trackGraph : trackNetworks.values())
.forEach(TrackGraph::debugViewSignalData); TrackGraphVisualizer.debugViewGraph(trackGraph);
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && !AllKeys.altDown())
trackNetworks.values()
.forEach(TrackGraph::debugViewNodes);
} }
public GlobalRailwayManager sided(LevelAccessor level) { public GlobalRailwayManager sided(LevelAccessor level) {

View file

@ -6,6 +6,8 @@ import java.util.UUID;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.entity.Train; import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.NBTHelper;
@ -37,6 +39,7 @@ public class RailwaySavedData extends SavedData {
sd.signalEdgeGroups = new HashMap<>(); sd.signalEdgeGroups = new HashMap<>();
sd.trains = new HashMap<>(); sd.trains = new HashMap<>();
Create.LOGGER.info("Loading Railway Information..."); Create.LOGGER.info("Loading Railway Information...");
NBTHelper.iterateCompoundList(nbt.getList("RailGraphs", Tag.TAG_COMPOUND), c -> { NBTHelper.iterateCompoundList(nbt.getList("RailGraphs", Tag.TAG_COMPOUND), c -> {
TrackGraph graph = TrackGraph.read(c); TrackGraph graph = TrackGraph.read(c);
sd.trackNetworks.put(graph.id, graph); sd.trackNetworks.put(graph.id, graph);
@ -49,6 +52,22 @@ public class RailwaySavedData extends SavedData {
SignalEdgeGroup group = SignalEdgeGroup.read(c); SignalEdgeGroup group = SignalEdgeGroup.read(c);
sd.signalEdgeGroups.put(group.id, group); sd.signalEdgeGroups.put(group.id, group);
}); });
for (TrackGraph graph : sd.trackNetworks.values()) {
for (SignalBoundary signal : graph.getPoints(EdgePointType.SIGNAL)) {
UUID groupId = signal.groups.getFirst();
UUID otherGroupId = signal.groups.getSecond();
if (groupId == null || otherGroupId == null)
continue;
SignalEdgeGroup group = sd.signalEdgeGroups.get(groupId);
SignalEdgeGroup otherGroup = sd.signalEdgeGroups.get(otherGroupId);
if (group == null || otherGroup == null)
continue;
group.putAdjacent(otherGroupId);
otherGroup.putAdjacent(groupId);
}
}
return sd; return sd;
} }

View file

@ -1,22 +1,33 @@
package com.simibubi.create.content.logistics.trains; package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction.Axis;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class TrackEdge { public class TrackEdge {
public TrackNode node1;
public TrackNode node2;
BezierConnection turn; BezierConnection turn;
EdgeData edgeData; EdgeData edgeData;
public TrackEdge(BezierConnection turn) { public TrackEdge(TrackNode node1, TrackNode node2, BezierConnection turn) {
this.edgeData = new EdgeData(this);
this.node1 = node1;
this.node2 = node2;
this.turn = turn; this.turn = turn;
this.edgeData = new EdgeData();
} }
public boolean isTurn() { public boolean isTurn() {
@ -31,29 +42,112 @@ public class TrackEdge {
return turn; return turn;
} }
public Vec3 getDirection(TrackNode node1, TrackNode node2, boolean fromFirst) { public Vec3 getDirection(boolean fromFirst) {
return getPosition(node1, node2, fromFirst ? 0.25f : 1) return getPosition(fromFirst ? 0.25f : 1).subtract(getPosition(fromFirst ? 0 : 0.75f))
.subtract(getPosition(node1, node2, fromFirst ? 0 : 0.75f))
.normalize(); .normalize();
} }
public double getLength(TrackNode node1, TrackNode node2) { public double getLength() {
return isTurn() ? turn.getLength() return isTurn() ? turn.getLength()
: node1.location.getLocation() : node1.location.getLocation()
.distanceTo(node2.location.getLocation()); .distanceTo(node2.location.getLocation());
} }
public double incrementT(TrackNode node1, TrackNode node2, double currentT, double distance) { public double incrementT(double currentT, double distance) {
boolean tooFar = Math.abs(distance) > 5; boolean tooFar = Math.abs(distance) > 5;
distance = distance / getLength(node1, node2); distance = distance / getLength();
return !tooFar && isTurn() ? turn.incrementT(currentT, distance) : currentT + distance; return !tooFar && isTurn() ? turn.incrementT(currentT, distance) : currentT + distance;
} }
public Vec3 getPosition(TrackNode node1, TrackNode node2, double t) { public Vec3 getPosition(double t) {
return isTurn() ? turn.getPosition(Mth.clamp(t, 0, 1)) return isTurn() ? turn.getPosition(Mth.clamp(t, 0, 1))
: VecHelper.lerp((float) t, node1.location.getLocation(), node2.location.getLocation()); : VecHelper.lerp((float) t, node1.location.getLocation(), node2.location.getLocation());
} }
public Collection<double[]> getIntersection(TrackNode node1, TrackNode node2, TrackEdge other, TrackNode other1,
TrackNode other2) {
Vec3 v1 = node1.location.getLocation();
Vec3 v2 = node2.location.getLocation();
Vec3 w1 = other1.location.getLocation();
Vec3 w2 = other2.location.getLocation();
if (v1.y != v2.y || v1.y != w1.y || v1.y != w2.y)
return Collections.emptyList();
if (!isTurn()) {
if (!other.isTurn())
return ImmutableList.of(VecHelper.intersectRanged(v1, w1, v2, w2, Axis.Y));
return other.getIntersection(other1, other2, this, node1, node2)
.stream()
.map(a -> new double[] { a[1], a[0] })
.toList();
}
AABB bb = turn.getBounds();
if (!other.isTurn()) {
if (!bb.intersects(w1, w2))
return Collections.emptyList();
Vec3 seg1 = v1;
Vec3 seg2 = null;
double t = 0;
Collection<double[]> intersections = new ArrayList<>();
for (int i = 0; i < turn.getSegmentCount(); i++) {
double tOffset = t;
t += .5;
seg2 = getPosition(t / getLength());
double[] intersection = VecHelper.intersectRanged(seg1, w1, seg2, w2, Axis.Y);
seg1 = seg2;
if (intersection == null)
continue;
intersection[0] += tOffset;
intersections.add(intersection);
}
return intersections;
}
if (!bb.intersects(other.turn.getBounds()))
return Collections.emptyList();
Vec3 seg1 = v1;
Vec3 seg2 = null;
double t = 0;
Collection<double[]> intersections = new ArrayList<>();
for (int i = 0; i < turn.getSegmentCount(); i++) {
double tOffset = t;
t += .5;
seg2 = getPosition(t / getLength());
Vec3 otherSeg1 = w1;
Vec3 otherSeg2 = null;
double u = 0;
for (int j = 0; j < other.turn.getSegmentCount(); j++) {
double uOffset = u;
u += .5;
otherSeg2 = other.getPosition(u / other.getLength());
double[] intersection = VecHelper.intersectRanged(seg1, otherSeg1, seg2, otherSeg2, Axis.Y);
otherSeg1 = otherSeg2;
if (intersection == null)
continue;
intersection[0] += tOffset;
intersection[1] += uOffset;
intersections.add(intersection);
}
seg1 = seg2;
}
return intersections;
}
public Vec3 getNormal(TrackNode node1, TrackNode node2, double t) { public Vec3 getNormal(TrackNode node1, TrackNode node2, double t) {
return isTurn() ? turn.getNormal(Mth.clamp(t, 0, 1)) : node1.getNormal(); return isTurn() ? turn.getNormal(Mth.clamp(t, 0, 1)) : node1.getNormal();
} }
@ -65,7 +159,7 @@ public class TrackEdge {
} }
public static TrackEdge read(FriendlyByteBuf buffer) { public static TrackEdge read(FriendlyByteBuf buffer) {
return new TrackEdge(buffer.readBoolean() ? new BezierConnection(buffer) : null); return new TrackEdge(null, null, buffer.readBoolean() ? new BezierConnection(buffer) : null);
} }
public CompoundTag write() { public CompoundTag write() {
@ -76,8 +170,8 @@ public class TrackEdge {
public static TrackEdge read(CompoundTag tag, TrackGraph graph) { public static TrackEdge read(CompoundTag tag, TrackGraph graph) {
TrackEdge trackEdge = TrackEdge trackEdge =
new TrackEdge(tag.contains("Positions") ? new BezierConnection(tag, BlockPos.ZERO) : null); new TrackEdge(null, null, tag.contains("Positions") ? new BezierConnection(tag, BlockPos.ZERO) : null);
trackEdge.edgeData = EdgeData.read(tag.getCompound("Signals"), graph); trackEdge.edgeData = EdgeData.read(tag.getCompound("Signals"), trackEdge, graph);
return trackEdge; return trackEdge;
} }

View file

@ -16,34 +16,28 @@ import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.lwjgl.glfw.GLFW;
import com.simibubi.create.AllKeys;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation; import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
import com.simibubi.create.content.logistics.trains.entity.Train; import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointManager; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointManager;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointStorage; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointStorage;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary; import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackEdgeIntersection;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation;
import com.simibubi.create.foundation.utility.Color; import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag; import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.world.entity.Entity; import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@ -58,6 +52,9 @@ public class TrackGraph {
Map<Integer, TrackNode> nodesById; Map<Integer, TrackNode> nodesById;
Map<TrackNode, Map<TrackNode, TrackEdge>> connectionsByNode; Map<TrackNode, Map<TrackNode, TrackEdge>> connectionsByNode;
EdgePointStorage edgePoints; EdgePointStorage edgePoints;
Map<ResourceKey<Level>, TrackGraphBounds> bounds;
List<TrackEdge> deferredIntersectionUpdates;
public TrackGraph() { public TrackGraph() {
this(UUID.randomUUID()); this(UUID.randomUUID());
@ -67,8 +64,10 @@ public class TrackGraph {
setId(graphID); setId(graphID);
nodes = new HashMap<>(); nodes = new HashMap<>();
nodesById = new HashMap<>(); nodesById = new HashMap<>();
bounds = new HashMap<>();
connectionsByNode = new IdentityHashMap<>(); connectionsByNode = new IdentityHashMap<>();
edgePoints = new EdgePointStorage(); edgePoints = new EdgePointStorage();
deferredIntersectionUpdates = new ArrayList<>();
} }
// //
@ -104,6 +103,16 @@ public class TrackGraph {
// //
public TrackGraphBounds getBounds(Level level) {
return bounds.computeIfAbsent(level.dimension(), dim -> new TrackGraphBounds(this, dim));
}
public void invalidateBounds() {
bounds.clear();
}
//
public Set<TrackNodeLocation> getNodes() { public Set<TrackNodeLocation> getNodes() {
return nodes.keySet(); return nodes.keySet();
} }
@ -125,6 +134,7 @@ public class TrackGraph {
return false; return false;
TrackNode newNode = nodes.get(location); TrackNode newNode = nodes.get(location);
Create.RAILWAYS.sync.nodeAdded(this, newNode); Create.RAILWAYS.sync.nodeAdded(this, newNode);
invalidateBounds();
markDirty(); markDirty();
return true; return true;
} }
@ -165,17 +175,30 @@ public class TrackGraph {
} }
nodesById.remove(removed.netId); nodesById.remove(removed.netId);
invalidateBounds();
if (!connectionsByNode.containsKey(removed)) if (!connectionsByNode.containsKey(removed))
return true; return true;
Map<TrackNode, TrackEdge> connections = connectionsByNode.remove(removed); Map<TrackNode, TrackEdge> connections = connectionsByNode.remove(removed);
for (TrackEdge trackEdge : connections.values()) for (Entry<TrackNode, TrackEdge> entry : connections.entrySet()) {
for (TrackEdgePoint point : trackEdge.getEdgeData() TrackEdge trackEdge = entry.getValue();
.getPoints()) { EdgeData edgeData = trackEdge.getEdgeData();
for (TrackEdgePoint point : edgeData.getPoints()) {
if (level != null) if (level != null)
point.invalidate(level); point.invalidate(level);
edgePoints.remove(point.getType(), point.getId()); edgePoints.remove(point.getType(), point.getId());
} }
if (level != null) {
TrackNode otherNode = entry.getKey();
for (TrackEdgeIntersection intersection : edgeData.getIntersections()) {
Couple<TrackNodeLocation> target = intersection.target;
TrackGraph graph = Create.RAILWAYS.getGraph(level, target.getFirst());
if (graph != null)
graph.removeIntersection(intersection, removed, otherNode);
}
}
}
for (TrackNode railNode : connections.keySet()) for (TrackNode railNode : connections.keySet())
if (connectionsByNode.containsKey(railNode)) if (connectionsByNode.containsKey(railNode))
@ -185,6 +208,29 @@ public class TrackGraph {
return true; return true;
} }
private void removeIntersection(TrackEdgeIntersection intersection, TrackNode targetNode1, TrackNode targetNode2) {
TrackNode node1 = locateNode(intersection.target.getFirst());
TrackNode node2 = locateNode(intersection.target.getSecond());
if (node1 == null || node2 == null)
return;
Map<TrackNode, TrackEdge> from1 = getConnectionsFrom(node1);
if (from1 != null) {
TrackEdge edge = from1.get(node2);
if (edge != null)
edge.getEdgeData()
.removeIntersection(this, intersection.id);
}
Map<TrackNode, TrackEdge> from2 = getConnectionsFrom(node2);
if (from2 != null) {
TrackEdge edge = from2.get(node1);
if (edge != null)
edge.getEdgeData()
.removeIntersection(this, intersection.id);
}
}
public static int nextNodeId() { public static int nextNodeId() {
return netIdGenerator.incrementAndGet(); return netIdGenerator.incrementAndGet();
} }
@ -209,6 +255,7 @@ public class TrackGraph {
edgePoints.transferAll(toOther, toOther.edgePoints); edgePoints.transferAll(toOther, toOther.edgePoints);
nodes.clear(); nodes.clear();
connectionsByNode.clear(); connectionsByNode.clear();
toOther.invalidateBounds();
Map<UUID, Train> trains = Create.RAILWAYS.trains; Map<UUID, Train> trains = Create.RAILWAYS.trains;
for (Iterator<UUID> iterator = trains.keySet() for (Iterator<UUID> iterator = trains.keySet()
@ -268,6 +315,7 @@ public class TrackGraph {
public void transfer(TrackNode node, TrackGraph target) { public void transfer(TrackNode node, TrackGraph target) {
target.addNode(node); target.addNode(node);
target.invalidateBounds();
TrackNodeLocation nodeLoc = node.getLocation(); TrackNodeLocation nodeLoc = node.getLocation();
Map<TrackNode, TrackEdge> connections = getConnectionsFrom(node); Map<TrackNode, TrackEdge> connections = getConnectionsFrom(node);
@ -298,6 +346,7 @@ public class TrackGraph {
nodes.remove(nodeLoc); nodes.remove(nodeLoc);
nodesById.remove(node.getNetId()); nodesById.remove(node.getNetId());
connectionsByNode.remove(node); connectionsByNode.remove(node);
invalidateBounds();
} }
public boolean isEmpty() { public boolean isEmpty() {
@ -312,16 +361,66 @@ public class TrackGraph {
return getConnectionsFrom(nodes.getFirst()).get(nodes.getSecond()); return getConnectionsFrom(nodes.getFirst()).get(nodes.getSecond());
} }
public void connectNodes(TrackNodeLocation location, TrackNodeLocation location2, TrackEdge edge) { public void connectNodes(LevelAccessor reader, TrackNodeLocation location, TrackNodeLocation location2,
@Nullable BezierConnection turn) {
TrackNode node1 = nodes.get(location); TrackNode node1 = nodes.get(location);
TrackNode node2 = nodes.get(location2); TrackNode node2 = nodes.get(location2);
TrackEdge edge2 = new TrackEdge(edge.turn != null ? edge.turn.secondary() : null);
boolean bezier = turn != null;
TrackEdge edge = new TrackEdge(node1, node2, turn);
TrackEdge edge2 = new TrackEdge(node2, node1, bezier ? turn.secondary() : null);
if (reader instanceof Level level) {
for (TrackGraph graph : Create.RAILWAYS.trackNetworks.values()) {
if (graph != this
&& !graph.getBounds(level).box.intersects(location.getLocation(), location2.getLocation()))
continue;
for (TrackNode otherNode1 : graph.nodes.values()) {
Map<TrackNode, TrackEdge> connections = graph.connectionsByNode.get(otherNode1);
if (connections == null)
continue;
for (Entry<TrackNode, TrackEdge> entry : connections.entrySet()) {
TrackNode otherNode2 = entry.getKey();
TrackEdge otherEdge = entry.getValue();
if (graph == this)
if (otherNode1 == node1 || otherNode2 == node1 || otherNode1 == node2
|| otherNode2 == node2)
continue;
if (edge == otherEdge)
continue;
if (!bezier && !otherEdge.isTurn())
continue;
if (otherEdge.isTurn() && otherEdge.turn.isPrimary())
continue;
Collection<double[]> intersections =
edge.getIntersection(node1, node2, otherEdge, otherNode1, otherNode2);
UUID id = UUID.randomUUID();
for (double[] intersection : intersections) {
double s = intersection[0];
double t = intersection[1];
edge.edgeData.addIntersection(this, id, s, otherNode1, otherNode2, t);
edge2.edgeData.addIntersection(this, id, edge.getLength() - s, otherNode1, otherNode2, t);
otherEdge.edgeData.addIntersection(graph, id, t, node1, node2, s);
TrackEdge otherEdge2 = graph.getConnection(Couple.create(otherNode2, otherNode1));
if (otherEdge2 != null)
otherEdge2.edgeData.addIntersection(graph, id, otherEdge.getLength() - t, node1, node2,
s);
}
}
}
}
}
putConnection(node1, node2, edge); putConnection(node1, node2, edge);
putConnection(node2, node1, edge2); putConnection(node2, node1, edge2);
Create.RAILWAYS.sync.edgeAdded(this, node1, node2, edge); Create.RAILWAYS.sync.edgeAdded(this, node1, node2, edge);
Create.RAILWAYS.sync.edgeAdded(this, node2, node1, edge2); Create.RAILWAYS.sync.edgeAdded(this, node2, node1, edge2);
markDirty(); markDirty();
} }
@ -343,6 +442,47 @@ public class TrackGraph {
return connections.put(node2, edge) == null; return connections.put(node2, edge) == null;
} }
public void deferIntersectionUpdate(TrackEdge edge) {
deferredIntersectionUpdates.add(edge);
}
public void resolveIntersectingEdgeGroups(Level level) {
for (TrackEdge edge : deferredIntersectionUpdates) {
if (!connectionsByNode.containsKey(edge.node1) || edge != connectionsByNode.get(edge.node1)
.get(edge.node2))
continue;
EdgeData edgeData = edge.getEdgeData();
for (TrackEdgeIntersection intersection : edgeData.getIntersections()) {
UUID groupId = edgeData.getGroupAtPosition(this, intersection.location);
Couple<TrackNodeLocation> target = intersection.target;
TrackGraph graph = Create.RAILWAYS.getGraph(level, target.getFirst());
if (graph == null)
continue;
TrackNode node1 = graph.locateNode(target.getFirst());
TrackNode node2 = graph.locateNode(target.getSecond());
Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node1);
if (connectionsFrom == null)
continue;
TrackEdge otherEdge = connectionsFrom.get(node2);
if (otherEdge == null)
continue;
UUID otherGroupId = otherEdge.getEdgeData()
.getGroupAtPosition(graph, intersection.targetLocation);
SignalEdgeGroup group = Create.RAILWAYS.signalEdgeGroups.get(groupId);
SignalEdgeGroup otherGroup = Create.RAILWAYS.signalEdgeGroups.get(otherGroupId);
if (group == null || otherGroup == null)
continue;
intersection.groupId = groupId;
group.putIntersection(intersection.id, otherGroupId);
otherGroup.putIntersection(intersection.id, groupId);
}
}
deferredIntersectionUpdates.clear();
}
public void markDirty() { public void markDirty() {
Create.RAILWAYS.markTracksDirty(); Create.RAILWAYS.markTracksDirty();
} }
@ -418,6 +558,8 @@ public class TrackGraph {
NBTHelper.iterateCompoundList(nodeTag.getList("Connections", Tag.TAG_COMPOUND), c -> { NBTHelper.iterateCompoundList(nodeTag.getList("Connections", Tag.TAG_COMPOUND), c -> {
TrackNode node2 = indexTracker.get(c.getInt("To")); TrackNode node2 = indexTracker.get(c.getInt("To"));
TrackEdge edge = TrackEdge.read(c.getCompound("EdgeData"), graph); TrackEdge edge = TrackEdge.read(c.getCompound("EdgeData"), graph);
edge.node1 = node1;
edge.node2 = node2;
graph.putConnection(node1, node2, edge); graph.putConnection(node1, node2, edge);
}); });
} }
@ -425,300 +567,4 @@ public class TrackGraph {
return graph; return graph;
} }
public void debugViewReserved() {
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null)
return;
Set<UUID> reserved = new HashSet<>();
Set<UUID> occupied = new HashSet<>();
for (Train train : Create.RAILWAYS.trains.values()) {
reserved.addAll(train.reservedSignalBlocks);
occupied.addAll(train.occupiedSignalBlocks.keySet());
}
reserved.removeAll(occupied);
Vec3 camera = cameraEntity.getEyePosition();
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 100)
continue;
Map<TrackNode, TrackEdge> map = connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
if (other.hashCode() > hashCode && !AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL))
continue;
Vec3 yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
TrackEdge edge = entry.getValue();
EdgeData signalData = edge.getEdgeData();
UUID singleGroup = signalData.singleSignalGroup;
SignalEdgeGroup signalEdgeGroup =
singleGroup == null ? null : Create.RAILWAYS.sided(null).signalEdgeGroups.get(singleGroup);
if (!edge.isTurn()) {
Vec3 p1 = edge.getPosition(node, other, 0);
Vec3 p2 = edge.getPosition(node, other, 1);
if (signalData.hasPoints()) {
double prev = 0;
double length = edge.getLength(node, other);
SignalBoundary prevBoundary = null;
SignalEdgeGroup group = null;
for (TrackEdgePoint trackEdgePoint : signalData.getPoints()) {
if (!(trackEdgePoint instanceof SignalBoundary boundary))
continue;
prevBoundary = boundary;
UUID groupId = boundary.getGroup(node);
group = Create.RAILWAYS.sided(null).signalEdgeGroups.get(groupId);
double start = prev + (prev == 0 ? 0 : 1 / 16f / length);
prev = (boundary.getLocationOn(node, other, edge) / length) - 1 / 16f / length;
if (group != null
&& (group.reserved != null || occupied.contains(groupId) || reserved.contains(groupId)))
CreateClient.OUTLINER
.showLine(Pair.of(boundary, edge), edge.getPosition(node, other, start)
.add(yOffset),
edge.getPosition(node, other, prev)
.add(yOffset))
.colored(occupied.contains(groupId) ? 0xF68989
: group.reserved != null ? 0xC5D8A4 : 0xF6E7D8)
.lineWidth(1 / 16f);
}
if (prevBoundary != null) {
UUID groupId = prevBoundary.getGroup(other);
SignalEdgeGroup lastGroup = Create.RAILWAYS.sided(null).signalEdgeGroups.get(groupId);
if (lastGroup != null && ((lastGroup.reserved != null || occupied.contains(groupId)
|| reserved.contains(groupId))))
CreateClient.OUTLINER
.showLine(edge, edge.getPosition(node, other, prev + 1 / 16f / length)
.add(yOffset), p2.add(yOffset))
.colored(occupied.contains(groupId) ? 0xF68989
: lastGroup.reserved != null ? 0xC5D8A4 : 0xF6E7D8)
.lineWidth(1 / 16f);
continue;
}
}
if (signalEdgeGroup == null || !(signalEdgeGroup.reserved != null || occupied.contains(singleGroup)
|| reserved.contains(singleGroup)))
continue;
CreateClient.OUTLINER.showLine(edge, p1.add(yOffset), p2.add(yOffset))
.colored(occupied.contains(singleGroup) ? 0xF68989
: signalEdgeGroup.reserved != null ? 0xC5D8A4 : 0xF6E7D8)
.lineWidth(1 / 16f);
continue;
}
if (signalEdgeGroup == null || !(signalEdgeGroup.reserved != null || occupied.contains(singleGroup)
|| reserved.contains(singleGroup)))
continue;
int color =
occupied.contains(singleGroup) ? 0xF68989 : signalEdgeGroup.reserved != null ? 0xC5D8A4 : 0xF6E7D8;
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(node, other, i * 1f / turn.getSegmentCount());
if (previous != null)
CreateClient.OUTLINER
.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(color)
.lineWidth(1 / 16f);
previous = current;
}
}
}
}
public void debugViewSignalData() {
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null)
return;
Vec3 camera = cameraEntity.getEyePosition();
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 50)
continue;
Map<TrackNode, TrackEdge> map = connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
if (other.hashCode() > hashCode && !AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL))
continue;
Vec3 yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
TrackEdge edge = entry.getValue();
EdgeData signalData = edge.getEdgeData();
UUID singleGroup = signalData.singleSignalGroup;
SignalEdgeGroup signalEdgeGroup =
singleGroup == null ? null : Create.RAILWAYS.sided(null).signalEdgeGroups.get(singleGroup);
if (!edge.isTurn()) {
Vec3 p1 = edge.getPosition(node, other, 0);
Vec3 p2 = edge.getPosition(node, other, 1);
if (signalData.hasPoints()) {
double prev = 0;
double length = edge.getLength(node, other);
SignalBoundary prevBoundary = null;
SignalEdgeGroup group = null;
for (TrackEdgePoint trackEdgePoint : signalData.getPoints()) {
if (trackEdgePoint instanceof GlobalStation) {
Vec3 v1 = edge
.getPosition(node, other,
(trackEdgePoint.getLocationOn(node, other, edge) / length))
.add(yOffset);
Vec3 v2 = v1.add(node.normal.scale(3 / 16f));
CreateClient.OUTLINER.showLine(trackEdgePoint.id, v1, v2)
.colored(Color.mixColors(Color.WHITE, color, 1))
.lineWidth(1 / 8f);
continue;
}
if (!(trackEdgePoint instanceof SignalBoundary boundary))
continue;
prevBoundary = boundary;
group = Create.RAILWAYS.sided(null).signalEdgeGroups.get(boundary.getGroup(node));
if (group != null)
CreateClient.OUTLINER
.showLine(Pair.of(boundary, edge),
edge.getPosition(node, other, prev + (prev == 0 ? 0 : 1 / 16f / length))
.add(yOffset),
edge.getPosition(node, other,
(prev = boundary.getLocationOn(node, other, edge) / length)
- 1 / 16f / length)
.add(yOffset))
.colored(group.color.getRGB())
.lineWidth(1 / 16f);
}
if (prevBoundary != null) {
group = Create.RAILWAYS.sided(null).signalEdgeGroups.get(prevBoundary.getGroup(other));
if (group != null)
CreateClient.OUTLINER
.showLine(edge, edge.getPosition(node, other, prev + 1 / 16f / length)
.add(yOffset), p2.add(yOffset))
.colored(group.color.getRGB())
.lineWidth(1 / 16f);
continue;
}
}
if (signalEdgeGroup == null)
continue;
CreateClient.OUTLINER.showLine(edge, p1.add(yOffset), p2.add(yOffset))
.colored(signalEdgeGroup.color.getRGB())
.lineWidth(1 / 16f);
continue;
}
if (signalEdgeGroup == null)
continue;
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(node, other, i * 1f / turn.getSegmentCount());
if (previous != null)
CreateClient.OUTLINER
.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(signalEdgeGroup.color.getRGB())
.lineWidth(1 / 16f);
previous = current;
}
}
}
}
public void debugViewNodes() {
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null)
return;
Vec3 camera = cameraEntity.getEyePosition();
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 50)
continue;
Vec3 yOffset = new Vec3(0, 3 / 16f, 0);
Vec3 v1 = location.add(yOffset);
Vec3 v2 = v1.add(node.normal.scale(3 / 16f));
CreateClient.OUTLINER.showLine(Integer.valueOf(node.netId), v1, v2)
.colored(Color.mixColors(Color.WHITE, color, 1))
.lineWidth(1 / 8f);
Map<TrackNode, TrackEdge> map = connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
if (other.hashCode() > hashCode && !AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL))
continue;
yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
TrackEdge edge = entry.getValue();
if (!edge.isTurn()) {
CreateClient.OUTLINER.showLine(edge, edge.getPosition(node, other, 0)
.add(yOffset),
edge.getPosition(node, other, 1)
.add(yOffset))
.colored(color)
.lineWidth(1 / 16f);
continue;
}
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(node, other, i * 1f / turn.getSegmentCount());
if (previous != null)
CreateClient.OUTLINER
.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(color)
.lineWidth(1 / 16f);
previous = current;
}
}
}
}
} }

View file

@ -0,0 +1,40 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public class TrackGraphBounds {
public AABB box;
public List<BezierConnection> beziers;
// TODO: filter nodes by dimensional coordinate
public TrackGraphBounds(TrackGraph graph, ResourceKey<Level> dimension) {
beziers = new ArrayList<>();
box = null;
for (TrackNode node : graph.nodes.values()) {
include(node);
Map<TrackNode, TrackEdge> connections = graph.getConnectionsFrom(node);
for (TrackEdge edge : connections.values())
if (edge.turn != null && edge.turn.isPrimary())
beziers.add(edge.turn);
}
if (box != null)
box = box.inflate(2);
}
private void include(TrackNode node) {
Vec3 v = node.location.getLocation();
AABB aabb = new AABB(v, v);
box = box == null ? aabb : box.minmax(aabb);
}
}

View file

@ -24,7 +24,7 @@ public class TrackGraphHelper {
public static GraphLocation getGraphLocationAt(Level level, BlockPos pos, AxisDirection targetDirection, public static GraphLocation getGraphLocationAt(Level level, BlockPos pos, AxisDirection targetDirection,
Vec3 targetAxis) { Vec3 targetAxis) {
BlockState trackBlockState = level.getBlockState(pos); BlockState trackBlockState = level.getBlockState(pos);
if (!(trackBlockState.getBlock() instanceof ITrackBlock track)) if (!(trackBlockState.getBlock()instanceof ITrackBlock track))
return null; return null;
Vec3 axis = targetAxis.scale(targetDirection.getStep()); Vec3 axis = targetAxis.scale(targetDirection.getStep());
@ -44,7 +44,7 @@ public class TrackGraphHelper {
for (Entry<TrackNode, TrackEdge> entry : connectionsFrom.entrySet()) { for (Entry<TrackNode, TrackEdge> entry : connectionsFrom.entrySet()) {
TrackNode backNode = entry.getKey(); TrackNode backNode = entry.getKey();
Vec3 direction = entry.getValue() Vec3 direction = entry.getValue()
.getDirection(node, backNode, true); .getDirection(true);
if (direction.scale(length) if (direction.scale(length)
.distanceToSqr(axis.scale(-1)) > 1 / 4096f) .distanceToSqr(axis.scale(-1)) > 1 / 4096f)
continue; continue;
@ -133,9 +133,9 @@ public class TrackGraphHelper {
BezierTrackPointLocation targetBezier) { BezierTrackPointLocation targetBezier) {
BlockState state = level.getBlockState(pos); BlockState state = level.getBlockState(pos);
if (!(state.getBlock() instanceof ITrackBlock track)) if (!(state.getBlock()instanceof ITrackBlock track))
return null; return null;
if (!(level.getBlockEntity(pos) instanceof TrackTileEntity trackTE)) if (!(level.getBlockEntity(pos)instanceof TrackTileEntity trackTE))
return null; return null;
BezierConnection bc = trackTE.getConnections() BezierConnection bc = trackTE.getConnections()
.get(targetBezier.curveTarget()); .get(targetBezier.curveTarget());
@ -163,7 +163,7 @@ public class TrackGraphHelper {
graphLocation.position = (targetBezier.segment() + 1) / 2f; graphLocation.position = (targetBezier.segment() + 1) / 2f;
if (targetDirection == AxisDirection.POSITIVE) { if (targetDirection == AxisDirection.POSITIVE) {
graphLocation.edge = graphLocation.edge.swap(); graphLocation.edge = graphLocation.edge.swap();
graphLocation.position = edge.getLength(node, targetNode) - graphLocation.position; graphLocation.position = edge.getLength() - graphLocation.position;
} }
return graphLocation; return graphLocation;

View file

@ -1,7 +1,7 @@
package com.simibubi.create.content.logistics.trains; package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -11,6 +11,7 @@ import javax.annotation.Nullable;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.EdgeGroupColor;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroupPacket; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroupPacket;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.AllPackets;
@ -87,16 +88,18 @@ public class TrackGraphSync {
// //
public void sendEdgeGroups(Collection<UUID> ids, ServerPlayer player) { public void sendEdgeGroups(List<UUID> ids, List<EdgeGroupColor> colors, ServerPlayer player) {
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SignalEdgeGroupPacket(ids, true)); AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player),
new SignalEdgeGroupPacket(ids, colors, true));
} }
public void edgeGroupCreated(UUID id) { public void edgeGroupCreated(UUID id, EdgeGroupColor color) {
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new SignalEdgeGroupPacket(ImmutableList.of(id), true)); AllPackets.channel.send(PacketDistributor.ALL.noArg(), new SignalEdgeGroupPacket(id, color));
} }
public void edgeGroupRemoved(UUID id) { public void edgeGroupRemoved(UUID id) {
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new SignalEdgeGroupPacket(ImmutableList.of(id), false)); AllPackets.channel.send(PacketDistributor.ALL.noArg(),
new SignalEdgeGroupPacket(ImmutableList.of(id), Collections.emptyList(), false));
} }
// //

View file

@ -169,8 +169,14 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
for (Pair<Couple<Integer>, TrackEdge> pair : addedEdges) { for (Pair<Couple<Integer>, TrackEdge> pair : addedEdges) {
Couple<TrackNode> nodes = pair.getFirst() Couple<TrackNode> nodes = pair.getFirst()
.map(graph::getNode); .map(graph::getNode);
if (nodes.getFirst() != null && nodes.getSecond() != null) TrackNode node1 = nodes.getFirst();
graph.putConnection(nodes.getFirst(), nodes.getSecond(), pair.getSecond()); TrackNode node2 = nodes.getSecond();
if (node1 != null && node2 != null) {
TrackEdge edge = pair.getSecond();
edge.node1 = node1;
edge.node2 = node2;
graph.putConnection(node1, node2, edge);
}
} }
for (TrackEdgePoint edgePoint : addedEdgePoints) for (TrackEdgePoint edgePoint : addedEdgePoints)
@ -203,13 +209,13 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
if (edge == null) if (edge == null)
continue; continue;
EdgeData edgeData = new EdgeData(); EdgeData edgeData = new EdgeData(edge);
if (groupType == NULL_GROUP) if (groupType == NULL_GROUP)
edgeData.singleSignalGroup = null; edgeData.setSingleSignalGroup(null, null);
else if (groupType == PASSIVE_GROUP) else if (groupType == PASSIVE_GROUP)
edgeData.singleSignalGroup = EdgeData.passiveGroup; edgeData.setSingleSignalGroup(null, EdgeData.passiveGroup);
else else
edgeData.singleSignalGroup = idList.get(0); edgeData.setSingleSignalGroup(null, idList.get(0));
List<TrackEdgePoint> points = edgeData.getPoints(); List<TrackEdgePoint> points = edgeData.getPoints();
edge.edgeData = edgeData; edge.edgeData = edgeData;
@ -232,9 +238,9 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
List<UUID> list = new ArrayList<>(); List<UUID> list = new ArrayList<>();
EdgeData edgeData = edge.getEdgeData(); EdgeData edgeData = edge.getEdgeData();
int groupType = edgeData.hasSignalBoundaries() ? NULL_GROUP int groupType = edgeData.hasSignalBoundaries() ? NULL_GROUP
: EdgeData.passiveGroup.equals(edgeData.singleSignalGroup) ? PASSIVE_GROUP : GROUP; : EdgeData.passiveGroup.equals(edgeData.getSingleSignalGroup()) ? PASSIVE_GROUP : GROUP;
if (groupType == GROUP) if (groupType == GROUP)
list.add(edgeData.singleSignalGroup); list.add(edgeData.getSingleSignalGroup());
for (TrackEdgePoint point : edgeData.getPoints()) for (TrackEdgePoint point : edgeData.getPoints())
list.add(point.getId()); list.add(point.getId());
updatedEdgeData.put(key, Pair.of(groupType, list)); updatedEdgeData.put(key, Pair.of(groupType, list));

View file

@ -0,0 +1,283 @@
package com.simibubi.create.content.logistics.trains;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import org.lwjgl.glfw.GLFW;
import com.simibubi.create.AllKeys;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackEdgeIntersection;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.outliner.Outliner;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public class TrackGraphVisualizer {
public static void visualiseSignalEdgeGroups(TrackGraph graph) {
Minecraft mc = Minecraft.getInstance();
Entity cameraEntity = mc.cameraEntity;
if (cameraEntity == null)
return;
AABB box = graph.getBounds(mc.level).box;
if (box == null || !box.intersects(cameraEntity.getBoundingBox()
.inflate(50)))
return;
Vec3 camera = cameraEntity.getEyePosition();
Outliner outliner = CreateClient.OUTLINER;
boolean ctrl = AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL);
Map<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.sided(null).signalEdgeGroups;
float width = 1 / 8f;
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : graph.nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 50)
continue;
Map<TrackNode, TrackEdge> map = graph.connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
TrackEdge edge = entry.getValue();
EdgeData signalData = edge.getEdgeData();
// temporary
if (other.hashCode() > hashCode == ctrl)
for (TrackEdgeIntersection intersection : signalData.getIntersections()) {
Vec3 v1 = edge.getPosition(intersection.location / edge.getLength());
Vec3 v2 = v1.add(node.normal.scale(8 / 16f));
outliner.showLine(intersection, v1, v2)
.colored(Color.mixColors(Color.WHITE, graph.color, 1))
.lineWidth(width);
} //
if (other.hashCode() > hashCode && !ctrl)
continue;
Vec3 yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 5) / 64f, 0);
Vec3 startPoint = edge.getPosition(0);
Vec3 endPoint = edge.getPosition(1);
if (!edge.isTurn()) {
// Straight edge with signal boundaries
if (signalData.hasSignalBoundaries()) {
double prev = 0;
double length = edge.getLength();
SignalBoundary prevBoundary = null;
SignalEdgeGroup group = null;
for (TrackEdgePoint trackEdgePoint : signalData.getPoints()) {
if (!(trackEdgePoint instanceof SignalBoundary boundary))
continue;
prevBoundary = boundary;
group = allGroups.get(boundary.getGroup(node));
if (group != null)
outliner.showLine(Pair.of(boundary, edge),
edge.getPosition(prev + (prev == 0 ? 0 : 1 / 16f / length))
.add(yOffset),
edge.getPosition((prev = boundary.getLocationOn(edge) / length) - 1 / 16f / length)
.add(yOffset))
.colored(group.color.get())
.lineWidth(width);
}
if (prevBoundary != null) {
group = allGroups.get(prevBoundary.getGroup(other));
if (group != null)
outliner.showLine(edge, edge.getPosition(prev + 1 / 16f / length)
.add(yOffset), endPoint.add(yOffset))
.colored(group.color.get())
.lineWidth(width);
continue;
}
}
// Straight edge, no signal boundaries
UUID singleGroup = signalData.getEffectiveEdgeGroupId(graph);
SignalEdgeGroup singleEdgeGroup = singleGroup == null ? null : allGroups.get(singleGroup);
if (singleEdgeGroup == null)
continue;
outliner.showLine(edge, startPoint.add(yOffset), endPoint.add(yOffset))
.colored(singleEdgeGroup.color.get())
.lineWidth(width);
} else {
// Bezier edge with signal boundaries
if (signalData.hasSignalBoundaries()) {
Iterator<TrackEdgePoint> points = signalData.getPoints()
.iterator();
SignalBoundary currentBoundary = null;
double currentBoundaryPosition = 0;
while (points.hasNext()) {
TrackEdgePoint next = points.next();
if (!(next instanceof SignalBoundary signal))
continue;
currentBoundary = signal;
currentBoundaryPosition = signal.getLocationOn(edge);
break;
}
if (currentBoundary == null)
continue;
UUID initialGroupId = currentBoundary.getGroup(node);
if (initialGroupId == null)
continue;
SignalEdgeGroup initialGroup = allGroups.get(initialGroupId);
if (initialGroup == null)
continue;
Color currentColour = initialGroup.color.get();
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
double f = i * 1f / turn.getSegmentCount();
double position = f * turn.getLength();
Vec3 current = edge.getPosition(f);
if (previous != null) {
if (currentBoundary != null && position > currentBoundaryPosition) {
current = edge.getPosition((currentBoundaryPosition - width) / turn.getLength());
outliner
.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(currentColour)
.lineWidth(width);
current = edge.getPosition((currentBoundaryPosition + width) / turn.getLength());
previous = current;
UUID newId = currentBoundary.getGroup(other);
if (newId != null && allGroups.containsKey(newId))
currentColour = allGroups.get(newId).color.get();
currentBoundary = null;
while (points.hasNext()) {
TrackEdgePoint next = points.next();
if (!(next instanceof SignalBoundary signal))
continue;
currentBoundary = signal;
currentBoundaryPosition = signal.getLocationOn(edge);
break;
}
}
outliner.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(currentColour)
.lineWidth(width);
}
previous = current;
}
}
// Bezier edge, no signal boundaries
UUID singleGroup = signalData.getEffectiveEdgeGroupId(graph);
SignalEdgeGroup singleEdgeGroup = singleGroup == null ? null : allGroups.get(singleGroup);
if (singleEdgeGroup == null)
continue;
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(i * 1f / turn.getSegmentCount());
if (previous != null)
outliner.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(singleEdgeGroup.color.get())
.lineWidth(width);
previous = current;
}
}
}
}
}
public static void debugViewGraph(TrackGraph graph) {
Minecraft mc = Minecraft.getInstance();
Entity cameraEntity = mc.cameraEntity;
if (cameraEntity == null)
return;
AABB box = graph.getBounds(mc.level).box;
if (box == null || !box.intersects(cameraEntity.getBoundingBox()
.inflate(50)))
return;
Vec3 camera = cameraEntity.getEyePosition();
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : graph.nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 50)
continue;
Vec3 yOffset = new Vec3(0, 3 / 16f, 0);
Vec3 v1 = location.add(yOffset);
Vec3 v2 = v1.add(node.normal.scale(3 / 16f));
CreateClient.OUTLINER.showLine(Integer.valueOf(node.netId), v1, v2)
.colored(Color.mixColors(Color.WHITE, graph.color, 1))
.lineWidth(1 / 8f);
Map<TrackNode, TrackEdge> map = graph.connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
if (other.hashCode() > hashCode && !AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL))
continue;
yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
TrackEdge edge = entry.getValue();
if (!edge.isTurn()) {
CreateClient.OUTLINER.showLine(edge, edge.getPosition(0)
.add(yOffset),
edge.getPosition(1)
.add(yOffset))
.colored(graph.color)
.lineWidth(1 / 16f);
continue;
}
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(i * 1f / turn.getSegmentCount());
if (previous != null)
CreateClient.OUTLINER
.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(graph.color)
.lineWidth(1 / 16f);
previous = current;
}
}
}
}
}

View file

@ -32,7 +32,7 @@ public class TrackPropagator {
} }
public static void onRailRemoved(LevelAccessor reader, BlockPos pos, BlockState state) { public static void onRailRemoved(LevelAccessor reader, BlockPos pos, BlockState state) {
if (!(state.getBlock()instanceof ITrackBlock track)) if (!(state.getBlock() instanceof ITrackBlock track))
return; return;
Collection<DiscoveredLocation> ends = track.getConnected(reader, pos, state, false, null); Collection<DiscoveredLocation> ends = track.getConnected(reader, pos, state, false, null);
@ -51,7 +51,7 @@ public class TrackPropagator {
sync.nodeRemoved(foundGraph, removedNode); sync.nodeRemoved(foundGraph, removedNode);
if (!foundGraph.isEmpty()) if (!foundGraph.isEmpty())
continue; continue;
manager.removeGraph(foundGraph); manager.removeGraphAndGroup(foundGraph);
sync.graphRemoved(foundGraph); sync.graphRemoved(foundGraph);
} }
} }
@ -79,7 +79,7 @@ public class TrackPropagator {
} }
public static TrackGraph onRailAdded(LevelAccessor reader, BlockPos pos, BlockState state) { public static TrackGraph onRailAdded(LevelAccessor reader, BlockPos pos, BlockState state) {
if (!(state.getBlock()instanceof ITrackBlock track)) if (!(state.getBlock() instanceof ITrackBlock track))
return null; return null;
// 1. Remove all immediately reachable node locations // 1. Remove all immediately reachable node locations
@ -124,7 +124,7 @@ public class TrackPropagator {
TrackGraph railGraph = iterator.next(); TrackGraph railGraph = iterator.next();
if (!railGraph.isEmpty() || connectedGraphs.size() == 1) if (!railGraph.isEmpty() || connectedGraphs.size() == 1)
continue; continue;
manager.removeGraph(railGraph); manager.removeGraphAndGroup(railGraph);
sync.graphRemoved(railGraph); sync.graphRemoved(railGraph);
iterator.remove(); iterator.remove();
} }
@ -136,7 +136,7 @@ public class TrackPropagator {
graph = other; graph = other;
else { else {
other.transferAll(graph); other.transferAll(graph);
manager.removeGraph(other); manager.removeGraphAndGroup(other);
sync.graphRemoved(other); sync.graphRemoved(other);
} }
} else if (connectedGraphs.size() == 1) { } else if (connectedGraphs.size() == 1) {
@ -144,7 +144,7 @@ public class TrackPropagator {
.findFirst() .findFirst()
.get(); .get();
} else } else
manager.putGraph(graph = new TrackGraph()); manager.putGraphWithDefaultGroup(graph = new TrackGraph());
DiscoveredLocation startNode = null; DiscoveredLocation startNode = null;
@ -191,7 +191,7 @@ public class TrackPropagator {
if (isValidGraphNodeLocation(entry.currentNode, ends, first) && entry.currentNode != startNode) { if (isValidGraphNodeLocation(entry.currentNode, ends, first) && entry.currentNode != startNode) {
boolean nodeIsNew = graph.createNodeIfAbsent(entry.currentNode); boolean nodeIsNew = graph.createNodeIfAbsent(entry.currentNode);
graph.connectNodes(parentNode, entry.currentNode, new TrackEdge(entry.currentNode.getTurn())); graph.connectNodes(reader, parentNode, entry.currentNode, entry.currentNode.getTurn());
addedNodes.add(graph.locateNode(entry.currentNode)); addedNodes.add(graph.locateNode(entry.currentNode));
parentNode = entry.currentNode; parentNode = entry.currentNode;
if (!nodeIsNew) if (!nodeIsNew)

View file

@ -239,8 +239,8 @@ public class CarriageSyncData {
TravellingPoint toApproach = pointsToApproach[index]; TravellingPoint toApproach = pointsToApproach[index];
point.travel(graph, partial * f, point.travel(graph, partial * f,
point.follow(toApproach, b -> success.setValue(success.booleanValue() && b)), point.ignoreEdgePoints(), point.follow(toApproach, b -> success.setValue(success.booleanValue() && b)),
point.ignoreTurns()); point.ignoreEdgePoints(), point.ignoreTurns());
// could not pathfind to server location // could not pathfind to server location
if (!success.booleanValue()) { if (!success.booleanValue()) {
@ -284,8 +284,7 @@ public class CarriageSyncData {
TrackEdge targetEdge = graph.getConnectionsFrom(targetNode1) TrackEdge targetEdge = graph.getConnectionsFrom(targetNode1)
.get(targetNode2); .get(targetNode2);
double distanceToNode2 = double distanceToNode2 = forward ? initialEdge.getLength() - current.position : current.position;
forward ? initialEdge.getLength(initialNode1, initialNode2) - current.position : current.position;
frontier.add(Pair.of(distanceToNode2, Pair.of(Couple.create(initialNode1, initialNode2), initialEdge))); frontier.add(Pair.of(distanceToNode2, Pair.of(Couple.create(initialNode1, initialNode2), initialEdge)));
@ -294,15 +293,12 @@ public class CarriageSyncData {
double distance = poll.getFirst(); double distance = poll.getFirst();
Pair<Couple<TrackNode>, TrackEdge> currentEntry = poll.getSecond(); Pair<Couple<TrackNode>, TrackEdge> currentEntry = poll.getSecond();
TrackNode node1 = currentEntry.getFirst()
.getFirst();
TrackNode node2 = currentEntry.getFirst() TrackNode node2 = currentEntry.getFirst()
.getSecond(); .getSecond();
TrackEdge edge = currentEntry.getSecond(); TrackEdge edge = currentEntry.getSecond();
if (edge == targetEdge) if (edge == targetEdge)
return (float) (distance return (float) (distance - (forward ? edge.getLength() - target.position : target.position));
- (forward ? edge.getLength(node1, node2) - target.position : target.position));
if (distance > maxDistance) if (distance > maxDistance)
continue; continue;
@ -310,10 +306,9 @@ public class CarriageSyncData {
List<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>(); List<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>();
Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node2); Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node2);
for (Entry<TrackNode, TrackEdge> entry : connectionsFrom.entrySet()) { for (Entry<TrackNode, TrackEdge> entry : connectionsFrom.entrySet()) {
TrackNode newNode = entry.getKey();
TrackEdge newEdge = entry.getValue(); TrackEdge newEdge = entry.getValue();
Vec3 currentDirection = edge.getDirection(node1, node2, false); Vec3 currentDirection = edge.getDirection(false);
Vec3 newDirection = newEdge.getDirection(node2, newNode, true); Vec3 newDirection = newEdge.getDirection(true);
if (currentDirection.dot(newDirection) < 3 / 4f) if (currentDirection.dot(newDirection) < 3 / 4f)
continue; continue;
if (!visited.add(entry.getValue())) if (!visited.add(entry.getValue()))
@ -328,8 +323,7 @@ public class CarriageSyncData {
TrackNode newNode = entry.getKey(); TrackNode newNode = entry.getKey();
TrackEdge newEdge = entry.getValue(); TrackEdge newEdge = entry.getValue();
reachedVia.put(newEdge, Pair.of(validTargets.size() > 1, edge)); reachedVia.put(newEdge, Pair.of(validTargets.size() > 1, edge));
frontier.add(Pair.of(newEdge.getLength(node2, newNode) + distance, frontier.add(Pair.of(newEdge.getLength() + distance, Pair.of(Couple.create(node2, newNode), newEdge)));
Pair.of(Couple.create(node2, newNode), newEdge)));
} }
} }

View file

@ -449,7 +449,7 @@ public class Navigation {
backTrack = reachedVia.get(edgeReached); backTrack = reachedVia.get(edgeReached);
} }
double position = edge.getLength(node1, node2) - destination.getLocationOn(node1, node2, edge); double position = edge.getLength() - destination.getLocationOn(edge);
double distanceToDestination = distance - position; double distanceToDestination = distance - position;
results.set(forward, new DiscoveredPath((forward ? 1 : -1) * distanceToDestination, cost, currentPath)); results.set(forward, new DiscoveredPath((forward ? 1 : -1) * distanceToDestination, cost, currentPath));
return true; return true;
@ -510,12 +510,7 @@ public class Navigation {
return false; return false;
TrackEdge edge = currentEntry.getSecond(); TrackEdge edge = currentEntry.getSecond();
TrackNode node1 = currentEntry.getFirst() double position = edge.getLength() - globalStation.getLocationOn(edge);
.getFirst();
TrackNode node2 = currentEntry.getFirst()
.getSecond();
double position = edge.getLength(node1, node2) - globalStation.getLocationOn(node1, node2, edge);
if (distance - position < minDistance) if (distance - position < minDistance)
return false; return false;
result.setValue(globalStation); result.setValue(globalStation);
@ -571,8 +566,7 @@ public class Navigation {
TrackNode initialNode2 = forward ? startingPoint.node2 : startingPoint.node1; TrackNode initialNode2 = forward ? startingPoint.node2 : startingPoint.node1;
TrackEdge initialEdge = graph.getConnectionsFrom(initialNode1) TrackEdge initialEdge = graph.getConnectionsFrom(initialNode1)
.get(initialNode2); .get(initialNode2);
double distanceToNode2 = forward ? initialEdge.getLength(initialNode1, initialNode2) - startingPoint.position double distanceToNode2 = forward ? initialEdge.getLength() - startingPoint.position : startingPoint.position;
: startingPoint.position;
frontier.add(new FrontierEntry(distanceToNode2, 0, initialNode1, initialNode2, initialEdge)); frontier.add(new FrontierEntry(distanceToNode2, 0, initialNode1, initialNode2, initialEdge));
@ -597,8 +591,7 @@ public class Navigation {
EdgeData signalData = edge.getEdgeData(); EdgeData signalData = edge.getEdgeData();
if (signalData.hasPoints()) { if (signalData.hasPoints()) {
for (TrackEdgePoint point : signalData.getPoints()) { for (TrackEdgePoint point : signalData.getPoints()) {
if (node1 == initialNode1 if (node1 == initialNode1 && point.getLocationOn(edge) < edge.getLength() - distanceToNode2)
&& point.getLocationOn(node1, node2, edge) < edge.getLength(node1, node2) - distanceToNode2)
continue; continue;
if (costRelevant && distance + penalty > maxCost) if (costRelevant && distance + penalty > maxCost)
continue Search; continue Search;
@ -624,10 +617,9 @@ public class Navigation {
List<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>(); List<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>();
Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node2); Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node2);
for (Entry<TrackNode, TrackEdge> connection : connectionsFrom.entrySet()) { for (Entry<TrackNode, TrackEdge> connection : connectionsFrom.entrySet()) {
TrackNode newNode = connection.getKey();
TrackEdge newEdge = connection.getValue(); TrackEdge newEdge = connection.getValue();
Vec3 currentDirection = edge.getDirection(node1, node2, false); Vec3 currentDirection = edge.getDirection(false);
Vec3 newDirection = newEdge.getDirection(node2, newNode, true); Vec3 newDirection = newEdge.getDirection(true);
if (currentDirection.dot(newDirection) < 3 / 4f) if (currentDirection.dot(newDirection) < 3 / 4f)
continue; continue;
validTargets.add(connection); validTargets.add(connection);
@ -639,7 +631,7 @@ public class Navigation {
for (Entry<TrackNode, TrackEdge> target : validTargets) { for (Entry<TrackNode, TrackEdge> target : validTargets) {
TrackNode newNode = target.getKey(); TrackNode newNode = target.getKey();
TrackEdge newEdge = target.getValue(); TrackEdge newEdge = target.getValue();
double newDistance = newEdge.getLength(node2, newNode) + distance; double newDistance = newEdge.getLength() + distance;
int newPenalty = penalty; int newPenalty = penalty;
reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2)));
frontier.add(new FrontierEntry(newDistance, newPenalty, node2, newNode, newEdge)); frontier.add(new FrontierEntry(newDistance, newPenalty, node2, newNode, newEdge));

View file

@ -714,15 +714,15 @@ public class Train {
MutableObject<UUID> prevGroup = new MutableObject<>(null); MutableObject<UUID> prevGroup = new MutableObject<>(null);
if (signalData.hasSignalBoundaries()) { if (signalData.hasSignalBoundaries()) {
SignalBoundary nextBoundary = signalData.next(EdgePointType.SIGNAL, node1, node2, edge, position); SignalBoundary nextBoundary = signalData.next(EdgePointType.SIGNAL, position);
if (nextBoundary == null) { if (nextBoundary == null) {
double d = 0; double d = 0;
SignalBoundary prev = null; SignalBoundary prev = null;
SignalBoundary current = signalData.next(EdgePointType.SIGNAL, node1, node2, edge, 0); SignalBoundary current = signalData.next(EdgePointType.SIGNAL, 0);
while (current != null) { while (current != null) {
prev = current; prev = current;
d = current.getLocationOn(node1, node2, edge); d = current.getLocationOn(edge);
current = signalData.next(EdgePointType.SIGNAL, node1, node2, edge, d); current = signalData.next(EdgePointType.SIGNAL, d);
} }
if (prev != null) { if (prev != null) {
UUID group = prev.getGroup(node2); UUID group = prev.getGroup(node2);
@ -740,9 +740,12 @@ public class Train {
} }
} }
} else if (signalData.singleSignalGroup != null && allGroups.containsKey(signalData.singleSignalGroup)) { } else {
occupy(signalData.singleSignalGroup, null); UUID groupId = signalData.getEffectiveEdgeGroupId(graph);
prevGroup.setValue(signalData.singleSignalGroup); if (allGroups.containsKey(groupId)) {
occupy(groupId, null);
prevGroup.setValue(groupId);
}
} }
forEachTravellingPointBackwards((tp, d) -> { forEachTravellingPointBackwards((tp, d) -> {

View file

@ -27,8 +27,8 @@ public class TrainMigration {
public TrainMigration() {} public TrainMigration() {}
public TrainMigration(TravellingPoint point) { public TrainMigration(TravellingPoint point) {
double t = point.position / point.edge.getLength(point.node1, point.node2); double t = point.position / point.edge.getLength();
fallback = point.edge.getPosition(point.node1, point.node2, t); fallback = point.edge.getPosition(t);
curve = point.edge.isTurn(); curve = point.edge.isTurn();
positionOnOldEdge = point.position; positionOnOldEdge = point.position;
locations = Couple.create(point.node1.getLocation(), point.node2.getLocation()); locations = Couple.create(point.node1.getLocation(), point.node2.getLocation());
@ -71,7 +71,7 @@ public class TrainMigration {
continue; continue;
TrackNode newNode2 = entry.getKey(); TrackNode newNode2 = entry.getKey();
float radius = 1 / 64f; float radius = 1 / 64f;
Vec3 direction = edge.getDirection(newNode1, newNode2, true); Vec3 direction = edge.getDirection(true);
if (!Mth.equal(direction.dot(prevDirection), 1)) if (!Mth.equal(direction.dot(prevDirection), 1))
continue; continue;
Vec3 intersectSphere = VecHelper.intersectSphere(nodeVec, direction, fallback, radius); Vec3 intersectSphere = VecHelper.intersectSphere(nodeVec, direction, fallback, radius);
@ -80,7 +80,7 @@ public class TrainMigration {
if (!Mth.equal(direction.dot(intersectSphere.subtract(nodeVec) if (!Mth.equal(direction.dot(intersectSphere.subtract(nodeVec)
.normalize()), 1)) .normalize()), 1))
continue; continue;
double edgeLength = edge.getLength(newNode1, newNode2); double edgeLength = edge.getLength();
double position = intersectSphere.distanceTo(nodeVec) - radius; double position = intersectSphere.distanceTo(nodeVec) - radius;
if (Double.isNaN(position)) if (Double.isNaN(position))
continue; continue;

View file

@ -153,9 +153,9 @@ public class TravellingPoint {
Entry<TrackNode, TrackEdge> best = null; Entry<TrackNode, TrackEdge> best = null;
for (Entry<TrackNode, TrackEdge> entry : validTargets) { for (Entry<TrackNode, TrackEdge> entry : validTargets) {
Vec3 trajectory = edge.getDirection(node1, node2, false); Vec3 trajectory = edge.getDirection(false);
Vec3 entryTrajectory = entry.getValue() Vec3 entryTrajectory = entry.getValue()
.getDirection(node2, entry.getKey(), true); .getDirection(true);
Vec3 normal = trajectory.cross(upNormal); Vec3 normal = trajectory.cross(upNormal);
double dot = normal.dot(entryTrajectory); double dot = normal.dot(entryTrajectory);
double diff = Math.abs(direction.targetDot - dot); double diff = Math.abs(direction.targetDot - dot);
@ -178,14 +178,14 @@ public class TravellingPoint {
public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector, public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector,
IEdgePointListener signalListener, ITurnListener turnListener) { IEdgePointListener signalListener, ITurnListener turnListener) {
blocked = false; blocked = false;
double edgeLength = edge.getLength(node1, node2); double edgeLength = edge.getLength();
if (distance == 0) if (distance == 0)
return 0; return 0;
double prevPos = position; double prevPos = position;
double traveled = distance; double traveled = distance;
double currentT = position / edgeLength; double currentT = position / edgeLength;
double incrementT = edge.incrementT(node1, node2, currentT, distance); double incrementT = edge.incrementT(currentT, distance);
position = incrementT * edgeLength; position = incrementT * edgeLength;
// FIXME: using incrementT like this becomes inaccurate at medium-long distances // FIXME: using incrementT like this becomes inaccurate at medium-long distances
@ -221,8 +221,8 @@ public class TravellingPoint {
continue; continue;
TrackEdge newEdge = entry.getValue(); TrackEdge newEdge = entry.getValue();
Vec3 currentDirection = edge.getDirection(node1, node2, false); Vec3 currentDirection = edge.getDirection(false);
Vec3 newDirection = newEdge.getDirection(node2, newNode, true); Vec3 newDirection = newEdge.getDirection(true);
if (currentDirection.dot(newDirection) < 3 / 4f) if (currentDirection.dot(newDirection) < 3 / 4f)
continue; continue;
@ -258,7 +258,7 @@ public class TravellingPoint {
} }
prevPos = 0; prevPos = 0;
edgeLength = edge.getLength(node1, node2); edgeLength = edge.getLength();
} }
} else { } else {
@ -274,8 +274,8 @@ public class TravellingPoint {
TrackEdge newEdge = graph.getConnectionsFrom(newNode) TrackEdge newEdge = graph.getConnectionsFrom(newNode)
.get(node1); .get(node1);
Vec3 currentDirection = edge.getDirection(node1, node2, true); Vec3 currentDirection = edge.getDirection(true);
Vec3 newDirection = newEdge.getDirection(newNode, node1, false); Vec3 newDirection = newEdge.getDirection(false);
if (currentDirection.dot(newDirection) < 3 / 4f) if (currentDirection.dot(newDirection) < 3 / 4f)
continue; continue;
@ -297,7 +297,7 @@ public class TravellingPoint {
edge = graph.getConnectionsFrom(node1) edge = graph.getConnectionsFrom(node1)
.get(node2); .get(node2);
collectedDistance += edgeLength; collectedDistance += edgeLength;
edgeLength = edge.getLength(node1, node2); edgeLength = edge.getLength();
position += edgeLength; position += edgeLength;
blockedLocation = blockedLocation =
@ -326,11 +326,11 @@ public class TravellingPoint {
double to = forward ? position : prevPos; double to = forward ? position : prevPos;
List<TrackEdgePoint> edgePoints = edgeData.getPoints(); List<TrackEdgePoint> edgePoints = edgeData.getPoints();
double length = edge.getLength(node1, node2); double length = edge.getLength();
for (int i = 0; i < edgePoints.size(); i++) { for (int i = 0; i < edgePoints.size(); i++) {
int index = forward ? i : edgePoints.size() - i - 1; int index = forward ? i : edgePoints.size() - i - 1;
TrackEdgePoint nextBoundary = edgePoints.get(index); TrackEdgePoint nextBoundary = edgePoints.get(index);
double locationOn = nextBoundary.getLocationOn(node1, node2, edge); double locationOn = nextBoundary.getLocationOn(edge);
double distance = forward ? locationOn : length - locationOn; double distance = forward ? locationOn : length - locationOn;
if (forward ? (locationOn < from || locationOn >= to) : (locationOn <= from || locationOn > to)) if (forward ? (locationOn < from || locationOn >= to) : (locationOn <= from || locationOn > to))
continue; continue;
@ -346,14 +346,14 @@ public class TravellingPoint {
TrackNode n = node1; TrackNode n = node1;
node1 = node2; node1 = node2;
node2 = n; node2 = n;
position = edge.getLength(node1, node2) - position; position = edge.getLength() - position;
edge = graph.getConnectionsFrom(node1) edge = graph.getConnectionsFrom(node1)
.get(node2); .get(node2);
} }
public Vec3 getPosition() { public Vec3 getPosition() {
double t = position / edge.getLength(node1, node2); double t = position / edge.getLength();
return edge.getPosition(node1, node2, t) return edge.getPosition(t)
.add(edge.getNormal(node1, node2, t) .add(edge.getNormal(node1, node2, t)
.scale(1)); .scale(1));
} }

View file

@ -1,15 +1,23 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint; package com.simibubi.create.content.logistics.trains.management.edgePoint;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.google.common.base.Objects;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackEdge;
import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.TrackNode; import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
@ -21,11 +29,15 @@ public class EdgeData {
public static final UUID passiveGroup = UUID.fromString("00000000-0000-0000-0000-000000000000"); public static final UUID passiveGroup = UUID.fromString("00000000-0000-0000-0000-000000000000");
public UUID singleSignalGroup; private UUID singleSignalGroup;
private List<TrackEdgePoint> points; private List<TrackEdgePoint> points;
private List<TrackEdgeIntersection> intersections;
private TrackEdge edge;
public EdgeData() { public EdgeData(TrackEdge edge) {
this.edge = edge;
points = new ArrayList<>(); points = new ArrayList<>();
intersections = new ArrayList<>();
singleSignalGroup = passiveGroup; singleSignalGroup = passiveGroup;
} }
@ -33,56 +45,134 @@ public class EdgeData {
return singleSignalGroup == null; return singleSignalGroup == null;
} }
public UUID getSingleSignalGroup() {
return singleSignalGroup;
}
public void setSingleSignalGroup(@Nullable TrackGraph graph, UUID singleSignalGroup) {
if (graph != null && !Objects.equal(singleSignalGroup, this.singleSignalGroup))
refreshIntersectingSignalGroups(graph);
this.singleSignalGroup = singleSignalGroup;
}
public void refreshIntersectingSignalGroups(TrackGraph graph) {
Map<UUID, SignalEdgeGroup> groups = Create.RAILWAYS.signalEdgeGroups;
for (TrackEdgeIntersection intersection : intersections) {
if (intersection.groupId == null)
continue;
SignalEdgeGroup group = groups.get(intersection.groupId);
if (group != null)
group.removeIntersection(intersection.id);
}
if (hasIntersections())
graph.deferIntersectionUpdate(edge);
}
public boolean hasPoints() { public boolean hasPoints() {
return !points.isEmpty(); return !points.isEmpty();
} }
public boolean hasIntersections() {
return !intersections.isEmpty();
}
public List<TrackEdgeIntersection> getIntersections() {
return intersections;
}
public void addIntersection(TrackGraph graph, UUID id, double position, TrackNode target1, TrackNode target2,
double targetPosition) {
TrackNodeLocation loc1 = target1.getLocation();
TrackNodeLocation loc2 = target2.getLocation();
for (TrackEdgeIntersection existing : intersections)
if (existing.isNear(position) && existing.targets(loc1, loc2))
return;
TrackEdgeIntersection intersection = new TrackEdgeIntersection();
intersection.id = id;
intersection.location = position;
intersection.target = Couple.create(loc1, loc2);
intersection.targetLocation = targetPosition;
intersections.add(intersection);
graph.deferIntersectionUpdate(edge);
}
public void removeIntersection(TrackGraph graph, UUID id) {
refreshIntersectingSignalGroups(graph);
for (Iterator<TrackEdgeIntersection> iterator = intersections.iterator(); iterator.hasNext();) {
TrackEdgeIntersection existing = iterator.next();
if (existing.id.equals(id))
iterator.remove();
}
}
public UUID getGroupAtPosition(TrackGraph graph, double position) {
if (!hasSignalBoundaries())
return getEffectiveEdgeGroupId(graph);
SignalBoundary firstSignal = next(EdgePointType.SIGNAL, 0);
UUID currentGroup = firstSignal.getGroup(edge.node1);
for (TrackEdgePoint trackEdgePoint : getPoints()) {
if (!(trackEdgePoint instanceof SignalBoundary sb))
continue;
if (sb.getLocationOn(edge) >= position)
return currentGroup;
currentGroup = sb.getGroup(edge.node2);
}
return currentGroup;
}
public List<TrackEdgePoint> getPoints() { public List<TrackEdgePoint> getPoints() {
return points; return points;
} }
public void removePoint(TrackNode node1, TrackNode node2, TrackEdge edge, TrackEdgePoint point) { public UUID getEffectiveEdgeGroupId(TrackGraph graph) {
points.remove(point); return singleSignalGroup == null ? null : singleSignalGroup.equals(passiveGroup) ? graph.id : singleSignalGroup;
if (point.getType() == EdgePointType.SIGNAL)
singleSignalGroup = next(point.getType(), node1, node2, edge, 0) == null ? passiveGroup : null;
} }
public <T extends TrackEdgePoint> void addPoint(TrackNode node1, TrackNode node2, TrackEdge edge, public void removePoint(TrackGraph graph, TrackEdgePoint point) {
TrackEdgePoint point) { points.remove(point);
if (point.getType() == EdgePointType.SIGNAL) {
boolean noSignalsRemaining = next(point.getType(), 0) == null;
setSingleSignalGroup(graph, noSignalsRemaining ? passiveGroup : null);
}
}
public <T extends TrackEdgePoint> void addPoint(TrackGraph graph, TrackEdgePoint point) {
if (point.getType() == EdgePointType.SIGNAL) if (point.getType() == EdgePointType.SIGNAL)
singleSignalGroup = null; setSingleSignalGroup(graph, null);
double locationOn = point.getLocationOn(node1, node2, edge); double locationOn = point.getLocationOn(edge);
int i = 0; int i = 0;
for (; i < points.size(); i++) for (; i < points.size(); i++)
if (points.get(i) if (points.get(i)
.getLocationOn(node1, node2, edge) > locationOn) .getLocationOn(edge) > locationOn)
break; break;
points.add(i, point); points.add(i, point);
} }
@Nullable @Nullable
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends TrackEdgePoint> T next(EdgePointType<T> type, TrackNode node1, TrackNode node2, TrackEdge edge, public <T extends TrackEdgePoint> T next(EdgePointType<T> type, double minPosition) {
double minPosition) {
for (TrackEdgePoint point : points) for (TrackEdgePoint point : points)
if (point.getType() == type && point.getLocationOn(node1, node2, edge) > minPosition) if (point.getType() == type && point.getLocationOn(edge) > minPosition)
return (T) point; return (T) point;
return null; return null;
} }
@Nullable @Nullable
public TrackEdgePoint next(TrackNode node1, TrackNode node2, TrackEdge edge, double minPosition) { public TrackEdgePoint next(double minPosition) {
for (TrackEdgePoint point : points) for (TrackEdgePoint point : points)
if (point.getLocationOn(node1, node2, edge) > minPosition) if (point.getLocationOn(edge) > minPosition)
return point; return point;
return null; return null;
} }
@Nullable @Nullable
public <T extends TrackEdgePoint> T get(EdgePointType<T> type, TrackNode node1, TrackNode node2, TrackEdge edge, public <T extends TrackEdgePoint> T get(EdgePointType<T> type, double exactPosition) {
double exactPosition) { T next = next(type, exactPosition - .5f);
T next = next(type, node1, node2, edge, exactPosition - .5f); if (next != null && Mth.equal(next.getLocationOn(edge), exactPosition))
if (next != null && Mth.equal(next.getLocationOn(node1, node2, edge), exactPosition))
return next; return next;
return null; return null;
} }
@ -103,11 +193,13 @@ public class EdgeData {
.toString()); .toString());
return tag; return tag;
})); }));
if (hasIntersections())
nbt.put("Intersections", NBTHelper.writeCompoundList(intersections, TrackEdgeIntersection::write));
return nbt; return nbt;
} }
public static EdgeData read(CompoundTag nbt, TrackGraph graph) { public static EdgeData read(CompoundTag nbt, TrackEdge edge, TrackGraph graph) {
EdgeData data = new EdgeData(); EdgeData data = new EdgeData(edge);
if (nbt.contains("SignalGroup")) if (nbt.contains("SignalGroup"))
data.singleSignalGroup = nbt.getUUID("SignalGroup"); data.singleSignalGroup = nbt.getUUID("SignalGroup");
else if (!nbt.contains("PassiveGroup")) else if (!nbt.contains("PassiveGroup"))
@ -123,6 +215,9 @@ public class EdgeData {
if (point != null) if (point != null)
data.points.add(point); data.points.add(point);
}); });
if (nbt.contains("Intersections"))
data.intersections =
NBTHelper.readCompoundList(nbt.getList("Intersections", Tag.TAG_COMPOUND), TrackEdgeIntersection::read);
return data; return data;
} }

View file

@ -22,7 +22,7 @@ public class EdgePointManager {
TrackNode node2 = startNodes.get(!front); TrackNode node2 = startNodes.get(!front);
TrackEdge startEdge = startEdges.get(front); TrackEdge startEdge = startEdges.get(front);
startEdge.getEdgeData() startEdge.getEdgeData()
.addPoint(node1, node2, startEdge, point); .addPoint(graph, point);
Create.RAILWAYS.sync.edgeDataChanged(graph, node1, node2, startEdge); Create.RAILWAYS.sync.edgeDataChanged(graph, node1, node2, startEdge);
} }
} }
@ -37,7 +37,7 @@ public class EdgePointManager {
if (trackEdge == null) if (trackEdge == null)
return; return;
trackEdge.getEdgeData() trackEdge.getEdgeData()
.removePoint(l1, l2, trackEdge, point); .removePoint(graph, point);
Create.RAILWAYS.sync.edgeDataChanged(graph, l1, l2, trackEdge); Create.RAILWAYS.sync.edgeDataChanged(graph, l1, l2, trackEdge);
}, startNodes.swap()); }, startNodes.swap());
} }

View file

@ -0,0 +1,57 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint;
import java.util.UUID;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation;
import com.simibubi.create.foundation.utility.Couple;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
public class TrackEdgeIntersection {
public double location;
public Couple<TrackNodeLocation> target;
public double targetLocation;
public UUID groupId;
public UUID id;
public TrackEdgeIntersection() {
id = UUID.randomUUID();
}
public boolean isNear(double location) {
return Math.abs(location - this.location) < 1 / 32f;
}
public boolean targets(TrackNodeLocation target1, TrackNodeLocation target2) {
return target1.equals(target.getFirst()) && target2.equals(target.getSecond())
|| target1.equals(target.getSecond()) && target2.equals(target.getFirst());
}
public CompoundTag write() {
CompoundTag nbt = new CompoundTag();
nbt.putUUID("Id", id);
if (groupId != null)
nbt.putUUID("GroupId", groupId);
nbt.putDouble("Location", location);
nbt.putDouble("TargetLocation", targetLocation);
nbt.put("TargetEdge", target.serializeEach(loc -> NbtUtils.writeBlockPos(new BlockPos(loc))));
return nbt;
}
public static TrackEdgeIntersection read(CompoundTag nbt) {
TrackEdgeIntersection intersection = new TrackEdgeIntersection();
intersection.id = nbt.getUUID("Id");
if (nbt.contains("GroupId"))
intersection.groupId = nbt.getUUID("GroupId");
intersection.location = nbt.getDouble("Location");
intersection.targetLocation = nbt.getDouble("TargetLocation");
intersection.target = Couple.deserializeEach(nbt.getList("TargetEdge", Tag.TAG_COMPOUND),
tag -> TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(tag)));
return intersection;
}
}

View file

@ -149,13 +149,13 @@ public class TrackTargetingBehaviour<T extends TrackEdgePoint> extends TileEntit
if (edge == null) if (edge == null)
return null; return null;
double length = edge.getLength(node1, node2); double length = edge.getLength();
CompoundTag data = migrationData; CompoundTag data = migrationData;
migrationData = null; migrationData = null;
{ {
orthogonal = targetBezier == null; orthogonal = targetBezier == null;
Vec3 direction = edge.getDirection(node1, node2, true); Vec3 direction = edge.getDirection(true);
int nonZeroComponents = 0; int nonZeroComponents = 0;
for (Axis axis : Iterate.axes) for (Axis axis : Iterate.axes)
nonZeroComponents += direction.get(axis) != 0 ? 1 : 0; nonZeroComponents += direction.get(axis) != 0 ? 1 : 0;
@ -165,7 +165,7 @@ public class TrackTargetingBehaviour<T extends TrackEdgePoint> extends TileEntit
EdgeData signalData = edge.getEdgeData(); EdgeData signalData = edge.getEdgeData();
if (signalData.hasPoints()) { if (signalData.hasPoints()) {
for (EdgePointType<?> otherType : EdgePointType.TYPES.values()) { for (EdgePointType<?> otherType : EdgePointType.TYPES.values()) {
TrackEdgePoint otherPoint = signalData.get(otherType, node1, node2, edge, loc.position); TrackEdgePoint otherPoint = signalData.get(otherType, loc.position);
if (otherPoint == null) if (otherPoint == null)
continue; continue;
if (otherType != edgePointType) { if (otherType != edgePointType) {

View file

@ -202,7 +202,7 @@ public class TrackTargetingBlockItem extends BlockItem {
double edgePosition = location.position; double edgePosition = location.position;
for (TrackEdgePoint edgePoint : edgeData.getPoints()) { for (TrackEdgePoint edgePoint : edgeData.getPoints()) {
double otherEdgePosition = edgePoint.getLocationOn(nodes.getFirst(), nodes.getSecond(), edge); double otherEdgePosition = edgePoint.getLocationOn(edge);
double distance = Math.abs(edgePosition - otherEdgePosition); double distance = Math.abs(edgePosition - otherEdgePosition);
if (distance > .75) if (distance > .75)
continue; continue;

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.logistics.trains.management.edgePoint;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.GraphLocation; import com.simibubi.create.content.logistics.trains.GraphLocation;
import com.simibubi.create.content.logistics.trains.ITrackBlock; import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBehaviour.RenderedTrackOverlayType; import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBehaviour.RenderedTrackOverlayType;
@ -47,9 +48,13 @@ public class TrackTargetingClient {
BezierTrackPointLocation hoveredBezier = null; BezierTrackPointLocation hoveredBezier = null;
ItemStack stack = player.getMainHandItem(); ItemStack stack = player.getMainHandItem();
if (stack.getItem() instanceof TrackTargetingBlockItem ttbi) if (stack.getItem()instanceof TrackTargetingBlockItem ttbi)
type = ttbi.getType(stack); type = ttbi.getType(stack);
if (type == EdgePointType.SIGNAL)
Create.RAILWAYS.sided(null)
.tickSignalOverlay();
boolean alreadySelected = stack.hasTag() && stack.getTag() boolean alreadySelected = stack.hasTag() && stack.getTag()
.contains("SelectedPos"); .contains("SelectedPos");
@ -78,7 +83,7 @@ public class TrackTargetingClient {
BlockHitResult blockHitResult = (BlockHitResult) hitResult; BlockHitResult blockHitResult = (BlockHitResult) hitResult;
BlockPos pos = blockHitResult.getBlockPos(); BlockPos pos = blockHitResult.getBlockPos();
BlockState blockState = mc.level.getBlockState(pos); BlockState blockState = mc.level.getBlockState(pos);
if (blockState.getBlock() instanceof ITrackBlock track) { if (blockState.getBlock()instanceof ITrackBlock track) {
direction = track.getNearestTrackAxis(mc.level, pos, blockState, lookAngle) direction = track.getNearestTrackAxis(mc.level, pos, blockState, lookAngle)
.getSecond() == AxisDirection.POSITIVE; .getSecond() == AxisDirection.POSITIVE;
hovered = pos; hovered = pos;

View file

@ -0,0 +1,52 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint.signal;
import com.simibubi.create.foundation.utility.Color;
public enum EdgeGroupColor {
YELLOW(0xEBC255),
GREEN(0x51C054),
BLUE(0x5391E1),
ORANGE(0xE36E36),
LAVENDER(0xCB92BA),
RED(0xA43538),
CYAN(0x6EDAD9),
BROWN(0xA17C58),
WHITE(0xE5E1DC)
;
private Color color;
private int mask;
private EdgeGroupColor(int color) {
this.color = new Color(color);
mask = 1 << ordinal();
}
public int strikeFrom(int mask) {
if (this == WHITE)
return mask;
return mask | this.mask;
}
public Color get() {
return color;
}
public static EdgeGroupColor getDefault() {
return values()[0];
}
public static EdgeGroupColor findNextAvailable(int mask) {
EdgeGroupColor[] values = values();
for (int i = 0; i < values.length; i++) {
if ((mask & 1) == 0)
return values[i];
mask = mask >> 1;
}
return WHITE;
}
}

View file

@ -45,9 +45,32 @@ public class SignalBoundary extends TrackEdgePoint {
cachedStates = Couple.create(() -> SignalState.INVALID); cachedStates = Couple.create(() -> SignalState.INVALID);
} }
public void setGroup(TrackNode side, UUID groupId) { public void setGroup(boolean primary, UUID groupId) {
boolean primary = isPrimary(side); UUID previous = groups.get(primary);
groups.set(primary, groupId); groups.set(primary, groupId);
UUID opposite = groups.get(!primary);
Map<UUID, SignalEdgeGroup> signalEdgeGroups = Create.RAILWAYS.signalEdgeGroups;
if (opposite != null && signalEdgeGroups.containsKey(opposite)) {
SignalEdgeGroup oppositeGroup = signalEdgeGroups.get(opposite);
if (previous != null)
oppositeGroup.removeAdjacent(previous);
if (groupId != null)
oppositeGroup.putAdjacent(groupId);
}
if (groupId != null && signalEdgeGroups.containsKey(groupId)) {
SignalEdgeGroup group = signalEdgeGroups.get(groupId);
if (opposite != null)
group.putAdjacent(opposite);
}
}
public void setGroupAndUpdate(TrackNode side, UUID groupId) {
boolean primary = isPrimary(side);
setGroup(primary, groupId);
sidesToUpdate.set(primary, false); sidesToUpdate.set(primary, false);
chainedSignals.set(primary, null); chainedSignals.set(primary, null);
} }
@ -60,6 +83,10 @@ public class SignalBoundary extends TrackEdgePoint {
@Override @Override
public void invalidate(LevelAccessor level) { public void invalidate(LevelAccessor level) {
blockEntities.forEach(s -> s.forEach(pos -> invalidateAt(level, pos))); blockEntities.forEach(s -> s.forEach(pos -> invalidateAt(level, pos)));
groups.forEach(uuid -> {
if (Create.RAILWAYS.signalEdgeGroups.remove(uuid) != null)
Create.RAILWAYS.sync.edgeGroupRemoved(uuid);
});
} }
@Override @Override

View file

@ -1,51 +1,147 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint.signal; package com.simibubi.create.content.logistics.trains.management.edgePoint.signal;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer;
import org.apache.commons.lang3.mutable.MutableInt;
import com.google.common.base.Predicates;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.entity.Train; import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.foundation.utility.Color; import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
public class SignalEdgeGroup { public class SignalEdgeGroup {
public UUID id; public UUID id;
public Color color; public EdgeGroupColor color;
public Set<Train> trains; public Set<Train> trains;
public SignalBoundary reserved; public SignalBoundary reserved;
public Map<UUID, UUID> intersecting;
public Set<SignalEdgeGroup> intersectingResolved;
public Set<UUID> adjacent;
public SignalEdgeGroup(UUID id) { public SignalEdgeGroup(UUID id) {
this.id = id; this.id = id;
color = Color.rainbowColor(Create.RANDOM.nextInt());
trains = new HashSet<>(); trains = new HashSet<>();
adjacent = new HashSet<>();
intersecting = new HashMap<>();
intersectingResolved = new HashSet<>();
color = EdgeGroupColor.getDefault();
} }
public boolean isOccupiedUnless(Train train) { public boolean isOccupiedUnless(Train train) {
if (intersectingResolved.isEmpty())
walkIntersecting(intersectingResolved::add);
for (SignalEdgeGroup group : intersectingResolved)
if (group.isThisOccupiedUnless(train))
return true;
return false;
}
private boolean isThisOccupiedUnless(Train train) {
return reserved != null || trains.size() > 1 || !trains.contains(train) && !trains.isEmpty(); return reserved != null || trains.size() > 1 || !trains.contains(train) && !trains.isEmpty();
} }
public boolean isOccupiedUnless(SignalBoundary boundary) { public boolean isOccupiedUnless(SignalBoundary boundary) {
if (intersectingResolved.isEmpty())
walkIntersecting(intersectingResolved::add);
for (SignalEdgeGroup group : intersectingResolved)
if (group.isThisOccupiedUnless(boundary))
return true;
return false;
}
private boolean isThisOccupiedUnless(SignalBoundary boundary) {
return !trains.isEmpty() || reserved != null && reserved != boundary; return !trains.isEmpty() || reserved != null && reserved != boundary;
} }
public boolean isOccupied() { public void putIntersection(UUID intersectionId, UUID targetGroup) {
return !trains.isEmpty() || reserved != null; intersecting.put(intersectionId, targetGroup);
walkIntersecting(g -> g.intersectingResolved.clear());
resolveColor();
}
public void removeIntersection(UUID intersectionId) {
walkIntersecting(g -> g.intersectingResolved.clear());
UUID removed = intersecting.remove(intersectionId);
SignalEdgeGroup other = Create.RAILWAYS.signalEdgeGroups.get(removed);
if (other != null)
other.intersecting.remove(intersectionId);
resolveColor();
}
public void putAdjacent(UUID adjacent) {
this.adjacent.add(adjacent);
}
public void removeAdjacent(UUID adjacent) {
this.adjacent.remove(adjacent);
}
public void resolveColor() {
if (intersectingResolved.isEmpty())
walkIntersecting(intersectingResolved::add);
MutableInt mask = new MutableInt(0);
intersectingResolved.forEach(group -> group.adjacent.stream()
.map(Create.RAILWAYS.signalEdgeGroups::get)
.filter(Objects::nonNull)
.filter(Predicates.not(intersectingResolved::contains))
.forEach(adjacent -> mask.setValue(adjacent.color.strikeFrom(mask.getValue()))));
EdgeGroupColor newColour = EdgeGroupColor.findNextAvailable(mask.getValue());
if (newColour == color)
return;
walkIntersecting(group -> Create.RAILWAYS.sync.edgeGroupCreated(group.id, group.color = newColour));
Create.RAILWAYS.markTracksDirty();
}
private void walkIntersecting(Consumer<SignalEdgeGroup> callback) {
walkIntersectingRec(new HashSet<>(), callback);
}
private void walkIntersectingRec(Set<SignalEdgeGroup> visited, Consumer<SignalEdgeGroup> callback) {
if (!visited.add(this))
return;
callback.accept(this);
for (UUID uuid : intersecting.values()) {
SignalEdgeGroup group = Create.RAILWAYS.signalEdgeGroups.get(uuid);
if (group != null)
group.walkIntersectingRec(visited, callback);
}
} }
public static SignalEdgeGroup read(CompoundTag tag) { public static SignalEdgeGroup read(CompoundTag tag) {
SignalEdgeGroup group = new SignalEdgeGroup(tag.getUUID("Id")); SignalEdgeGroup group = new SignalEdgeGroup(tag.getUUID("Id"));
group.color = new Color(tag.getInt("Color")); group.color = NBTHelper.readEnum(tag, "Color", EdgeGroupColor.class);
NBTHelper.iterateCompoundList(tag.getList("Connected", Tag.TAG_COMPOUND),
nbt -> group.intersecting.put(nbt.getUUID("Key"), nbt.getUUID("Value")));
return group; return group;
} }
public CompoundTag write() { public CompoundTag write() {
CompoundTag tag = new CompoundTag(); CompoundTag tag = new CompoundTag();
tag.putUUID("Id", id); tag.putUUID("Id", id);
tag.putInt("Color", color.getRGB()); NBTHelper.writeEnum(tag, "Color", color);
tag.put("Connected", NBTHelper.writeCompoundList(intersecting.entrySet(), e -> {
CompoundTag nbt = new CompoundTag();
nbt.putUUID("Key", e.getKey());
nbt.putUUID("Value", e.getValue());
return nbt;
}));
return tag; return tag;
} }

View file

@ -1,11 +1,12 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint.signal; package com.simibubi.create.content.logistics.trains.management.edgePoint.signal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.function.Supplier; import java.util.function.Supplier;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.networking.SimplePacketBase; import com.simibubi.create.foundation.networking.SimplePacketBase;
@ -14,20 +15,30 @@ import net.minecraftforge.network.NetworkEvent.Context;
public class SignalEdgeGroupPacket extends SimplePacketBase { public class SignalEdgeGroupPacket extends SimplePacketBase {
Collection<UUID> ids; List<UUID> ids;
List<EdgeGroupColor> colors;
boolean add; boolean add;
public SignalEdgeGroupPacket(Collection<UUID> ids, boolean add) { public SignalEdgeGroupPacket(UUID id, EdgeGroupColor color) {
this(ImmutableList.of(id), ImmutableList.of(color), true);
}
public SignalEdgeGroupPacket(List<UUID> ids, List<EdgeGroupColor> colors, boolean add) {
this.ids = ids; this.ids = ids;
this.colors = colors;
this.add = add; this.add = add;
} }
public SignalEdgeGroupPacket(FriendlyByteBuf buffer) { public SignalEdgeGroupPacket(FriendlyByteBuf buffer) {
ids = new ArrayList<>(); ids = new ArrayList<>();
colors = new ArrayList<>();
add = buffer.readBoolean(); add = buffer.readBoolean();
int size = buffer.readVarInt(); int size = buffer.readVarInt();
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
ids.add(buffer.readUUID()); ids.add(buffer.readUUID());
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
colors.add(EdgeGroupColor.values()[buffer.readVarInt()]);
} }
@Override @Override
@ -35,6 +46,8 @@ public class SignalEdgeGroupPacket extends SimplePacketBase {
buffer.writeBoolean(add); buffer.writeBoolean(add);
buffer.writeVarInt(ids.size()); buffer.writeVarInt(ids.size());
ids.forEach(buffer::writeUUID); ids.forEach(buffer::writeUUID);
buffer.writeVarInt(colors.size());
colors.forEach(c -> buffer.writeVarInt(c.ordinal()));
} }
@Override @Override
@ -42,11 +55,18 @@ public class SignalEdgeGroupPacket extends SimplePacketBase {
context.get() context.get()
.enqueueWork(() -> { .enqueueWork(() -> {
Map<UUID, SignalEdgeGroup> signalEdgeGroups = CreateClient.RAILWAYS.signalEdgeGroups; Map<UUID, SignalEdgeGroup> signalEdgeGroups = CreateClient.RAILWAYS.signalEdgeGroups;
int i = 0;
for (UUID id : ids) { for (UUID id : ids) {
if (add) if (!add) {
signalEdgeGroups.put(id, new SignalEdgeGroup(id));
else
signalEdgeGroups.remove(id); signalEdgeGroups.remove(id);
continue;
}
SignalEdgeGroup group = new SignalEdgeGroup(id);
signalEdgeGroups.put(id, group);
if (colors.size() > i)
group.color = colors.get(i);
i++;
} }
}); });
context.get() context.get()

View file

@ -36,12 +36,21 @@ public class SignalPropagator {
UUID id = signal.groups.get(front); UUID id = signal.groups.get(front);
if (Create.RAILWAYS.signalEdgeGroups.remove(id) != null) if (Create.RAILWAYS.signalEdgeGroups.remove(id) != null)
Create.RAILWAYS.sync.edgeGroupRemoved(id); Create.RAILWAYS.sync.edgeGroupRemoved(id);
walkSignals(graph, signal, front, pair -> { walkSignals(graph, signal, front, pair -> {
TrackNode node1 = pair.getFirst(); TrackNode node1 = pair.getFirst();
SignalBoundary boundary = pair.getSecond(); SignalBoundary boundary = pair.getSecond();
boundary.queueUpdate(node1); boundary.queueUpdate(node1);
return false; return false;
}, Predicates.alwaysFalse(), false);
}, signalData -> {
if (!signalData.hasSignalBoundaries()) {
signalData.setSingleSignalGroup(graph, EdgeData.passiveGroup);
return true;
}
return false;
}, false);
} }
} }
@ -53,7 +62,15 @@ public class SignalPropagator {
SignalBoundary boundary = pair.getSecond(); SignalBoundary boundary = pair.getSecond();
boundary.queueUpdate(node1); boundary.queueUpdate(node1);
return false; return false;
}, Predicates.alwaysFalse(), false);
}, signalData -> {
if (!signalData.hasSignalBoundaries()) {
signalData.setSingleSignalGroup(graph, EdgeData.passiveGroup);
return true;
}
return false;
}, false);
} }
public static void propagateSignalGroup(TrackGraph graph, SignalBoundary signal, boolean front) { public static void propagateSignalGroup(TrackGraph graph, SignalBoundary signal, boolean front) {
@ -63,8 +80,7 @@ public class SignalPropagator {
SignalEdgeGroup group = new SignalEdgeGroup(UUID.randomUUID()); SignalEdgeGroup group = new SignalEdgeGroup(UUID.randomUUID());
UUID groupId = group.id; UUID groupId = group.id;
globalGroups.put(groupId, group); globalGroups.put(groupId, group);
sync.edgeGroupCreated(groupId); signal.setGroup(front, groupId);
signal.groups.set(front, groupId);
sync.pointAdded(graph, signal); sync.pointAdded(graph, signal);
walkSignals(graph, signal, front, pair -> { walkSignals(graph, signal, front, pair -> {
@ -74,18 +90,22 @@ public class SignalPropagator {
if (currentGroup != null) if (currentGroup != null)
if (globalGroups.remove(currentGroup) != null) if (globalGroups.remove(currentGroup) != null)
sync.edgeGroupRemoved(currentGroup); sync.edgeGroupRemoved(currentGroup);
boundary.setGroup(node1, groupId); boundary.setGroupAndUpdate(node1, groupId);
sync.pointAdded(graph, boundary); sync.pointAdded(graph, boundary);
return true; return true;
}, signalData -> { }, signalData -> {
if (signalData.singleSignalGroup != null) UUID singleSignalGroup = signalData.getSingleSignalGroup();
if (globalGroups.remove(signalData.singleSignalGroup) != null) if (singleSignalGroup != null)
sync.edgeGroupRemoved(signalData.singleSignalGroup); if (globalGroups.remove(singleSignalGroup) != null)
signalData.singleSignalGroup = groupId; sync.edgeGroupRemoved(singleSignalGroup);
signalData.setSingleSignalGroup(graph, groupId);
return true; return true;
}, false); }, false);
group.resolveColor();
sync.edgeGroupCreated(groupId, group.color);
} }
public static Map<UUID, Boolean> collectChainedSignals(TrackGraph graph, SignalBoundary signal, boolean front) { public static Map<UUID, Boolean> collectChainedSignals(TrackGraph graph, SignalBoundary signal, boolean front) {
@ -117,14 +137,18 @@ public class SignalPropagator {
if (!forCollection) { if (!forCollection) {
notifyTrains(graph, startEdge, oppositeEdge); notifyTrains(graph, startEdge, oppositeEdge);
startEdge.getEdgeData()
.refreshIntersectingSignalGroups(graph);
Create.RAILWAYS.sync.edgeDataChanged(graph, node1, node2, startEdge, oppositeEdge); Create.RAILWAYS.sync.edgeDataChanged(graph, node1, node2, startEdge, oppositeEdge);
} }
// Check for signal on the same edge // Check for signal on the same edge
SignalBoundary immediateBoundary = startEdge.getEdgeData() SignalBoundary immediateBoundary = startEdge.getEdgeData()
.next(EdgePointType.SIGNAL, node1, node2, startEdge, signal.getLocationOn(node1, node2, startEdge)); .next(EdgePointType.SIGNAL, signal.getLocationOn(startEdge));
if (immediateBoundary != null) { if (immediateBoundary != null) {
boundaryCallback.test(Pair.of(node1, immediateBoundary)); if (boundaryCallback.test(Pair.of(node1, immediateBoundary)))
startEdge.getEdgeData()
.refreshIntersectingSignalGroups(graph);
return; return;
} }
@ -159,8 +183,8 @@ public class SignalPropagator {
if (forCollection) { if (forCollection) {
Vec3 currentDirection = graph.getConnectionsFrom(prevNode) Vec3 currentDirection = graph.getConnectionsFrom(prevNode)
.get(currentNode) .get(currentNode)
.getDirection(prevNode, currentNode, false); .getDirection(false);
Vec3 newDirection = edge.getDirection(currentNode, nextNode, true); Vec3 newDirection = edge.getDirection(true);
if (currentDirection.dot(newDirection) < 3 / 4f) if (currentDirection.dot(newDirection) < 3 / 4f)
continue; continue;
} }
@ -175,20 +199,21 @@ public class SignalPropagator {
// no boundary- update group of edge // no boundary- update group of edge
if (!signalData.hasSignalBoundaries()) { if (!signalData.hasSignalBoundaries()) {
if (!nonBoundaryCallback.test(signalData)) if (nonBoundaryCallback.test(signalData)) {
continue;
notifyTrains(graph, currentEdge); notifyTrains(graph, currentEdge);
Create.RAILWAYS.sync.edgeDataChanged(graph, currentNode, nextNode, edge, oppositeEdge); Create.RAILWAYS.sync.edgeDataChanged(graph, currentNode, nextNode, edge, oppositeEdge);
}
continue; continue;
} }
// other/own boundary found // other/own boundary found
SignalBoundary nextBoundary = SignalBoundary nextBoundary = signalData.next(EdgePointType.SIGNAL, 0);
signalData.next(EdgePointType.SIGNAL, currentNode, nextNode, currentEdge, 0);
if (nextBoundary == null) if (nextBoundary == null)
continue; continue;
if (boundaryCallback.test(Pair.of(currentNode, nextBoundary))) { if (boundaryCallback.test(Pair.of(currentNode, nextBoundary))) {
notifyTrains(graph, edge, oppositeEdge); notifyTrains(graph, edge, oppositeEdge);
currentEdge.getEdgeData()
.refreshIntersectingSignalGroups(graph);
Create.RAILWAYS.sync.edgeDataChanged(graph, currentNode, nextNode, edge, oppositeEdge); Create.RAILWAYS.sync.edgeDataChanged(graph, currentNode, nextNode, edge, oppositeEdge);
} }
continue EdgeWalk; continue EdgeWalk;

View file

@ -71,8 +71,8 @@ public abstract class TrackEdgePoint {
this.position = position; this.position = position;
} }
public double getLocationOn(TrackNode node1, TrackNode node2, TrackEdge edge) { public double getLocationOn(TrackEdge edge) {
return isPrimary(node1) ? edge.getLength(node1, node2) - position : position; return isPrimary(edge.node1) ? edge.getLength() - position : position;
} }
public boolean canNavigateVia(TrackNode side) { public boolean canNavigateVia(TrackNode side) {

View file

@ -316,7 +316,7 @@ public class StationTileEntity extends SmartTileEntity {
} }
BlockState potentialBogeyState = level.getBlockState(bogeyOffset.offset(currentPos)); BlockState potentialBogeyState = level.getBlockState(bogeyOffset.offset(currentPos));
if (potentialBogeyState.getBlock() instanceof IBogeyBlock bogey && bogeyIndex < bogeyLocations.length) { if (potentialBogeyState.getBlock()instanceof IBogeyBlock bogey && bogeyIndex < bogeyLocations.length) {
bogeyTypes[bogeyIndex] = bogey; bogeyTypes[bogeyIndex] = bogey;
bogeyLocations[bogeyIndex] = i; bogeyLocations[bogeyIndex] = i;
bogeyIndex++; bogeyIndex++;
@ -449,7 +449,7 @@ public class StationTileEntity extends SmartTileEntity {
TrackNode otherNode = entry.getKey(); TrackNode otherNode = entry.getKey();
if (edge.isTurn()) if (edge.isTurn())
continue; continue;
Vec3 edgeDirection = edge.getDirection(node, otherNode, true); Vec3 edgeDirection = edge.getDirection(true);
if (Mth.equal(edgeDirection.normalize() if (Mth.equal(edgeDirection.normalize()
.dot(directionVec), -1d)) .dot(directionVec), -1d))
secondNode = otherNode; secondNode = otherNode;
@ -638,7 +638,7 @@ public class StationTileEntity extends SmartTileEntity {
return true; return true;
BlockState target = edgePoint.getTrackBlockState(); BlockState target = edgePoint.getTrackBlockState();
if (!(target.getBlock() instanceof ITrackBlock def)) if (!(target.getBlock()instanceof ITrackBlock def))
return false; return false;
Vec3 axis = null; Vec3 axis = null;

View file

@ -302,6 +302,20 @@ public class VecHelper {
.add(p2.scale(3 * t * t)); .add(p2.scale(3 * t * t));
} }
@Nullable
public static double[] intersectRanged(Vec3 p1, Vec3 q1, Vec3 p2, Vec3 q2, Axis plane) {
Vec3 pDiff = p2.subtract(p1);
Vec3 qDiff = q2.subtract(q1);
double[] intersect = intersect(p1, q1, pDiff.normalize(), qDiff.normalize(), plane);
if (intersect == null)
return null;
if (intersect[0] < 0 || intersect[1] < 0)
return null;
if (intersect[0] > pDiff.length() || intersect[1] > qDiff.length())
return null;
return intersect;
}
@Nullable @Nullable
public static double[] intersect(Vec3 p1, Vec3 p2, Vec3 r, Vec3 s, Axis plane) { public static double[] intersect(Vec3 p1, Vec3 p2, Vec3 r, Vec3 s, Axis plane) {
if (plane == Axis.X) { if (plane == Axis.X) {