diff --git a/include/threepp/loaders/AssimpLoader.hpp b/include/threepp/loaders/AssimpLoader.hpp index 57f58225..5ddd2389 100644 --- a/include/threepp/loaders/AssimpLoader.hpp +++ b/include/threepp/loaders/AssimpLoader.hpp @@ -395,15 +395,32 @@ namespace threepp { } sum = std::sqrt(sum); - for (unsigned i = 0; i < 4; i++) { + // Only normalize if sum is not zero + if (sum > 0.0f) { + for (unsigned i = 0; i < 4; i++) { + + pairs[i].second = pairs[i].second / sum; - pairs[i].second = pairs[i].second / sum; + while (indexes.size() <= i) indexes.emplace_back(); + while (weights.size() <= i) weights.emplace_back(); - while (indexes.size() <= i) indexes.emplace_back(); - while (weights.size() <= i) weights.emplace_back(); + indexes[i] = pairs[i].first; + weights[i] = pairs[i].second; + } + } else { + // Fallback: If all weights are zero (degenerate case), distribute equally. + // This maintains compatibility with downstream code expecting 4 weights, + // though in practice this should rarely occur in well-formed models. + for (unsigned i = 0; i < 4; i++) { - indexes[i] = pairs[i].first; - weights[i] = pairs[i].second; + pairs[i].second = 0.25f; + + while (indexes.size() <= i) indexes.emplace_back(); + while (weights.size() <= i) weights.emplace_back(); + + indexes[i] = pairs[i].first; + weights[i] = pairs[i].second; + } } } diff --git a/src/threepp/extras/curves/SplineCurve.cpp b/src/threepp/extras/curves/SplineCurve.cpp index 8c23de30..70b6e296 100644 --- a/src/threepp/extras/curves/SplineCurve.cpp +++ b/src/threepp/extras/curves/SplineCurve.cpp @@ -14,6 +14,12 @@ SplineCurve::SplineCurve(std::vector points) void SplineCurve::getPoint(float t, Vector2& point) const { + // Validate input: need at least 1 point + if (points.empty()) { + point.set(0.0f, 0.0f); + return; + } + const auto p = static_cast(points.size() - 1) * t; const auto intPoint = static_cast(std::floor(p)); @@ -21,8 +27,8 @@ void SplineCurve::getPoint(float t, Vector2& point) const { const auto p0 = points[intPoint == 0 ? intPoint : intPoint - 1]; const auto p1 = points[intPoint]; - const auto p2 = points[intPoint > points.size() - 2 ? points.size() - 1 : intPoint + 1]; - const auto p3 = points[intPoint > points.size() - 3 ? points.size() - 1 : intPoint + 2]; + const auto p2 = points[intPoint >= points.size() - 1 ? points.size() - 1 : intPoint + 1]; + const auto p3 = points[intPoint >= points.size() - 2 ? points.size() - 1 : intPoint + 2]; point.set( interpolants::CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), diff --git a/src/threepp/geometries/LatheGeometry.cpp b/src/threepp/geometries/LatheGeometry.cpp index 1287413f..347feee4 100644 --- a/src/threepp/geometries/LatheGeometry.cpp +++ b/src/threepp/geometries/LatheGeometry.cpp @@ -8,6 +8,11 @@ using namespace threepp; LatheGeometry::LatheGeometry(const std::vector& points, unsigned int segments, float phiStart, float phiLength) { + // Validate input: need at least 2 points to create geometry + if (points.size() < 2) { + return; + } + // clamp phiLength so it's in range of [ 0, 2PI ] phiLength = std::clamp(phiLength, 0.f, math::TWO_PI); diff --git a/src/threepp/loaders/SVGLoader.cpp b/src/threepp/loaders/SVGLoader.cpp index 25a8d409..07328008 100644 --- a/src/threepp/loaders/SVGLoader.cpp +++ b/src/threepp/loaders/SVGLoader.cpp @@ -282,6 +282,7 @@ unsigned int pointsToStrokeWithBuffers( float strokeWidth2 = style.strokeWidth / 2; + // Safe division since we already checked numPoints >= 2 float deltaU = 1.f / static_cast(numPoints - 1); float u0 = 0, u1; diff --git a/src/threepp/loaders/svg/SVGFunctions.hpp b/src/threepp/loaders/svg/SVGFunctions.hpp index e4388b2a..b2e45c8b 100644 --- a/src/threepp/loaders/svg/SVGFunctions.hpp +++ b/src/threepp/loaders/svg/SVGFunctions.hpp @@ -363,9 +363,14 @@ namespace threepp::svg { t = bx / ax; - } else { + } else if (ay != 0) { t = by / ay; + + } else { + // Both ax and ay are 0, degenerate case + classifyResult.loc = IntersectionLocationType::BEHIND; + return; } classifyResult.loc = IntersectionLocationType::BETWEEN; diff --git a/src/threepp/math/Lut.cpp b/src/threepp/math/Lut.cpp index 0eb481cf..3ead3253 100644 --- a/src/threepp/math/Lut.cpp +++ b/src/threepp/math/Lut.cpp @@ -29,6 +29,12 @@ Lut& Lut::setColorMap(const std::string& colormap, int numberofcolors) { this->map = ColorMapKeywords[colormap]; this->n = numberofcolors; + // Validate input: need at least 1 color. Clamp to 1 to prevent division by zero. + // Using silent correction to maintain backward compatibility with existing code. + if (this->n <= 0) { + this->n = 1; + } + const float step = 1.f / static_cast(this->n); @@ -49,7 +55,9 @@ Lut& Lut::setColorMap(const std::string& colormap, int numberofcolors) { Color minColor(this->map[j].second); Color maxColor(this->map[j + 1].second); - Color color = minColor.lerp(maxColor, (alpha - min) / (max - min)); + // Avoid division by zero if min == max + float t = (max - min > 0.0f) ? (alpha - min) / (max - min) : 0.0f; + Color color = minColor.lerp(maxColor, t); this->lut.emplace_back(color); } @@ -72,10 +80,18 @@ void Lut::copy(const Lut& lut) { Color Lut::getColor(float alpha) const { alpha = std::clamp(alpha, this->minV, this->maxV); - alpha = (alpha - this->minV) / (this->maxV - this->minV); + // Avoid division by zero if minV == maxV + if (this->maxV - this->minV > 0.0f) { + alpha = (alpha - this->minV) / (this->maxV - this->minV); + } else { + alpha = 0.0f; + } const int colorPosition = static_cast(std::round(alpha * static_cast(this->n))); - return this->lut[colorPosition]; + // Clamp colorPosition to valid range [0, n-1] to prevent out-of-bounds access + const int clampedPosition = std::min(colorPosition, static_cast(this->n - 1)); + + return this->lut[clampedPosition]; } void Lut::addColorMap(const std::string& name, const std::vector>& arrayOfColors) {