{"record":{"id":"c87818270000dfe5","repo":"stride3d/stride","slug":"can-t-handle-nameof-heightrange-included-both-of-positive","errorCode":null,"errorMessage":"Can't handle { nameof(heightRange) } included both of positive and negative in { heightType } type.","messagePattern":"Can't handle (.+?) included both of positive and negative in (.+?) type\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Physics/Engine/HeightmapUtils.cs","lineNumber":49,"sourceCode":"                    throw new ArgumentException($\"{ nameof(heightScale) } should be 1 in { heightType } type.\");\n                }\n\n                // HeightRange\n\n                if (heightRange.Y < heightRange.X)\n                {\n                    throw new ArgumentException($\"{ nameof(heightRange) }.{ nameof(heightRange.Y) } should be greater than { nameof(heightRange.X) }.\");\n                }\n\n                if (Math.Abs((heightRange.Y / heightScale) - (heightRange.X / heightScale)) < 1)\n                {\n                    throw new ArgumentException($\"{ nameof(heightRange) } is too short.\");\n                }\n\n                if ((heightType == HeightfieldTypes.Byte) &&\n                    (Math.Sign(heightRange.X) + Math.Sign(heightRange.Y)) == 0)\n                {\n                    throw new ArgumentException($\"Can't handle { nameof(heightRange) } included both of positive and negative in { heightType } type.\");\n                }\n            }\n            catch (ArgumentException)\n            {\n                if (throwExceptionWhenInvalid) throw;\n\n                return false;\n            }\n\n            return true;\n        }\n\n        public static float ConvertToFloatHeight(float minValue, float maxValue, float value) => MathUtil.InverseLerp(minValue, maxValue, MathUtil.Clamp(value, minValue, maxValue)) * 2 - 1;\n        public static short ConvertToShortHeight(float minValue, float maxValue, float value) => (short)Math.Round(ConvertToFloatHeight(minValue, maxValue, value) * short.MaxValue, MidpointRounding.AwayFromZero);\n        public static byte ConvertToByteHeight(float minValue, float maxValue, float value) => (byte)Math.Round(MathUtil.InverseLerp(minValue, maxValue, MathUtil.Clamp(value, minValue, maxValue)) * byte.MaxValue, MidpointRounding.AwayFromZero);\n\n        public static float[] ConvertToFloatHeights(float[] values, float minValue, float maxValue) => values.Select((v) => ConvertToFloatHeight(minValue, maxValue, v)).ToArray();\n        public static float[] ConvertToFloatHeights(short[] values, short minValue = short.MinValue, short maxValue = short.MaxValue) => values.Select((v) => ConvertToFloatHeight(minValue, maxValue, v)).ToArray();","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Physics/Engine/HeightmapUtils.cs#L31-L67","documentation":"Byte heightfields can only encode heights on one side of zero, because byte values are mapped into a range that cannot straddle both positive and negative with this conversion. CheckHeightParameters throws when a Byte-type heightmap's range has X negative and Y positive (signs summing to 0).","triggerScenarios":"HeightType = HeightfieldTypes.Byte with a HeightRange like (-1, 1) or (-10, 100); using ConvertToByteHeights with a range crossing zero.","commonSituations":"Terrain with sea level at 0 where artists keep symmetric positive/negative ranges; switching HeightType from Short/Float to Byte without adjusting the range.","solutions":["Shift the height range so it lies entirely on one side of zero (e.g. 0..max) for Byte type.","Switch HeightType to Short or Float, which support ranges crossing zero.","Offset the height data so the minimum becomes 0 and store the offset elsewhere.","Validate with IsValid() and normalize the range before creating colliders."],"exampleFix":"// before\nheightmap.HeightType = HeightfieldTypes.Byte;\nheightmap.HeightRange = new FloatRange(-10f, 10f);\n// after\nheightmap.HeightType = HeightfieldTypes.Byte;\nheightmap.HeightRange = new FloatRange(0f, 20f); // shift data by +10","handlingStrategy":"validation","validationCode":"if (heightType == HeightfieldTypes.Byte && Math.Sign(range.X) + Math.Sign(range.Y) == 0) range = new FloatRange(0f, range.Y - range.X);","typeGuard":"bool ByteSafeRange(HeightfieldTypes t, FloatRange r) => t != HeightfieldTypes.Byte || (Math.Sign(r.X) + Math.Sign(r.Y)) != 0;","tryCatchPattern":"try { CheckHeightParameters(size, type, range, scale, true); } catch (ArgumentException) { /* shift range or switch to Short/Float */ }","preventionTips":["Use Byte type only for single-sign ranges","Shift data so min becomes 0 for Byte","Re-evaluate type when terrain crosses zero (elevation datum)"],"tags":["csharp","argument-validation","physics","heightfield-types"],"backgroundTag":"invalid-argument-value","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}