Tooling
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
package plates
|
||||
|
||||
import "math"
|
||||
|
||||
// Belt is how faulted a margin's surroundings are: the deformation zone around a boundary, and how densely
|
||||
// it is broken.
|
||||
//
|
||||
// It lives beside the plates rather than beside the faults because it describes a *boundary*, not a fault.
|
||||
// How wide the ground is that a margin deforms is a property of what that margin is doing - a continental
|
||||
// collision takes up its convergence across a belt a thousand kilometres wide and a mid-ocean ridge across an
|
||||
// axis a few tens wide - and the traces are a consequence of that width, not the other way round. What
|
||||
// internal/uplift's belt_faults.go does with these numbers, and the fault map they were read off, is
|
||||
// documented there.
|
||||
type Belt struct {
|
||||
// ZoneKm is the deformation half-width of a *collision* margin closing at ReferenceCmYr, in kilometres.
|
||||
// Every other kind of margin is a fraction of it, and every margin scales with its own rate.
|
||||
ZoneKm float64 `json:"zone_km"`
|
||||
|
||||
// ReferenceCmYr is the rate ZoneKm is quoted at. Earth's big collisions run 2 to 5 cm/yr.
|
||||
ReferenceCmYr float64 `json:"reference_cm_yr"`
|
||||
|
||||
// Per1000Km2 is the trace density over the *zone*, not over the planet: a belt is as faulted as a belt is
|
||||
// wherever it happens to run, and the ground away from one is not lightly faulted, it is unfaulted.
|
||||
Per1000Km2 float64 `json:"per_1000km2"`
|
||||
|
||||
// ThrowM is the total displacement over the whole run, low to high, before the closing rate scales it -
|
||||
// the height of the scarp the fault would build if nothing eroded it.
|
||||
ThrowM [2]float64 `json:"throw_m"`
|
||||
|
||||
// LengthKm is how long a trace is, low to high, before the local zone width scales it. A wide belt
|
||||
// carries long faults and a narrow one cannot.
|
||||
LengthKm [2]float64 `json:"length_km"`
|
||||
|
||||
// StrikeSpreadDeg is how far a trace may wander off the belt's local tangent. Small on purpose: a swarm
|
||||
// being sub-parallel is the thing that makes it read as a swarm.
|
||||
//
|
||||
// ConjugateFraction is the share of traces drawn on the second, crossing direction, and ConjugateDeg is
|
||||
// the angle between the two sets. One direction alone reads as corduroy.
|
||||
//
|
||||
// The first two are pointers for the reason Config.SpinFraction is: JSON cannot tell an absent number
|
||||
// from a zero one, and both of these have a real meaning at zero - perfectly parallel traces, and no
|
||||
// second set. Read as the same thing, a block that simply did not mention them silently turned them off,
|
||||
// which is how the first painted planet came out with no conjugate set at all. Absent takes the default;
|
||||
// an explicit 0 means none.
|
||||
StrikeSpreadDeg *float64 `json:"strike_spread_deg"`
|
||||
ConjugateFraction *float64 `json:"conjugate_fraction"`
|
||||
ConjugateDeg float64 `json:"conjugate_deg"`
|
||||
}
|
||||
|
||||
// DefaultStrikeSpreadDeg and DefaultConjugateFraction are what a belt that does not mention them gets.
|
||||
const (
|
||||
DefaultStrikeSpreadDeg = 11.0
|
||||
DefaultConjugateFraction = 0.22
|
||||
)
|
||||
|
||||
// Spread is the configured strike spread, or the default when the block said nothing.
|
||||
func (b Belt) Spread() float64 {
|
||||
if b.StrikeSpreadDeg == nil {
|
||||
return DefaultStrikeSpreadDeg
|
||||
}
|
||||
return math.Max(0, *b.StrikeSpreadDeg)
|
||||
}
|
||||
|
||||
// Conjugate is the configured share of crossing traces, or the default when the block said nothing.
|
||||
func (b Belt) Conjugate() float64 {
|
||||
if b.ConjugateFraction == nil {
|
||||
return DefaultConjugateFraction
|
||||
}
|
||||
return math.Min(1, math.Max(0, *b.ConjugateFraction))
|
||||
}
|
||||
|
||||
// DefaultBelt is what a planet that asks for belt faults but says nothing else gets.
|
||||
func DefaultBelt() Belt {
|
||||
return Belt{
|
||||
ZoneKm: 6,
|
||||
ReferenceCmYr: 4,
|
||||
Per1000Km2: 90,
|
||||
ThrowM: [2]float64{80, 420},
|
||||
LengthKm: [2]float64{4, 16},
|
||||
ConjugateDeg: 32,
|
||||
// StrikeSpreadDeg and ConjugateFraction stay nil: their defaults live in Spread and Conjugate, so
|
||||
// that an explicit zero can mean none.
|
||||
}
|
||||
}
|
||||
|
||||
// Wanted reports whether this asks for anything. A zero Belt is a planet whose margins are not faulted, which
|
||||
// is what every painted planet had before this existed.
|
||||
func (b Belt) Wanted() bool {
|
||||
return b.ZoneKm > 0 && b.Per1000Km2 > 0 && b.LengthKm[1] > 0 && b.ThrowM[1] > 0
|
||||
}
|
||||
|
||||
// WithDefaults fills in the fields that have a sensible value when left out. ZoneKm, Per1000Km2, ThrowM and
|
||||
// LengthKm are deliberately not among them: those four are the feature, and defaulting them would turn
|
||||
// leaving the block out into switching the feature on.
|
||||
func (b Belt) WithDefaults() Belt {
|
||||
d := DefaultBelt()
|
||||
if b.ReferenceCmYr <= 0 {
|
||||
b.ReferenceCmYr = d.ReferenceCmYr
|
||||
}
|
||||
if b.ConjugateDeg <= 0 {
|
||||
b.ConjugateDeg = d.ConjugateDeg
|
||||
}
|
||||
return b
|
||||
}
|
||||
Reference in New Issue
Block a user