Rules and patterns for writing tests of Avalonia UI visual elements (controls, dialogs, panels). Use when creating or modifying tests for XAML controls, dialogs, or panels.
日本語の概要は準備中です。原文の説明を表示しています。
Use whenever writing, reviewing, or debugging .NET/C# code that uses the BallisticCalculator NuGet package (namespace BallisticCalculator) or its Gehtsoft.Measurements unit types — even when the user doesn't name the package explicitly but is clearly doing external/interior ballistics in .NET (bullet drop, velocity, energy, windage, spin drift, time of flight, zeroing a scope) for rifles, air rifles, bows, or artillery. Covers the full public API (Ammunition, Rifle, Atmosphere, Wind, ShotParameters, TrajectoryCalculator, TrajectoryPoint); standard, custom, .drg, and multi-BC drag tables; saving/loading data and building your own file format around it (BXml and System.Text.Json serialization); and building or rendering scope reticles (e.g. to SVG). Also use it for this library's Gehtsoft.Measurements Measurement<T> unit types (DistanceUnit, VelocityUnit, AngularUnit, WeightUnit, PressureUnit, …). Skip for: generic physics or projectile-motion exercises, a different ballistics library (e.g. GNU/JBM), game-engine or Unity projectile motion, plain unit conversions, and non-code ballistics questions (load-data or forensics advice). Self-contained: no access to the library source or binaries is required — prefer this over scanning the package to rediscover the API. Describes 1.1.13+, including the named ZeroRangeCantBeReachedException / TrajectoryCannotBeCalculatedException failures.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
A .NET library (netstandard2.0) that models the trajectory of a projectile through the
atmosphere using a 3DOF (point-mass) model.
BallisticCalculator (namespace BallisticCalculator). This describes 1.1.13+; where
behaviour changed, the version that changed it is named.Gehtsoft.Measurements (namespace Gehtsoft.Measurements) — every physical
quantity is a strongly-typed Measurement<TUnit>.using BallisticCalculator;
using Gehtsoft.Measurements;
The workflow is always: (1) describe the ammunition, rifle, and atmosphere → (2) compute the
zero (sight) correction with CalculateZeroParameters and Apply it to the ShotParameters →
(3) call Calculate → (4) read the returned TrajectoryPoint[].
Every dimensional value is a Measurement<TUnit> struct — a double value plus a unit. You pick
the unit at construction and again when you read it; metric and US units interoperate freely.
var d = new Measurement<DistanceUnit>(100, DistanceUnit.Yard); // value + unit
var d2 = new Measurement<DistanceUnit>("100yd"); // parse "<number><unit>"
double meters = d.In(DistanceUnit.Meter); // convert & read as double
double raw = d.Value; // value in its own unit (100)
DistanceUnit u = d.Unit; // DistanceUnit.Yard
var zero = Measurement<DistanceUnit>.ZERO; // 0
double mm = Measurement<DistanceUnit>.Convert(1, DistanceUnit.Inch, DistanceUnit.Meter); // static double->double
var d3 = 100.As(DistanceUnit.Yard); // extension sugar: (double|int).As(unit)
var d4 = DistanceUnit.Yard.New(100); // unit-first sugar: unit.New(value)
Operators: + - (same unit), * / by a scalar, and comparisons. Nullable Measurement<T>?
marks optional inputs. The three construction forms above (new, .As, .New) are equivalent —
use whichever reads best.
Unit enums (use the exact member names):
| Enum | Members |
|---|---|
DistanceUnit | Millimeter, Centimeter, Meter, Kilometer, Inch, Foot, Yard, Mile, NauticalMile, Line, RussianLine, Point, Pica |
VelocityUnit | MetersPerSecond, KilometersPerHour, FeetPerSecond, MilesPerHour, Knot |
AngularUnit | Radian, Degree, MOA, Mil, MRad, Thousand, InchesPer100Yards, CmPer100Meters, Percent, Turn, Gradian |
WeightUnit | Grain, Ounce, Gram, Pound, Kilogram, Neuton, Dram, TroyOz, Tonne, USTonne, UKTonne |
PressureUnit | Pascal, KiloPascal, Bar, Millibar, Atmosphere, TechincalAtmosphere, MillimetersOfMercury, InchesOfMercury, PoundsPerSquareInch, MillimetersOfWater |
TemperatureUnit | Fahrenheit, Celsius, Kelvin, Rankin, Reaumur, Delisle |
EnergyUnit | FootPound, Joule, BTU, HpH, Wh |
DensityUnit | GramPerCubicCentimeter, KilogramPerCubicMeter, PoundsPerCubicInch, OuncesPerCubicFeet, PoundsPerCubicFoot |
(TechincalAtmosphere is spelled that way in the library.)
public Ammunition(
Measurement<WeightUnit> weight,
BallisticCoefficient ballisticCoefficient,
Measurement<VelocityUnit> muzzleVelocity,
Measurement<DistanceUnit>? bulletDiameter = null, // required only for spin drift
Measurement<DistanceUnit>? bulletLength = null) // required only for spin drift
Properties: Weight, BallisticCoefficient, MuzzleVelocity, BulletDiameter?, BulletLength?,
CustomTableFileName. double GetBallisticCoefficient() returns the effective BC.
new BallisticCoefficient(0.325, DragTableId.G7) // a coefficient
new BallisticCoefficient(1.0, DragTableId.GC, BallisticCoefficientValueType.FormFactor)
new BallisticCoefficient("0.325G7") // parse text form
DragTableId: G1, G2, G5, G6, G7, G8, GI, GS, RA4 (standard curves) and GC (custom — see §6).BallisticCoefficientValueType: Coefficient (a BC number) or FormFactor."0.325G7", or "F1GC" (leading F = form factor; last two chars = table id).new Sight(Measurement<DistanceUnit> sightHeight,
Measurement<AngularUnit> verticalClick,
Measurement<AngularUnit> horizontalClick) // clicks may be Measurement<AngularUnit>.ZERO
new Rifling(Measurement<DistanceUnit> riflingStep, TwistDirection direction) // step = distance per turn; TwistDirection.Left|Right
new ZeroingParameters(Measurement<DistanceUnit> distance, Ammunition ammunition, Atmosphere atmosphere)
// ammunition/atmosphere may be null; when set they override the shot's ammo/atmo FOR ZEROING ONLY.
// Optional property: VerticalOffset (Measurement<DistanceUnit>?, + is up) shifts the zero impact point.
new Rifle(Sight sight, ZeroingParameters zero, Rifling rifling = null) // rifling optional
new Atmosphere() // sea level standard
new Atmosphere(Measurement<DistanceUnit> altitude,
Measurement<PressureUnit> pressure, // STATION pressure at that altitude
Measurement<TemperatureUnit> temperature,
double humidity) // humidity is a FRACTION 0..1, not %
new Atmosphere(Measurement<DistanceUnit> altitude,
Measurement<PressureUnit> pressure,
bool pressureAtSeaLevel, // true => pressure is sea-level-corrected
Measurement<TemperatureUnit> temperature,
double humidity)
Atmosphere.CreateICAOAtmosphere(Measurement<DistanceUnit> altitude, double humidity = 0)
Read-only properties: Altitude, Pressure, Temperature, Humidity, SoundVelocity, Density,
DensityAltitude (1.1.12 — the standard-atmosphere altitude matching this air's density, humidity included).
Static: Atmosphere.StandardDensity.
Density is computed from the resolved station Pressure, so it is consistent with what the engine uses (this
was wrong before 1.1.12 — up to 21% high at 5000 ft with pressureAtSeaLevel: true). DensityAltitude is
baselined on the ICAO sea-level density, not Atmosphere.StandardDensity — they differ by ~0.005% (~1.9 ft)
and are not interchangeable.
new Wind(Measurement<VelocityUnit> velocity,
Measurement<AngularUnit> direction,
Measurement<DistanceUnit>? maximumRange = null)
Calculate takes a Wind[]. Each wind applies out to its MaximumRange; the last one (or one
with a null range) applies to the end. Sort the array by ascending MaximumRange.var shot = new ShotParameters
{
Step = new Measurement<DistanceUnit>(100, DistanceUnit.Yard), // row spacing
MaximumDistance = new Measurement<DistanceUnit>(1000, DistanceUnit.Yard), // extent
ShotAngle = null, // Measurement<AngularUnit>? line-of-sight incline, + up / - down
CantAngle = null, // Measurement<AngularUnit>?
BarrelAzimuth = null, // Measurement<AngularUnit>?
Latitude = null, // Measurement<AngularUnit>? shooter latitude (Coriolis); optional
};
// The zero (sight) correction is REQUIRED but not set by hand — compute it and copy it in
// (see §3). Apply fills ZeroDropAdjustment / ZeroWindageAdjustment on the shot:
shot.Apply(calc.CalculateZeroParameters(ammo, atmosphere, rifle, rifle.Zero));
Apply sets the ZeroDropAdjustment / ZeroWindageAdjustment properties; the incline-adjusted
ShotDropAdjustment / ShotWindageAdjustment are also exposed. There is no longer a SightAngle
property.
public class TrajectoryCalculator
{
// Compute the zero (sight) correction for the rifle's zero distance. `zero` is usually
// rifle.Zero. Pass dragTable only when the BC uses table GC. NOTE the argument order:
// atmosphere comes BEFORE rifle here (the opposite of Calculate).
ZeroCalculatedParameters CalculateZeroParameters(
Ammunition ammunition, Atmosphere atmosphere, Rifle rifle, ZeroingParameters zero,
ShotParameters shot = null, Wind[] wind = null, DragTable dragTable = null,
Measurement<DistanceUnit>? accuracy = null); // accuracy default 0.1 mm
TrajectoryPoint[] Calculate(Ammunition ammunition, Rifle rifle, Atmosphere atmosphere,
ShotParameters shot, Wind[] wind = null, DragTable dragTable = null);
Measurement<DistanceUnit> MaximumCalculationStepSize { get; set; } // default 10 cm
IntegrationMethod Integrator { get; set; } // Euler | MidpointRK2 (default)
static Measurement<DistanceUnit> MaximumDrop { get; } // 10000 ft — stop condition
static Measurement<VelocityUnit> MinimumVelocity { get; } // 50 ft/s — stop condition
}
// Returned by CalculateZeroParameters; copy it into the shot with ShotParameters.Apply.
public class ZeroCalculatedParameters
{
Measurement<AngularUnit> ZeroDropAdjustment { get; } // vertical sight correction
Measurement<AngularUnit>? ZeroWindageAdjustment { get; } // horizontal, when computed
}
Compute the zero correction first, Apply it to the ShotParameters, then call Calculate:
var zero = calc.CalculateZeroParameters(ammo, atmosphere, rifle, rifle.Zero);
shot.Apply(zero); // sets shot.ZeroDropAdjustment (+ windage) — REQUIRED before Calculate
var trajectory = calc.Calculate(ammo, rifle, atmosphere, shot);
ShotAngle, if set, is added to the barrel elevation inside Calculate.
The result array can be shorter than MaximumDistance/Step + 1: the run stops early if velocity
drops below 50 ft/s or drop exceeds 10000 ft. Iterate the returned array; don't assume its length.
Both calls throw rather than returning nonsense, and the two conditions a user can fix have their own exception types:
// namespace BallisticCalculator; both : InvalidOperationException
public class ZeroRangeCantBeReachedException : InvalidOperationException { } // CalculateZeroParameters
public class TrajectoryCannotBeCalculatedException : InvalidOperationException { } // Calculate
| Type | Raised when | What the user changes |
|---|---|---|
ZeroRangeCantBeReachedException | the load cannot carry to the zero distance — a subsonic pistol load asked for a 1,000 yd zero | zero closer, more muzzle velocity, better BC |
TrajectoryCannotBeCalculatedException | the numbers leave nothing to integrate — a zero or absurd BC, weight or muzzle velocity | fix the offending ammunition field |
Two rules follow:
InvalidOperationException, so a
catch (InvalidOperationException) earlier in the chain swallows them.ArgumentException for a form factor with no
bullet diameter (or a non-positive form factor/weight), and ArgumentNullException when the BC table is
GC and no dragTable was supplied.Where a field can be checked up front — a non-positive BC, weight or muzzle velocity — validate instead of
catching. TrajectoryCannotBeCalculatedException is the net under that, not a substitute for it.
TrajectoryPoint, read-only)| Property | Type | Meaning |
|---|---|---|
Time | TimeSpan | Time of flight to this point. |
Distance | Measurement<DistanceUnit> | Distance along the line of sight. |
DistanceFlat | Measurement<DistanceUnit> | Horizontal distance from the muzzle. |
Velocity | Measurement<VelocityUnit> | Projectile speed. |
Mach | double | Speed relative to the local speed of sound. |
Drop | Measurement<DistanceUnit> | Vertical position vs the line of sight. At the muzzle = −sight height. |
DropFlat | Measurement<DistanceUnit> | Vertical position vs the muzzle (bore line). |
Windage | Measurement<DistanceUnit> | Horizontal deflection. Left +, right −. Includes spin drift when modelled. |
Energy | Measurement<EnergyUnit> | Kinetic energy. |
DropAdjustment | Measurement<AngularUnit> | Angular scope correction for the drop at this distance. |
WindageAdjustment | Measurement<AngularUnit> | Angular correction for the windage at this distance. |
LineOfSightElevation | Measurement<DistanceUnit> | Height of the sight line at this distance. |
LineOfDepartureElevation | Measurement<DistanceUnit> | Height of the bore line at this distance. |
OptimalGameWeight | Measurement<WeightUnit> | Litz optimal game weight estimate. |
foreach (var p in trajectory)
Console.WriteLine($"{p.Distance.In(DistanceUnit.Yard):N0} yd " +
$"{p.Velocity.In(VelocityUnit.FeetPerSecond):N0} fps " +
$"drop {p.Drop.In(DistanceUnit.Inch):N1} in " +
$"wind {p.Windage.In(DistanceUnit.Inch):N1} in");
var ammo = new Ammunition(
weight: new Measurement<WeightUnit>(168, WeightUnit.Grain),
ballisticCoefficient: new BallisticCoefficient(0.223, DragTableId.G7),
muzzleVelocity: new Measurement<VelocityUnit>(2700, VelocityUnit.FeetPerSecond));
var rifle = new Rifle(
sight: new Sight(new Measurement<DistanceUnit>(1.5, DistanceUnit.Inch),
Measurement<AngularUnit>.ZERO, Measurement<AngularUnit>.ZERO),
zero: new ZeroingParameters(new Measurement<DistanceUnit>(100, DistanceUnit.Yard), null, null));
var atmosphere = new Atmosphere();
var calc = new TrajectoryCalculator();
var shot = new ShotParameters
{
MaximumDistance = new Measurement<DistanceUnit>(1000, DistanceUnit.Yard),
Step = new Measurement<DistanceUnit>(100, DistanceUnit.Yard),
};
shot.Apply(calc.CalculateZeroParameters(ammo, atmosphere, rifle, rifle.Zero));
TrajectoryPoint[] trajectory = calc.Calculate(ammo, rifle, atmosphere, shot);
Spin drift is modelled only when the rifle has Rifling and the ammunition has both a bullet
diameter and length; it is folded into Windage (there is no separate output).
var ammo = new Ammunition(
weight: new Measurement<WeightUnit>(168, WeightUnit.Grain),
ballisticCoefficient: new BallisticCoefficient(0.223, DragTableId.G7),
muzzleVelocity: new Measurement<VelocityUnit>(2700, VelocityUnit.FeetPerSecond),
bulletDiameter: new Measurement<DistanceUnit>(0.308, DistanceUnit.Inch),
bulletLength: new Measurement<DistanceUnit>(1.2, DistanceUnit.Inch));
var rifle = new Rifle(
sight: new Sight(new Measurement<DistanceUnit>(2.0, DistanceUnit.Inch),
Measurement<AngularUnit>.ZERO, Measurement<AngularUnit>.ZERO),
zero: new ZeroingParameters(new Measurement<DistanceUnit>(100, DistanceUnit.Yard), null, null),
rifling: new Rifling(new Measurement<DistanceUnit>(11.25, DistanceUnit.Inch), TwistDirection.Right));
var atmosphere = new Atmosphere(
altitude: new Measurement<DistanceUnit>(5000, DistanceUnit.Foot),
pressure: new Measurement<PressureUnit>(24.9, PressureUnit.InchesOfMercury),
temperature: new Measurement<TemperatureUnit>(40, TemperatureUnit.Fahrenheit),
humidity: 0.30);
var wind = new[]
{
new Wind(new Measurement<VelocityUnit>(10, VelocityUnit.MilesPerHour),
new Measurement<AngularUnit>(90, AngularUnit.Degree),
new Measurement<DistanceUnit>(500, DistanceUnit.Yard)), // out to 500 yd
new Wind(new Measurement<VelocityUnit>(5, VelocityUnit.MilesPerHour),
new Measurement<AngularUnit>(45, AngularUnit.Degree)), // beyond 500 yd
};
var calc = new TrajectoryCalculator();
var shot = new ShotParameters
{
MaximumDistance = new Measurement<DistanceUnit>(1000, DistanceUnit.Yard),
Step = new Measurement<DistanceUnit>(100, DistanceUnit.Yard),
};
shot.Apply(calc.CalculateZeroParameters(ammo, atmosphere, rifle, rifle.Zero));
var trajectory = calc.Calculate(ammo, rifle, atmosphere, shot, wind);
Standard G1..RA4 curves are applied automatically when the BC names a standard table — nothing
extra to do. If, and only if, the task involves a non-standard drag curve — a custom/measured
drag table, a radar .drg file, or a multi-BC (BC-vs-Mach) profile — read
references/custom-drag.md and follow it. That file has the full,
copy-pasteable recipes for all three (DragTable subclass, DrgDragTable.Open, and
DrgDragTableFactory.Build with BcAtMach), including the exact signatures. Do not reconstruct
this API from memory or hand-roll a drag table — the helpers exist and the reference has them.
All three techniques use table id GC and require passing the DragTable instance to both
CalculateZeroParameters and Calculate (DragTable.Get(GC) throws — you supply the instance).
Atmosphere constructor is the station pressure at that altitude;
use the 5-argument overload with pressureAtSeaLevel: true to pass a sea-level value.Calculate: compute it with CalculateZeroParameters
and copy it into the shot via ShotParameters.Apply(...). There is no SightAngle property anymore.CalculateZeroParameters(ammunition, atmosphere, rifle, zero) puts atmosphere before rifle, whereas Calculate(ammunition, rifle, atmosphere, shot)
puts rifle before atmosphere. Easy to transpose.Drop at the muzzle = −sight height.Rifling and BulletDiameter and BulletLength; otherwise windage is
wind-only. It is folded into Windage (no separate value). Right twist drifts right, left drifts left.GC (custom) drag requires passing the DragTable to both CalculateZeroParameters and
Calculate; DragTable.Get(DragTableId.GC) throws — you must supply the instance.ZeroRangeCantBeReachedException / TrajectoryCannotBeCalculatedException both derive from
InvalidOperationException — catch them before any broader handler, and treat them as input errors the
user can fix rather than crashes. See §3.AngularUnit.Mil is the military mil (1/6400), not the milliradian (MRad, 1/6283.19) — ~1.9 % apart.
A mil-dot reticle is a milliradian instrument, and MilDotReticle is built in MRad.Rifling and the bullet
diameter and length; without them the term is absent. Right twist + wind from the right lifts the impact.These live in references/ and load only when the task needs them — read the matching file and follow
it rather than reconstructing the API from memory:
DragTable, radar .drg, multi-BC synthesis) →
references/custom-drag.md (see §6).Ammunition/Rifle/Atmosphere/libraries via BXml
or System.Text.Json, embedding library objects in your own file format, and decorating your own
classes for the BXml serializer → references/serialization.md.references/reticle.md.まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Rules and patterns for writing tests of Avalonia UI visual elements (controls, dialogs, panels). Use when creating or modifying tests for XAML controls, dialogs, or panels.
日本語の概要は準備中です。原文の説明を表示しています。
Rules and patterns for creating Avalonia UI visual elements (controls, dialogs, panels, windows). Use when creating or modifying XAML controls, dialogs, panels, or windows in this project.
日本語の概要は準備中です。原文の説明を表示しています。
Author and modify documentation for projects that use Gehtsoft's docgen utility. Use this skill whenever the user mentions docgen, .ds files, the doc/ directory of a repository, project.xml or project.proj for documentation, or wants to write/edit/build/debug API documentation in any codebase that uses docgen. Also use it whenever the user asks about generating C#, C++, or Java API documentation that follows Gehtsoft conventions, even if they don't say "docgen" explicitly.
日本語の概要は準備中です。原文の説明を表示しています。
Design a rifle-scope reticle from a plain-language description and emit it as a BallisticCalculator `.reticle` file (BXml) plus a rendered SVG preview. Covers the complete `.reticle` element/attribute specification, reticle design principles (mrad vs MOA subtension, FFP/SFP, stroke-weight hierarchy, hash-mark ladders, christmas-tree wind holds, clutter budget, BDC anchors) and worked, verified patterns for duplex, mil-dot, hash-ladder, christmas-tree, chevron, circle-dot, dense-grid and rangefinding-stadia reticles. Also covers reproducing an existing commercial reticle from its manufacturer's technical data sheet. Use this skill whenever the user wants to create, modify, review or render a scope reticle, crosshair, mil-dot / MOA grid, holdover ladder, christmas tree, BDC ladder or rangefinding stadia — including any mention of a `.reticle` file, `ReticleDefinition`, `ReticleDrawController`, `SvgCanvasFactory`, or "draw me a reticle", and including requests to clone or model a named commercial reticle (Horus H58/H59, TReMoR, Mil-C, MOAR, Tremor3, EBR, and the like). Trigger it even when the user only says something like "make a crosshair with 0.2 mil hashes out to 5 mils" or "turn this reticle picture into a file", and trigger it before hand-writing reticle XML or hand-rolling reticle geometry, because the file format silently ignores misspelled attributes and mis-cased booleans.
日本語の概要は準備中です。原文の説明を表示しています。