wip: continuous backbone floor profile for the cave planner (L134 design 4, untested)
Written by Luna before the Codex usage limit cut the round: one shared distance/floor profile across backbone rooms and edges, edge cubic floors derived from it, sloped route-room floors, lower-floor backing cap at overlaps, and validator checks for joint height/slope continuity. Not yet built or audited. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
379093401e
commit
4a97023b24
3 files changed
+294
-17
No files matched your search
@@ -497,8 +497,9 @@ namespace VoxelForgeCore
|
|||||||
// Version 226 unifies the passage influence reach in metres and converts it to core
|
// Version 226 unifies the passage influence reach in metres and converts it to core
|
||||||
// voxels for every broadphase and proof bound. Version 227 uses the exact smooth-max
|
// voxels for every broadphase and proof bound. Version 227 uses the exact smooth-max
|
||||||
// support width/6 when deriving that reach. Version 228 composes deterministic cave
|
// support width/6 when deriving that reach. Version 228 composes deterministic cave
|
||||||
// graphs from fixed passage mouths and the owner Tunnel Network settings.
|
// graphs from fixed passage mouths and the owner Tunnel Network settings. Version 229
|
||||||
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 229;
|
// budgets cave floor support; 230 records a shared backbone floor profile and joint cap.
|
||||||
|
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 230;
|
||||||
|
|
||||||
enum class FieldChannel : std::uint32_t
|
enum class FieldChannel : std::uint32_t
|
||||||
{
|
{
|
||||||
@@ -893,6 +894,7 @@ namespace VoxelForgeCore
|
|||||||
inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV2 = 2;
|
inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV2 = 2;
|
||||||
inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV3 = 3;
|
inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV3 = 3;
|
||||||
inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV1 = 1;
|
inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV1 = 1;
|
||||||
|
inline constexpr std::uint32_t ResolvedLayoutCavernGraphVocabularyVersionV2 = 2;
|
||||||
inline constexpr std::uint32_t ResolvedLayoutOreVeinVocabularyVersionV1 = 1;
|
inline constexpr std::uint32_t ResolvedLayoutOreVeinVocabularyVersionV1 = 1;
|
||||||
enum class ResolvedLayoutValidationModeV3 : std::uint8_t { compatibility, strict };
|
enum class ResolvedLayoutValidationModeV3 : std::uint8_t { compatibility, strict };
|
||||||
|
|
||||||
@@ -957,6 +959,11 @@ namespace VoxelForgeCore
|
|||||||
std::array<std::int64_t, 3> centerMicrometres{};
|
std::array<std::int64_t, 3> centerMicrometres{};
|
||||||
std::array<std::int64_t, 3> halfExtentsMicrometres{};
|
std::array<std::int64_t, 3> halfExtentsMicrometres{};
|
||||||
std::int64_t floorMicrometres = 0;
|
std::int64_t floorMicrometres = 0;
|
||||||
|
// Optional shared backbone profile reference. The room floor plane is
|
||||||
|
// floorMicrometres plus this Q16 XY gradient from centerMicrometres.
|
||||||
|
std::string floorProfileStableId;
|
||||||
|
std::int64_t floorProfileDistanceMicrometres = -1;
|
||||||
|
std::array<std::int32_t, 2> floorGradientQ16{};
|
||||||
std::int64_t smoothWidthMicrometres = 0;
|
std::int64_t smoothWidthMicrometres = 0;
|
||||||
// Optional feature-to-feature union width. Zero preserves the historical
|
// Optional feature-to-feature union width. Zero preserves the historical
|
||||||
// behavior where smoothWidthMicrometres also controls the graph union.
|
// behavior where smoothWidthMicrometres also controls the graph union.
|
||||||
@@ -976,6 +983,11 @@ namespace VoxelForgeCore
|
|||||||
std::int32_t strateIndex = -1;
|
std::int32_t strateIndex = -1;
|
||||||
// A single cubic Bezier in XY. Its four Z values are the cubic floor profile.
|
// A single cubic Bezier in XY. Its four Z values are the cubic floor profile.
|
||||||
std::array<std::array<std::int64_t, 3>, 4> controlPointsMicrometres{};
|
std::array<std::array<std::int64_t, 3>, 4> controlPointsMicrometres{};
|
||||||
|
// Edges with the same profile ID are adjacent pieces of one shared h(s).
|
||||||
|
// Distances are signed along that profile, so a terminal edge may be reversed.
|
||||||
|
std::string floorProfileStableId;
|
||||||
|
std::int64_t floorProfileStartDistanceMicrometres = -1;
|
||||||
|
std::int64_t floorProfileEndDistanceMicrometres = -1;
|
||||||
// Resolve-time, error-bounded approximation of the cubic centerline. Each
|
// Resolve-time, error-bounded approximation of the cubic centerline. Each
|
||||||
// consecutive pair is a physical segment used by the native cave field.
|
// consecutive pair is a physical segment used by the native cave field.
|
||||||
std::vector<std::array<std::int64_t, 3>> polylineMicrometres;
|
std::vector<std::array<std::int64_t, 3>> polylineMicrometres;
|
||||||
|
|||||||
@@ -621,6 +621,10 @@ namespace
|
|||||||
<<"],\"half_extents_um\":["<<room.halfExtentsMicrometres[0]<<','
|
<<"],\"half_extents_um\":["<<room.halfExtentsMicrometres[0]<<','
|
||||||
<<room.halfExtentsMicrometres[1]<<','<<room.halfExtentsMicrometres[2]
|
<<room.halfExtentsMicrometres[1]<<','<<room.halfExtentsMicrometres[2]
|
||||||
<<"],\"floor_um\":"<<room.floorMicrometres
|
<<"],\"floor_um\":"<<room.floorMicrometres
|
||||||
|
<<",\"floor_profile_id\":"<<LayoutEscape(room.floorProfileStableId)
|
||||||
|
<<",\"floor_profile_distance_um\":"<<room.floorProfileDistanceMicrometres
|
||||||
|
<<",\"floor_gradient_q16\":["<<room.floorGradientQ16[0]<<','
|
||||||
|
<<room.floorGradientQ16[1]<<']'
|
||||||
<<",\"smooth_width_um\":"<<room.smoothWidthMicrometres
|
<<",\"smooth_width_um\":"<<room.smoothWidthMicrometres
|
||||||
;
|
;
|
||||||
if(room.unionWidthMicrometres>0)
|
if(room.unionWidthMicrometres>0)
|
||||||
@@ -647,6 +651,11 @@ namespace
|
|||||||
out<<'['<<p[0]<<','<<p[1]<<','<<p[2]<<']';
|
out<<'['<<p[0]<<','<<p[1]<<','<<p[2]<<']';
|
||||||
}
|
}
|
||||||
out<<']';
|
out<<']';
|
||||||
|
out<<",\"floor_profile_id\":"<<LayoutEscape(edge.floorProfileStableId)
|
||||||
|
<<",\"floor_profile_start_distance_um\":"
|
||||||
|
<<edge.floorProfileStartDistanceMicrometres
|
||||||
|
<<",\"floor_profile_end_distance_um\":"
|
||||||
|
<<edge.floorProfileEndDistanceMicrometres;
|
||||||
if(!edge.polylineMicrometres.empty())
|
if(!edge.polylineMicrometres.empty())
|
||||||
{
|
{
|
||||||
out<<",\"polyline_um\":[";
|
out<<",\"polyline_um\":[";
|
||||||
@@ -1235,7 +1244,8 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
|||||||
add("cavern_graph_vocabulary_version",
|
add("cavern_graph_vocabulary_version",
|
||||||
"cavern graph vocabulary version is set without graph records");
|
"cavern graph vocabulary version is set without graph records");
|
||||||
}
|
}
|
||||||
else if(cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV1)
|
else if(cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV1
|
||||||
|
&&cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV2)
|
||||||
add("cavern_graph_vocabulary_version",
|
add("cavern_graph_vocabulary_version",
|
||||||
"cavern graph records require vocabulary version 1");
|
"cavern graph records require vocabulary version 1");
|
||||||
if(cavernGraphVocabularyVersion!=0)
|
if(cavernGraphVocabularyVersion!=0)
|
||||||
@@ -1268,6 +1278,16 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
|||||||
||(room.irregularityAmplitudeMicrometres>0
|
||(room.irregularityAmplitudeMicrometres>0
|
||||||
&&room.irregularityWavelengthMicrometres<=0))
|
&&room.irregularityWavelengthMicrometres<=0))
|
||||||
add("cavern_room_parameters",room.stableId+" has invalid or unbounded parameters");
|
add("cavern_room_parameters",room.stableId+" has invalid or unbounded parameters");
|
||||||
|
const std::int64_t gradientSquared=static_cast<std::int64_t>(room.floorGradientQ16[0])
|
||||||
|
*room.floorGradientQ16[0]+static_cast<std::int64_t>(room.floorGradientQ16[1])
|
||||||
|
*room.floorGradientQ16[1];
|
||||||
|
if((room.floorProfileStableId.empty()
|
||||||
|
&&room.floorProfileDistanceMicrometres!=-1)
|
||||||
|
||(!room.floorProfileStableId.empty()
|
||||||
|
&&room.floorProfileDistanceMicrometres<0)
|
||||||
|
||gradientSquared>static_cast<std::int64_t>(13108)*13108)
|
||||||
|
add("cavern_room_floor_profile",room.stableId+
|
||||||
|
" has an invalid shared profile reference or floor grade");
|
||||||
const std::int64_t pad=room.smoothWidthMicrometres
|
const std::int64_t pad=room.smoothWidthMicrometres
|
||||||
+room.unionWidthMicrometres+room.irregularityAmplitudeMicrometres;
|
+room.unionWidthMicrometres+room.irregularityAmplitudeMicrometres;
|
||||||
for(std::size_t axis=0;axis<3;++axis)
|
for(std::size_t axis=0;axis<3;++axis)
|
||||||
@@ -1308,6 +1328,15 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
|||||||
||(edge.irregularityAmplitudeMicrometres>0
|
||(edge.irregularityAmplitudeMicrometres>0
|
||||||
&&edge.irregularityWavelengthMicrometres<=0))
|
&&edge.irregularityWavelengthMicrometres<=0))
|
||||||
add("cavern_edge_parameters",edge.stableId+" has invalid or unbounded parameters");
|
add("cavern_edge_parameters",edge.stableId+" has invalid or unbounded parameters");
|
||||||
|
const bool hasFloorProfile=!edge.floorProfileStableId.empty();
|
||||||
|
if((hasFloorProfile&&(edge.floorProfileStartDistanceMicrometres<0
|
||||||
|
||edge.floorProfileEndDistanceMicrometres<0
|
||||||
|
||edge.floorProfileStartDistanceMicrometres
|
||||||
|
==edge.floorProfileEndDistanceMicrometres))
|
||||||
|
||(!hasFloorProfile&&(edge.floorProfileStartDistanceMicrometres!=-1
|
||||||
|
||edge.floorProfileEndDistanceMicrometres!=-1)))
|
||||||
|
add("cavern_edge_floor_profile",edge.stableId+
|
||||||
|
" has an invalid shared profile distance interval");
|
||||||
std::array<std::int64_t,3> low=edge.controlPointsMicrometres[0];
|
std::array<std::int64_t,3> low=edge.controlPointsMicrometres[0];
|
||||||
std::array<std::int64_t,3> high=low;
|
std::array<std::int64_t,3> high=low;
|
||||||
if(nativeLattice&&!edge.polylineMicrometres.empty())
|
if(nativeLattice&&!edge.polylineMicrometres.empty())
|
||||||
@@ -1363,6 +1392,114 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
|||||||
add("cavern_edge_polyline",edge.stableId+
|
add("cavern_edge_polyline",edge.stableId+
|
||||||
" has no bounded resolve-time polyline with matching endpoints");
|
" has no bounded resolve-time polyline with matching endpoints");
|
||||||
}
|
}
|
||||||
|
std::vector<std::string> floorProfileIds;
|
||||||
|
for(const auto& edge:cavernEdges)
|
||||||
|
if(!edge.floorProfileStableId.empty()
|
||||||
|
&&std::find(floorProfileIds.begin(),floorProfileIds.end(),
|
||||||
|
edge.floorProfileStableId)==floorProfileIds.end())
|
||||||
|
floorProfileIds.push_back(edge.floorProfileStableId);
|
||||||
|
for(const std::string& profileId:floorProfileIds)
|
||||||
|
{
|
||||||
|
std::vector<const ResolvedCavernEdgeV1*> profileEdges;
|
||||||
|
for(const auto& edge:cavernEdges)
|
||||||
|
if(edge.floorProfileStableId==profileId)profileEdges.push_back(&edge);
|
||||||
|
std::sort(profileEdges.begin(),profileEdges.end(),[](const auto* a,const auto* b)
|
||||||
|
{
|
||||||
|
return std::min(a->floorProfileStartDistanceMicrometres,
|
||||||
|
a->floorProfileEndDistanceMicrometres)
|
||||||
|
<std::min(b->floorProfileStartDistanceMicrometres,
|
||||||
|
b->floorProfileEndDistanceMicrometres);
|
||||||
|
});
|
||||||
|
const auto endpointZ=[](const ResolvedCavernEdgeV1& edge,bool atMinimum)
|
||||||
|
{
|
||||||
|
const bool forward=edge.floorProfileStartDistanceMicrometres
|
||||||
|
<edge.floorProfileEndDistanceMicrometres;
|
||||||
|
const std::size_t point=(atMinimum==forward)?0:3;
|
||||||
|
return edge.controlPointsMicrometres[point][2];
|
||||||
|
};
|
||||||
|
const auto endpointSlope=[](const ResolvedCavernEdgeV1& edge,bool atMinimum)
|
||||||
|
{
|
||||||
|
const double span=static_cast<double>(edge.floorProfileEndDistanceMicrometres
|
||||||
|
-edge.floorProfileStartDistanceMicrometres);
|
||||||
|
if(span==0.0)return std::numeric_limits<double>::infinity();
|
||||||
|
const bool forward=edge.floorProfileStartDistanceMicrometres
|
||||||
|
<edge.floorProfileEndDistanceMicrometres;
|
||||||
|
const std::size_t control=atMinimum==forward?0:2;
|
||||||
|
return 3.0*(edge.controlPointsMicrometres[control+1][2]
|
||||||
|
-edge.controlPointsMicrometres[control][2])/span;
|
||||||
|
};
|
||||||
|
const ResolvedCavernEdgeV1* previous=nullptr;
|
||||||
|
for(const auto* edge:profileEdges)
|
||||||
|
{
|
||||||
|
const std::int64_t low=std::min(edge->floorProfileStartDistanceMicrometres,
|
||||||
|
edge->floorProfileEndDistanceMicrometres);
|
||||||
|
const std::int64_t high=std::max(edge->floorProfileStartDistanceMicrometres,
|
||||||
|
edge->floorProfileEndDistanceMicrometres);
|
||||||
|
const double slope0=endpointSlope(*edge,true);
|
||||||
|
const double slope1=endpointSlope(*edge,false);
|
||||||
|
if(std::max(std::abs(slope0),std::abs(slope1))>0.200001)
|
||||||
|
add("cavern_floor_profile_grade",edge->stableId+
|
||||||
|
" exceeds the shared backbone 1:5 grade limit");
|
||||||
|
if(previous)
|
||||||
|
{
|
||||||
|
const std::int64_t previousHigh=std::max(
|
||||||
|
previous->floorProfileStartDistanceMicrometres,
|
||||||
|
previous->floorProfileEndDistanceMicrometres);
|
||||||
|
if(low!=previousHigh
|
||||||
|
||std::abs(endpointZ(*previous,false)-endpointZ(*edge,true))>1000
|
||||||
|
||std::abs(endpointSlope(*previous,false)-slope0)>0.000001)
|
||||||
|
add("cavern_floor_profile_joint",profileId+
|
||||||
|
" has a gap, height step, or slope step between adjacent edges");
|
||||||
|
}
|
||||||
|
previous=edge;
|
||||||
|
(void)high;
|
||||||
|
}
|
||||||
|
for(const auto& room:cavernRooms)
|
||||||
|
{
|
||||||
|
if(room.floorProfileStableId!=profileId)continue;
|
||||||
|
const ResolvedCavernEdgeV1* containing=nullptr;
|
||||||
|
for(const auto* edge:profileEdges)
|
||||||
|
{
|
||||||
|
const auto low=std::min(edge->floorProfileStartDistanceMicrometres,
|
||||||
|
edge->floorProfileEndDistanceMicrometres);
|
||||||
|
const auto high=std::max(edge->floorProfileStartDistanceMicrometres,
|
||||||
|
edge->floorProfileEndDistanceMicrometres);
|
||||||
|
if(room.floorProfileDistanceMicrometres>=low
|
||||||
|
&&room.floorProfileDistanceMicrometres<=high)
|
||||||
|
{containing=edge;break;}
|
||||||
|
}
|
||||||
|
if(!containing)
|
||||||
|
{
|
||||||
|
add("cavern_room_floor_profile",room.stableId+
|
||||||
|
" does not lie on its referenced continuous floor profile");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& edge=*containing;
|
||||||
|
const double t=static_cast<double>(room.floorProfileDistanceMicrometres
|
||||||
|
-edge.floorProfileStartDistanceMicrometres)
|
||||||
|
/(edge.floorProfileEndDistanceMicrometres
|
||||||
|
-edge.floorProfileStartDistanceMicrometres);
|
||||||
|
const double one=1.0-t;
|
||||||
|
const double expected=one*one*one*edge.controlPointsMicrometres[0][2]
|
||||||
|
+3.0*one*one*t*edge.controlPointsMicrometres[1][2]
|
||||||
|
+3.0*one*t*t*edge.controlPointsMicrometres[2][2]
|
||||||
|
+t*t*t*edge.controlPointsMicrometres[3][2];
|
||||||
|
std::array<double,2> routeXY{};
|
||||||
|
for(std::size_t axis=0;axis<2;++axis)
|
||||||
|
routeXY[axis]=one*one*one*edge.controlPointsMicrometres[0][axis]
|
||||||
|
+3.0*one*one*t*edge.controlPointsMicrometres[1][axis]
|
||||||
|
+3.0*one*t*t*edge.controlPointsMicrometres[2][axis]
|
||||||
|
+t*t*t*edge.controlPointsMicrometres[3][axis];
|
||||||
|
const double roomFloorAtProfile=room.floorMicrometres
|
||||||
|
+room.floorGradientQ16[0]/65536.0
|
||||||
|
*(routeXY[0]-room.centerMicrometres[0])
|
||||||
|
+room.floorGradientQ16[1]/65536.0
|
||||||
|
*(routeXY[1]-room.centerMicrometres[1]);
|
||||||
|
if(std::abs(expected-roomFloorAtProfile)>1000.0)
|
||||||
|
add("cavern_room_floor_profile",room.stableId+
|
||||||
|
" floor does not match its referenced h(s)");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(oreVeins.empty())
|
if(oreVeins.empty())
|
||||||
{
|
{
|
||||||
@@ -1526,6 +1663,12 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
|
|||||||
readTriplet("bounds_min_um",room.boundsMinimumMicrometres);
|
readTriplet("bounds_min_um",room.boundsMinimumMicrometres);
|
||||||
readTriplet("bounds_max_um",room.boundsMaximumMicrometres);
|
readTriplet("bounds_max_um",room.boundsMaximumMicrometres);
|
||||||
room.floorMicrometres=LayoutInt(v,"floor_um");
|
room.floorMicrometres=LayoutInt(v,"floor_um");
|
||||||
|
room.floorProfileStableId=LayoutString(v,"floor_profile_id");
|
||||||
|
if(v.Get("floor_profile_distance_um").IsNumber())
|
||||||
|
room.floorProfileDistanceMicrometres=LayoutInt(v,"floor_profile_distance_um");
|
||||||
|
const auto& floorGradient=v.Get("floor_gradient_q16").AsArray();
|
||||||
|
for(std::size_t axis=0;axis<2&&axis<floorGradient.size();++axis)
|
||||||
|
room.floorGradientQ16[axis]=static_cast<std::int32_t>(floorGradient[axis].Integer());
|
||||||
room.smoothWidthMicrometres=LayoutInt(v,"smooth_width_um");
|
room.smoothWidthMicrometres=LayoutInt(v,"smooth_width_um");
|
||||||
room.unionWidthMicrometres=LayoutInt(v,"union_width_um");
|
room.unionWidthMicrometres=LayoutInt(v,"union_width_um");
|
||||||
room.irregularityAmplitudeMicrometres=LayoutInt(v,"irregularity_amplitude_um");
|
room.irregularityAmplitudeMicrometres=LayoutInt(v,"irregularity_amplitude_um");
|
||||||
@@ -1539,6 +1682,13 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
|
|||||||
edge.startAnchorStableId=LayoutString(v,"start_anchor_stable_id");
|
edge.startAnchorStableId=LayoutString(v,"start_anchor_stable_id");
|
||||||
edge.endRoomStableId=LayoutString(v,"end_room_stable_id");
|
edge.endRoomStableId=LayoutString(v,"end_room_stable_id");
|
||||||
edge.strateIndex=static_cast<std::int32_t>(LayoutInt(v,"strate_index"));
|
edge.strateIndex=static_cast<std::int32_t>(LayoutInt(v,"strate_index"));
|
||||||
|
edge.floorProfileStableId=LayoutString(v,"floor_profile_id");
|
||||||
|
if(v.Get("floor_profile_start_distance_um").IsNumber())
|
||||||
|
edge.floorProfileStartDistanceMicrometres=
|
||||||
|
LayoutInt(v,"floor_profile_start_distance_um");
|
||||||
|
if(v.Get("floor_profile_end_distance_um").IsNumber())
|
||||||
|
edge.floorProfileEndDistanceMicrometres=
|
||||||
|
LayoutInt(v,"floor_profile_end_distance_um");
|
||||||
const auto& points=v.Get("control_points_um").AsArray();
|
const auto& points=v.Get("control_points_um").AsArray();
|
||||||
for(std::size_t i=0;i<edge.controlPointsMicrometres.size()&&i<points.size();++i)
|
for(std::size_t i=0;i<edge.controlPointsMicrometres.size()&&i<points.size();++i)
|
||||||
{
|
{
|
||||||
@@ -1879,7 +2029,7 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
|||||||
if(!candidate.finalize(error))return false;
|
if(!candidate.finalize(error))return false;
|
||||||
layout=std::move(candidate);return true;
|
layout=std::move(candidate);return true;
|
||||||
}
|
}
|
||||||
candidate.cavernGraphVocabularyVersion=ResolvedLayoutCavernGraphVocabularyVersionV1;
|
candidate.cavernGraphVocabularyVersion=ResolvedLayoutCavernGraphVocabularyVersionV2;
|
||||||
candidate.cavernRooms.assign(rooms.begin(),rooms.end());
|
candidate.cavernRooms.assign(rooms.begin(),rooms.end());
|
||||||
candidate.cavernEdges.assign(edges.begin(),edges.end());
|
candidate.cavernEdges.assign(edges.begin(),edges.end());
|
||||||
const auto isNativeCavesStrate=[&candidate](std::int32_t strateIndex)
|
const auto isNativeCavesStrate=[&candidate](std::int32_t strateIndex)
|
||||||
@@ -2195,6 +2345,42 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
cumulative[i]=cumulative[i-1]+distance2d(routePoints[i-1],routePoints[i]);
|
cumulative[i]=cumulative[i-1]+distance2d(routePoints[i-1],routePoints[i]);
|
||||||
if(cumulative.back()!=totalRouteLength)
|
if(cumulative.back()!=totalRouteLength)
|
||||||
{error="cavern planner route length accounting failed";return false;}
|
{error="cavern planner route length accounting failed";return false;}
|
||||||
|
const std::string backboneFloorProfileId="tunnel-network/strate-"
|
||||||
|
+std::to_string(strateIndex)+"/backbone-floor";
|
||||||
|
const auto routeFloorAt=[&](std::int64_t distance)
|
||||||
|
{
|
||||||
|
distance=std::clamp<std::int64_t>(distance,0,totalRouteLength);
|
||||||
|
return routeStart[2]+dz*distance/totalRouteLength;
|
||||||
|
};
|
||||||
|
const auto routeFloorGradientAt=[&](std::int64_t distance)
|
||||||
|
{
|
||||||
|
distance=std::clamp<std::int64_t>(distance,0,totalRouteLength);
|
||||||
|
std::size_t segment=1;
|
||||||
|
while(segment<cumulative.size()-1&&distance>cumulative[segment])++segment;
|
||||||
|
const auto unitTangent=[&](std::size_t index)
|
||||||
|
{
|
||||||
|
const double tx=static_cast<double>(routePoints[index][0]
|
||||||
|
-routePoints[index-1][0]);
|
||||||
|
const double ty=static_cast<double>(routePoints[index][1]
|
||||||
|
-routePoints[index-1][1]);
|
||||||
|
const double length=std::hypot(tx,ty);
|
||||||
|
return length>0.0?std::array<double,2>{tx/length,ty/length}
|
||||||
|
:std::array<double,2>{0.0,0.0};
|
||||||
|
};
|
||||||
|
auto tangent=unitTangent(segment);
|
||||||
|
if(segment+1<cumulative.size()&&distance==cumulative[segment])
|
||||||
|
{
|
||||||
|
const auto next=unitTangent(segment+1);
|
||||||
|
tangent[0]+=next[0];tangent[1]+=next[1];
|
||||||
|
const double length=std::hypot(tangent[0],tangent[1]);
|
||||||
|
if(length>1.0e-12){tangent[0]/=length;tangent[1]/=length;}
|
||||||
|
}
|
||||||
|
constexpr double Q16=65536.0;
|
||||||
|
const double profileSlope=static_cast<double>(dz)/totalRouteLength;
|
||||||
|
return std::array<std::int32_t,2>{
|
||||||
|
static_cast<std::int32_t>(std::llround(tangent[0]*profileSlope*Q16)),
|
||||||
|
static_cast<std::int32_t>(std::llround(tangent[1]*profileSlope*Q16))};
|
||||||
|
};
|
||||||
const auto pointAt=[&](std::int64_t distance)
|
const auto pointAt=[&](std::int64_t distance)
|
||||||
{
|
{
|
||||||
distance=std::clamp<std::int64_t>(distance,0,totalRouteLength);
|
distance=std::clamp<std::int64_t>(distance,0,totalRouteLength);
|
||||||
@@ -2211,7 +2397,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
-routePoints[segment-1][axis];
|
-routePoints[segment-1][axis];
|
||||||
point[axis]=routePoints[segment-1][axis]+delta*local/segmentLength;
|
point[axis]=routePoints[segment-1][axis]+delta*local/segmentLength;
|
||||||
}
|
}
|
||||||
point[2]=routeStart[2]+dz*distance/totalRouteLength;
|
point[2]=routeFloorAt(distance);
|
||||||
return point;
|
return point;
|
||||||
};
|
};
|
||||||
std::int64_t spacing=std::min(std::max<std::int64_t>(settings.roomSpacingMicrometres,
|
std::int64_t spacing=std::min(std::max<std::int64_t>(settings.roomSpacingMicrometres,
|
||||||
@@ -2281,6 +2467,9 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
{
|
{
|
||||||
std::string id;
|
std::string id;
|
||||||
FPoint point{};
|
FPoint point{};
|
||||||
|
std::string floorProfileStableId;
|
||||||
|
std::int64_t floorProfileDistanceMicrometres=-1;
|
||||||
|
std::array<std::int32_t,2> floorGradientQ16{};
|
||||||
std::int64_t radiusMicrometres=0;
|
std::int64_t radiusMicrometres=0;
|
||||||
bool destination=false;
|
bool destination=false;
|
||||||
};
|
};
|
||||||
@@ -2298,6 +2487,9 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
{
|
{
|
||||||
const std::int64_t distance=nodeDistances[nodeIndex];
|
const std::int64_t distance=nodeDistances[nodeIndex];
|
||||||
FRoomNode node;node.point=pointAt(distance);
|
FRoomNode node;node.point=pointAt(distance);
|
||||||
|
node.floorProfileStableId=backboneFloorProfileId;
|
||||||
|
node.floorProfileDistanceMicrometres=distance;
|
||||||
|
node.floorGradientQ16=routeFloorGradientAt(distance);
|
||||||
node.destination=distance==mainRouteMidpoint;
|
node.destination=distance==mainRouteMidpoint;
|
||||||
node.id="tunnel-network/strate-"+std::to_string(strateIndex)+"/room-"
|
node.id="tunnel-network/strate-"+std::to_string(strateIndex)+"/room-"
|
||||||
+std::to_string(nodeIndex);
|
+std::to_string(nodeIndex);
|
||||||
@@ -2347,7 +2539,10 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
pathAxisX*pathAxisX+pathAxisY*pathAxisY))));
|
pathAxisX*pathAxisX+pathAxisY*pathAxisY))));
|
||||||
const std::int64_t centerOffset=(component%2==0?1:-1)*offsetMagnitude;
|
const std::int64_t centerOffset=(component%2==0?1:-1)*offsetMagnitude;
|
||||||
ResolvedCavernRoomV1 room;room.stableId=stableId;
|
ResolvedCavernRoomV1 room;room.stableId=stableId;
|
||||||
room.strateIndex=strateIndex;room.floorMicrometres=node.point[2];
|
room.strateIndex=strateIndex;
|
||||||
|
room.floorProfileStableId=node.floorProfileStableId;
|
||||||
|
room.floorProfileDistanceMicrometres=node.floorProfileDistanceMicrometres;
|
||||||
|
room.floorGradientQ16=node.floorGradientQ16;
|
||||||
room.halfExtentsMicrometres={halfX,halfY,halfZ};
|
room.halfExtentsMicrometres={halfX,halfY,halfZ};
|
||||||
room.centerMicrometres={node.point[0]+(-pathAxisY)*centerOffset/routeLengthXY,
|
room.centerMicrometres={node.point[0]+(-pathAxisY)*centerOffset/routeLengthXY,
|
||||||
node.point[1]+pathAxisX*centerOffset/routeLengthXY,
|
node.point[1]+pathAxisX*centerOffset/routeLengthXY,
|
||||||
@@ -2362,6 +2557,11 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
%static_cast<std::uint64_t>(jitterRange+1)):0;
|
%static_cast<std::uint64_t>(jitterRange+1)):0;
|
||||||
room.centerMicrometres[2]+=ceilingJitter;
|
room.centerMicrometres[2]+=ceilingJitter;
|
||||||
}
|
}
|
||||||
|
room.floorMicrometres=node.point[2]
|
||||||
|
-(static_cast<std::int64_t>(room.floorGradientQ16[0])
|
||||||
|
*(room.centerMicrometres[0]-node.point[0])
|
||||||
|
+static_cast<std::int64_t>(room.floorGradientQ16[1])
|
||||||
|
*(room.centerMicrometres[1]-node.point[1]))/65536;
|
||||||
room.smoothWidthMicrometres=smooth;
|
room.smoothWidthMicrometres=smooth;
|
||||||
room.irregularityAmplitudeMicrometres=irregularity;
|
room.irregularityAmplitudeMicrometres=irregularity;
|
||||||
room.irregularityWavelengthMicrometres=settings.irregularityWavelengthMicrometres;
|
room.irregularityWavelengthMicrometres=settings.irregularityWavelengthMicrometres;
|
||||||
@@ -2374,7 +2574,8 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
const auto componentRoomId=[](const FRoomNode& node)
|
const auto componentRoomId=[](const FRoomNode& node)
|
||||||
{return node.id+"/primitive-0";};
|
{return node.id+"/primitive-0";};
|
||||||
const auto makeEdge=[&](std::string id,std::string startAnchor,
|
const auto makeEdge=[&](std::string id,std::string startAnchor,
|
||||||
const FPoint& start,const FRoomNode& endNode,std::int64_t boreRadius)
|
const FPoint& start,const FRoomNode& endNode,std::int64_t boreRadius,
|
||||||
|
std::int64_t profileStartDistance,std::int64_t profileEndDistance)
|
||||||
{
|
{
|
||||||
ResolvedCavernEdgeV1 edge;edge.stableId=std::move(id);
|
ResolvedCavernEdgeV1 edge;edge.stableId=std::move(id);
|
||||||
edge.startAnchorStableId=std::move(startAnchor);
|
edge.startAnchorStableId=std::move(startAnchor);
|
||||||
@@ -2382,6 +2583,12 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
edge.boreRadiusMicrometres=boreRadius;
|
edge.boreRadiusMicrometres=boreRadius;
|
||||||
edge.boreHeightMicrometres=std::max(PlannedBoreHeight,MinimumBoreHeight);
|
edge.boreHeightMicrometres=std::max(PlannedBoreHeight,MinimumBoreHeight);
|
||||||
edge.floorSupportDepthMicrometres=settings.floorSupportDepthMicrometres;
|
edge.floorSupportDepthMicrometres=settings.floorSupportDepthMicrometres;
|
||||||
|
if(profileStartDistance>=0&&profileEndDistance>=0)
|
||||||
|
{
|
||||||
|
edge.floorProfileStableId=backboneFloorProfileId;
|
||||||
|
edge.floorProfileStartDistanceMicrometres=profileStartDistance;
|
||||||
|
edge.floorProfileEndDistanceMicrometres=profileEndDistance;
|
||||||
|
}
|
||||||
edge.smoothWidthMicrometres=smooth;
|
edge.smoothWidthMicrometres=smooth;
|
||||||
edge.irregularityAmplitudeMicrometres=irregularity;
|
edge.irregularityAmplitudeMicrometres=irregularity;
|
||||||
edge.irregularityWavelengthMicrometres=settings.irregularityWavelengthMicrometres;
|
edge.irregularityWavelengthMicrometres=settings.irregularityWavelengthMicrometres;
|
||||||
@@ -2394,10 +2601,21 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
edge.controlPointsMicrometres[2][axis]=start[axis]+(delta*2)/3;
|
edge.controlPointsMicrometres[2][axis]=start[axis]+(delta*2)/3;
|
||||||
edge.controlPointsMicrometres[3][axis]=end[axis];
|
edge.controlPointsMicrometres[3][axis]=end[axis];
|
||||||
}
|
}
|
||||||
|
if(profileStartDistance>=0&&profileEndDistance>=0)
|
||||||
|
{
|
||||||
|
const std::int64_t floorStart=routeFloorAt(profileStartDistance);
|
||||||
|
const std::int64_t floorEnd=routeFloorAt(profileEndDistance);
|
||||||
|
const std::int64_t floorDelta=floorEnd-floorStart;
|
||||||
|
edge.controlPointsMicrometres[0][2]=floorStart;
|
||||||
|
edge.controlPointsMicrometres[1][2]=floorStart+floorDelta/3;
|
||||||
|
edge.controlPointsMicrometres[2][2]=floorStart+(floorDelta*2)/3;
|
||||||
|
edge.controlPointsMicrometres[3][2]=floorEnd;
|
||||||
|
}
|
||||||
return edge;
|
return edge;
|
||||||
};
|
};
|
||||||
const auto arrivalPoint=routeStart;
|
const auto arrivalPoint=routeStart;
|
||||||
FPoint previousPoint=arrivalPoint;
|
FPoint previousPoint=arrivalPoint;
|
||||||
|
std::int64_t previousProfileDistance=0;
|
||||||
std::string previousAnchor=arrivalPassage->stableId+"/lower-mouth";
|
std::string previousAnchor=arrivalPassage->stableId+"/lower-mouth";
|
||||||
for(std::size_t nodeIndex=0;nodeIndex<nodes.size();++nodeIndex)
|
for(std::size_t nodeIndex=0;nodeIndex<nodes.size();++nodeIndex)
|
||||||
{
|
{
|
||||||
@@ -2406,8 +2624,10 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
const std::string edgeId="tunnel-network/strate-"+std::to_string(strateIndex)
|
const std::string edgeId="tunnel-network/strate-"+std::to_string(strateIndex)
|
||||||
+"/route-edge-"+std::to_string(nodeIndex);
|
+"/route-edge-"+std::to_string(nodeIndex);
|
||||||
edges.push_back(makeEdge(edgeId,previousAnchor,previousPoint,node,
|
edges.push_back(makeEdge(edgeId,previousAnchor,previousPoint,node,
|
||||||
portal?passageBoreRadius(*arrivalPassage,true):selectedBoreRadius(edgeId)));
|
portal?passageBoreRadius(*arrivalPassage,true):selectedBoreRadius(edgeId),
|
||||||
|
previousProfileDistance,node.floorProfileDistanceMicrometres));
|
||||||
previousPoint={node.point[0],node.point[1],node.point[2]};
|
previousPoint={node.point[0],node.point[1],node.point[2]};
|
||||||
|
previousProfileDistance=node.floorProfileDistanceMicrometres;
|
||||||
previousAnchor=componentRoomId(node);
|
previousAnchor=componentRoomId(node);
|
||||||
}
|
}
|
||||||
if(!nodes.empty())
|
if(!nodes.empty())
|
||||||
@@ -2415,7 +2635,8 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
const std::string departureEdgeId="tunnel-network/strate-"
|
const std::string departureEdgeId="tunnel-network/strate-"
|
||||||
+std::to_string(strateIndex)+"/departure-edge";
|
+std::to_string(strateIndex)+"/departure-edge";
|
||||||
edges.push_back(makeEdge(departureEdgeId,departurePassage->stableId+"/upper-mouth",
|
edges.push_back(makeEdge(departureEdgeId,departurePassage->stableId+"/upper-mouth",
|
||||||
routeEnd,nodes.back(),passageBoreRadius(*departurePassage,false)));
|
routeEnd,nodes.back(),passageBoreRadius(*departurePassage,false),
|
||||||
|
totalRouteLength,nodes.back().floorProfileDistanceMicrometres));
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::int64_t roomDensity=clampQ16(settings.roomDensityQ16);
|
const std::int64_t roomDensity=clampQ16(settings.roomDensityQ16);
|
||||||
@@ -2444,16 +2665,19 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
|
|||||||
branch.point={parent.point[0]+(-pathAxisY)*branchLength/pathAxisLength*direction,
|
branch.point={parent.point[0]+(-pathAxisY)*branchLength/pathAxisLength*direction,
|
||||||
parent.point[1]+pathAxisX*branchLength/pathAxisLength*direction,
|
parent.point[1]+pathAxisX*branchLength/pathAxisLength*direction,
|
||||||
parent.point[2]};
|
parent.point[2]};
|
||||||
|
branch.floorProfileStableId=parent.floorProfileStableId;
|
||||||
|
branch.floorProfileDistanceMicrometres=parent.floorProfileDistanceMicrometres;
|
||||||
|
branch.floorGradientQ16=parent.floorGradientQ16;
|
||||||
makeCompound(branch,false,rooms);
|
makeCompound(branch,false,rooms);
|
||||||
const std::string branchEdgeId="tunnel-network/strate-"
|
const std::string branchEdgeId="tunnel-network/strate-"
|
||||||
+std::to_string(strateIndex)+"/side-edge-"+std::to_string(branchCount);
|
+std::to_string(strateIndex)+"/side-edge-"+std::to_string(branchCount);
|
||||||
ResolvedCavernEdgeV1 branchEdge;
|
ResolvedCavernEdgeV1 branchEdge;
|
||||||
if(settings.tunnelsFlowTowardOrigin)
|
if(settings.tunnelsFlowTowardOrigin)
|
||||||
branchEdge=makeEdge(branchEdgeId,componentRoomId(branch),
|
branchEdge=makeEdge(branchEdgeId,componentRoomId(branch),
|
||||||
branch.point,parent,selectedBoreRadius(branchEdgeId));
|
branch.point,parent,selectedBoreRadius(branchEdgeId),-1,-1);
|
||||||
else
|
else
|
||||||
branchEdge=makeEdge(branchEdgeId,componentRoomId(parent),
|
branchEdge=makeEdge(branchEdgeId,componentRoomId(parent),
|
||||||
parent.point,branch,selectedBoreRadius(branchEdgeId));
|
parent.point,branch,selectedBoreRadius(branchEdgeId),-1,-1);
|
||||||
// Side branches retain their cave floor SDF, but they do not cap the
|
// Side branches retain their cave floor SDF, but they do not cap the
|
||||||
// descending backbone when their tubes overlap it near a junction.
|
// descending backbone when their tubes overlap it near a junction.
|
||||||
branchEdge.floorSupportDepthMicrometres=0;
|
branchEdge.floorSupportDepthMicrometres=0;
|
||||||
|
|||||||
@@ -3583,6 +3583,7 @@ private:
|
|||||||
double minimumFloorMetres=std::numeric_limits<double>::infinity();
|
double minimumFloorMetres=std::numeric_limits<double>::infinity();
|
||||||
double maximumFloorMetres=-std::numeric_limits<double>::infinity();
|
double maximumFloorMetres=-std::numeric_limits<double>::infinity();
|
||||||
double maximumSegmentGrade=0.0;
|
double maximumSegmentGrade=0.0;
|
||||||
|
std::array<double,2> floorGradient{};
|
||||||
std::array<float,3> centerVoxels{},halfExtentsVoxels{};
|
std::array<float,3> centerVoxels{},halfExtentsVoxels{};
|
||||||
std::array<std::array<float,3>,4> controlPointsVoxels{};
|
std::array<std::array<float,3>,4> controlPointsVoxels{};
|
||||||
float floorVoxels=0.0f;
|
float floorVoxels=0.0f;
|
||||||
@@ -4011,8 +4012,10 @@ private:
|
|||||||
{
|
{
|
||||||
CavernRuntime.clear();CavernSpatialNodes.clear();CavernSpatialOrder.clear();
|
CavernRuntime.clear();CavernSpatialNodes.clear();CavernSpatialOrder.clear();
|
||||||
if(!ResolvedLayout||!Owner||Owner->CoreVoxelSizeMicrons==0
|
if(!ResolvedLayout||!Owner||Owner->CoreVoxelSizeMicrons==0
|
||||||
||ResolvedLayout->cavernGraphVocabularyVersion
|
||(ResolvedLayout->cavernGraphVocabularyVersion
|
||||||
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV1)return;
|
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV1
|
||||||
|
&&ResolvedLayout->cavernGraphVocabularyVersion
|
||||||
|
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV2))return;
|
||||||
const auto isNativeGraphStrate=[this](std::int32_t strateIndex)
|
const auto isNativeGraphStrate=[this](std::int32_t strateIndex)
|
||||||
{
|
{
|
||||||
const auto found=std::find_if(ResolvedLayout->strates.begin(),
|
const auto found=std::find_if(ResolvedLayout->strates.begin(),
|
||||||
@@ -4042,6 +4045,8 @@ private:
|
|||||||
}
|
}
|
||||||
runtime.floorVoxels=static_cast<float>(
|
runtime.floorVoxels=static_cast<float>(
|
||||||
static_cast<double>(room.floorMicrometres)/micronsPerVoxel);
|
static_cast<double>(room.floorMicrometres)/micronsPerVoxel);
|
||||||
|
runtime.floorGradient={room.floorGradientQ16[0]/65536.0,
|
||||||
|
room.floorGradientQ16[1]/65536.0};
|
||||||
runtime.smoothWidthVoxels=static_cast<float>(
|
runtime.smoothWidthVoxels=static_cast<float>(
|
||||||
static_cast<double>(room.smoothWidthMicrometres)/micronsPerVoxel);
|
static_cast<double>(room.smoothWidthMicrometres)/micronsPerVoxel);
|
||||||
runtime.irregularityAmplitudeVoxels=static_cast<float>(
|
runtime.irregularityAmplitudeVoxels=static_cast<float>(
|
||||||
@@ -5314,7 +5319,18 @@ public:
|
|||||||
(std::sqrt(normalizedMinimumSquared)-1.0)*meanRadius,
|
(std::sqrt(normalizedMinimumSquared)-1.0)*meanRadius,
|
||||||
(std::sqrt(normalizedMaximumSquared)-1.0)*meanRadius,true};
|
(std::sqrt(normalizedMaximumSquared)-1.0)*meanRadius,true};
|
||||||
const double floorZ=feature.room->floorMicrometres/1000000.0;
|
const double floorZ=feature.room->floorMicrometres/1000000.0;
|
||||||
const FMetricInterval floor={floorZ-hi[2],floorZ-lo[2],true};
|
const double centerX=feature.room->centerMicrometres[0]/1000000.0;
|
||||||
|
const double centerY=feature.room->centerMicrometres[1]/1000000.0;
|
||||||
|
const double gx=feature.room->floorGradientQ16[0]/65536.0;
|
||||||
|
const double gy=feature.room->floorGradientQ16[1]/65536.0;
|
||||||
|
const double x0=lo[0]-centerX,x1=hi[0]-centerX;
|
||||||
|
const double y0=lo[1]-centerY,y1=hi[1]-centerY;
|
||||||
|
const double gradientMinimum=(gx>=0.0?gx*x0:gx*x1)
|
||||||
|
+(gy>=0.0?gy*y0:gy*y1);
|
||||||
|
const double gradientMaximum=(gx>=0.0?gx*x1:gx*x0)
|
||||||
|
+(gy>=0.0?gy*y1:gy*y0);
|
||||||
|
const FMetricInterval floor={floorZ-hi[2]+gradientMinimum,
|
||||||
|
floorZ-lo[2]+gradientMaximum,true};
|
||||||
const double width=feature.room->smoothWidthMicrometres/1000000.0;
|
const double width=feature.room->smoothWidthMicrometres/1000000.0;
|
||||||
featureVoid={SmoothMaxMetres(ellipsoid.minimum,floor.minimum,width),
|
featureVoid={SmoothMaxMetres(ellipsoid.minimum,floor.minimum,width),
|
||||||
SmoothMaxMetres(ellipsoid.maximum,floor.maximum,width),true};
|
SmoothMaxMetres(ellipsoid.maximum,floor.maximum,width),true};
|
||||||
@@ -7463,6 +7479,7 @@ private:
|
|||||||
double floorSupportScore=std::numeric_limits<double>::infinity();
|
double floorSupportScore=std::numeric_limits<double>::infinity();
|
||||||
double floorSupportDepth=0.0,floorSupportFloor=0.0,floorSupportGrade=0.0;
|
double floorSupportDepth=0.0,floorSupportFloor=0.0,floorSupportGrade=0.0;
|
||||||
const std::string* floorSupportEdgeId=nullptr;
|
const std::string* floorSupportEdgeId=nullptr;
|
||||||
|
const std::string* floorSupportProfileId=nullptr;
|
||||||
for(const std::uint32_t index:*candidates)
|
for(const std::uint32_t index:*candidates)
|
||||||
{
|
{
|
||||||
if(index>=CavernRuntime.size())continue;
|
if(index>=CavernRuntime.size())continue;
|
||||||
@@ -7491,7 +7508,9 @@ private:
|
|||||||
feature.irregularityWavelengthVoxels,feature.irregularitySeed)
|
feature.irregularityWavelengthVoxels,feature.irregularitySeed)
|
||||||
*feature.irregularityAmplitudeVoxels*metresPerVoxel*fade;
|
*feature.irregularityAmplitudeVoxels*metresPerVoxel*fade;
|
||||||
}
|
}
|
||||||
const double floor=feature.room->floorMicrometres/1000000.0-point[2];
|
const double floor=feature.room->floorMicrometres/1000000.0-point[2]
|
||||||
|
+feature.floorGradient[0]*(point[0]-feature.centerMetres[0])
|
||||||
|
+feature.floorGradient[1]*(point[1]-feature.centerMetres[1]);
|
||||||
const double width=feature.room->smoothWidthMicrometres/1000000.0;
|
const double width=feature.room->smoothWidthMicrometres/1000000.0;
|
||||||
featureVoid=SmoothMaxMetres(ellipsoid,floor,width);
|
featureVoid=SmoothMaxMetres(ellipsoid,floor,width);
|
||||||
}
|
}
|
||||||
@@ -7599,10 +7618,32 @@ private:
|
|||||||
{
|
{
|
||||||
const double deltaZ=point[2]-nearestFloor;
|
const double deltaZ=point[2]-nearestFloor;
|
||||||
const double candidateScore=nearestRadial*nearestRadial+deltaZ*deltaZ;
|
const double candidateScore=nearestRadial*nearestRadial+deltaZ*deltaZ;
|
||||||
if(candidateScore<floorSupportScore
|
if(!feature.edge->floorProfileStableId.empty())
|
||||||
|
{
|
||||||
|
const bool sameProfile=floorSupportProfileId
|
||||||
|
&&*floorSupportProfileId==feature.edge->floorProfileStableId;
|
||||||
|
if(!floorSupportProfileId||sameProfile)
|
||||||
|
{
|
||||||
|
// Backbone edges share h(s). At a geometric joint their
|
||||||
|
// cave volumes overlap, so the backing top is the lower
|
||||||
|
// of the local floors, never the closer higher segment.
|
||||||
|
if(!floorSupportProfileId||nearestFloor<floorSupportFloor
|
||||||
|
||(nearestFloor==floorSupportFloor
|
||||||
|
&&feature.edge->stableId<*floorSupportEdgeId))
|
||||||
|
{
|
||||||
|
floorSupportDepth=candidateSupportDepth;
|
||||||
|
floorSupportFloor=nearestFloor;
|
||||||
|
floorSupportGrade=nearestGrade;
|
||||||
|
floorSupportEdgeId=&feature.edge->stableId;
|
||||||
|
}
|
||||||
|
floorSupportProfileId=&feature.edge->floorProfileStableId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(!floorSupportProfileId
|
||||||
|
&&(candidateScore<floorSupportScore
|
||||||
||(candidateScore==floorSupportScore
|
||(candidateScore==floorSupportScore
|
||||||
&&(!floorSupportEdgeId
|
&&(!floorSupportEdgeId
|
||||||
||feature.edge->stableId<*floorSupportEdgeId)))
|
||feature.edge->stableId<*floorSupportEdgeId))))
|
||||||
{
|
{
|
||||||
floorSupportScore=candidateScore;
|
floorSupportScore=candidateScore;
|
||||||
floorSupportDepth=candidateSupportDepth;
|
floorSupportDepth=candidateSupportDepth;
|
||||||
|
|||||||
Reference in new issue
Block a user