#pragma once #include "sphpack.h" #include "mars_geometry.h" #include "mars_ptr.h" #include "mars_splines.h" #include "mars_util.h" namespace geoworld { template < typename T > class SpherePackFactory { public: typedef typename RandomSpherePack::VECTOR VECTOR; typedef mars::ptr< RandomSpherePack > PackPtr; private: const mars::RangeX _sizeflux; const mars::RangeX _kidflux, _viter; const T _minimum, _maxtier, _limit; const float _verratic, _fminimum, _fpihalf; class VeinGuide : public mars::BasicGuide { private: inline VECTOR randomV (const T delta) const { using namespace mars; return vector3D ( RAND(delta) - delta / 2, RAND(delta) - delta / 2, RAND(delta) - delta / 2 ); } public: typedef mars::BasicGuide super; inline VeinGuide (const typename super::Point & a, const typename super::Point & b) : super::BasicGuide(a, b) {} void subdivide (const float factor) { typename super::PointList::const_iterator itFollow, itLeading; itLeading = super::begin(); itFollow = itLeading++; while (itLeading != super::end()) { const VECTOR g = this->grad(itFollow); const VECTOR p2 = *itFollow + g / 2 + randomV(static_cast< T > (static_cast< float > (MAG(g)) * factor)); super::_points.insert(itLeading, p2); itFollow = itLeading++; } } }; public: inline SpherePackFactory ( const T limit, // Smallest-visible sphere radius before descendant generation ceases const T minimum, // Absolute smallest sphere radius, any generated spheres with radii below this value are discarded const T maxtier, // The maximum number depth-iterations (as opposed to breadth), this also affects the level of detail const mars::RangeX & sizeflux, // Flux of size of spheres at starting tier const mars::RangeX & kidflux, // Flux of number of breadth-iterations (as opposed to depth) const mars::RangeX & viter, // Range of allowed number of vein-subdivision iterations (0 = perfectly straight vein, 2^N -> INF = hopelessly crooked) const float verratic // Amount of angular erratic placement of spheres in a vein ) : _sizeflux(sizeflux), _kidflux(kidflux), _limit(limit), _minimum(minimum), _fminimum(static_cast< float > (minimum)), _maxtier(maxtier), _viter(viter), _verratic(verratic), _fpihalf(static_cast< float > (mars::PI / 2)) {} PackPtr createLaccolith (const VECTOR & at, const float scale, const float tail) const { using namespace mars; PackPtr pPack = new RandomSpherePack (_limit, _minimum, _maxtier, _sizeflux, _kidflux); float size = scale * tail; pPack->spawn(at, static_cast< T > (scale)); if (size > _fminimum) { pPack->spawn(_fpihalf, _fpihalf, static_cast< T > (size)); size *= tail; if (size > _fminimum) pPack->spawn(_fpihalf, _fpihalf, static_cast< T > (size)); } pPack->generateDescendants(); return pPack; } PackPtr createCluster (const VECTOR & at, const float scale) const { using namespace mars; PackPtr pPack = new RandomSpherePack (_limit, _minimum, _maxtier, _sizeflux, _kidflux); pPack->spawn(at, static_cast< T > (scale)); pPack->generateDescendants(); return pPack; } PackPtr createVein (const VECTOR & a, const VECTOR & b, const float scale, const float deviation) const { using namespace mars; VeinGuide guide (a, b); const unsigned short iterations = _viter.next(); for (unsigned short c = 0; c < iterations; ++c) guide.subdivide(deviation); PackPtr pPack = new RandomSpherePack (_limit, _minimum, _maxtier, _sizeflux, _kidflux); typename RandomSpherePack< T >::Sphere region = pPack->createEmptySphere(); typename VeinGuide::SegmentType i = guide.begin(), ip; SphericalCoords< T > spc = guide.grad (i); const float fSQScale = SQ(scale); const RangeX rrarc(-_fpihalf * _verratic, _fpihalf * _verratic, 0.02f); region = pPack->spawn(a, static_cast< T > (scale)); while (MAGSQ(guide.back() - region.p) > fSQScale) { ip = guide.nearestPoint(i, region.p); spc = guide.tail(ip) - region.p; region = pPack->spawn(spc.zenith + rrarc.next(), spc.azimuth + rrarc.next(), static_cast< T > (scale)); i = ip; } pPack->generateDescendants(); return pPack; } template< typename SB > PackPtr createSpline (const mars::Spline< SB > & spline, const float scale, const float deviation) const { using namespace mars; typedef typename mars::Spline< SB >::real real; PackPtr pPack = new RandomSpherePack (_limit, _minimum, _maxtier, _sizeflux, _kidflux); for (unsigned int c = 0; c < spline.segCount(); ++c) { const real fLen = spline.len(c); const unsigned int nCount = static_cast< unsigned int > (fLen / scale * 2); for (unsigned int i = 0; i < nCount; ++i) pPack->spawn(spline.compute(c, static_cast< real > (i) / static_cast< real > (nCount)), static_cast< T > (scale)); } pPack->generateDescendants(); return pPack; } }; }