bool Polygon::DiagonalExists(int i, int j) const { assume(p.size() >= 3); assume(i >= 0); assume(j >= 0); assume(i < (int)p.size()); assume(j < (int)p.size()); #ifndef MATH_ENABLE_INSECURE_OPTIMIZATIONS if (p.size() < 3 || i < 0 || j < 0 || i >= (int)p.size() || j >= (int)p.size()) return false; #endif assume(IsPlanar()); assume(i != j); if (i == j) // Degenerate if i == j. return false; if (i > j) Swap(i, j); assume(i+1 != j); if (i+1 == j) // Is this LineSegment an edge of this polygon? return false; Plane polygonPlane = PlaneCCW(); LineSegment diagonal = polygonPlane.Project(LineSegment(p[i], p[j])); // First check that this diagonal line is not intersected by an edge of this polygon. for(int k = 0; k < (int)p.size(); ++k) if (!(k == i || k+1 == i || k == j)) if (polygonPlane.Project(LineSegment(p[k], p[k+1])).Intersects(diagonal)) return false; return IsConvex(); }
/** Implementation based on Graphics Gems 2, p. 170: "IV.1. Area of Planar Polygons and Volume of Polyhedra." */ float Polygon::Area() const { assume(IsPlanar()); float3 area = float3::zero; int i = NumEdges()-1; for(int j = 0; j < NumEdges(); ++j) { area += Vertex(i).Cross(Vertex(j)); i = j; } return 0.5f * Abs(NormalCCW().Dot(area)); }
bool Polygon::IsSimple() const { assume(IsPlanar()); Plane plane = PlaneCCW(); for(int i = 0; i < (int)p.size(); ++i) { LineSegment si = plane.Project(Edge(i)); for(int j = i+2; j < (int)p.size(); ++j) { if (i == 0 && j == (int)p.size() - 1) continue; // These two edges are consecutive and share a vertex. Don't check that pair. LineSegment sj = plane.Project(Edge(j)); if (si.Intersects(sj)) return false; } } return true; }
cairo_surface_t * GLSurface::Cairo () { int stride = size[0] * 4; if (!data) { data = (unsigned char *) g_malloc0 (stride * size[1]); // derived class should implement read back of texture image g_assert (texture == 0 && !IsPlanar ()); } return cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, size[0], size[1], stride); }
//------------------------------------------------------------------------------------- // Decompression //------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT Decompress( const Image& cImage, DXGI_FORMAT format, ScratchImage& image ) { if ( !IsCompressed(cImage.format) || IsCompressed(format) ) return E_INVALIDARG; if ( format == DXGI_FORMAT_UNKNOWN ) { // Pick a default decompressed format based on BC input format format = _DefaultDecompress( cImage.format ); if ( format == DXGI_FORMAT_UNKNOWN ) { // Input is not a compressed format return E_INVALIDARG; } } else { if ( !IsValid(format) ) return E_INVALIDARG; if ( IsTypeless(format) || IsPlanar(format) || IsPalettized(format) ) return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } // Create decompressed image HRESULT hr = image.Initialize2D( format, cImage.width, cImage.height, 1, 1 ); if ( FAILED(hr) ) return hr; const Image *img = image.GetImage( 0, 0, 0 ); if ( !img ) { image.Release(); return E_POINTER; } // Decompress single image hr = _DecompressBC( cImage, *img ); if ( FAILED(hr) ) image.Release(); return hr; }
float3 Polygon::ClosestPoint(const float3 &point) const { assume(IsPlanar()); std::vector<Triangle> tris = Triangulate(); float3 closestPt = float3::nan; float closestDist = FLT_MAX; for(size_t i = 0; i < tris.size(); ++i) { float3 pt = tris[i].ClosestPoint(point); float d = pt.DistanceSq(point); if (d < closestDist) { closestPt = pt; closestDist = d; } } return closestPt; }
vec Polygon::ClosestPoint(const vec &point) const { assume(IsPlanar()); TriangleArray tris = Triangulate(); vec closestPt = vec::nan; float closestDist = FLT_MAX; for(size_t i = 0; i < tris.size(); ++i) { vec pt = TRIANGLE(tris[i]).ClosestPoint(point); float d = pt.DistanceSq(point); if (d < closestDist) { closestPt = pt; closestDist = d; } } return closestPt; }
//------------------------------------------------------------------------------------- // Compression //------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT Compress( const Image& srcImage, DXGI_FORMAT format, DWORD compress, float alphaRef, ScratchImage& image ) { if ( IsCompressed(srcImage.format) || !IsCompressed(format) ) return E_INVALIDARG; if ( IsTypeless(format) || IsTypeless(srcImage.format) || IsPlanar(srcImage.format) || IsPalettized(srcImage.format) ) return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); // Create compressed image HRESULT hr = image.Initialize2D( format, srcImage.width, srcImage.height, 1, 1 ); if ( FAILED(hr) ) return hr; const Image *img = image.GetImage( 0, 0, 0 ); if ( !img ) { image.Release(); return E_POINTER; } // Compress single image if (compress & TEX_COMPRESS_PARALLEL) { #ifndef _OPENMP return E_NOTIMPL; #else hr = _CompressBC_Parallel( srcImage, *img, _GetBCFlags( compress ), _GetSRGBFlags( compress ), alphaRef ); #endif // _OPENMP } else { hr = _CompressBC( srcImage, *img, _GetBCFlags( compress ), _GetSRGBFlags( compress ), alphaRef ); } if ( FAILED(hr) ) image.Release(); return hr; }
float3 Polygon::ClosestPoint(const float3 &point) const { assume(IsPlanar()); float3 ptOnPlane = PlaneCCW().Project(point); if (Contains(ptOnPlane)) return ptOnPlane; float3 closestPt; float closestDist = FLOAT_MAX; for(int i = 0; i < NumEdges(); ++i) { float3 pt = Edge(i).ClosestPoint(point); float d = pt.DistanceSq(point); if (d < closestDist) { closestPt = pt; closestDist = d; } } return ptOnPlane; }
bool Polygon::IsConvex() const { assume(IsPlanar()); if (p.empty()) return false; if (p.size() <= 3) return true; int i = (int)p.size()-2; int j = (int)p.size()-1; int k = 0; while(k < (int)p.size()) { float2 a = MapTo2D(i); float2 b = MapTo2D(j); float2 c = MapTo2D(k); if (!float2::OrientedCCW(a, b, c)) return false; i = j; j = k; ++k; } return true; }
/** The implementation of this function is based on the paper "Kong, Everett, Toussant. The Graham Scan Triangulates Simple Polygons." See also p. 772-775 of Geometric Tools for Computer Graphics. The running time of this function is O(n^2). */ std::vector<Triangle> Polygon::Triangulate() const { assume(IsPlanar()); std::vector<Triangle> t; // Handle degenerate cases. if (NumVertices() < 3) return t; if (NumVertices() == 3) { t.push_back(Triangle(Vertex(0), Vertex(1), Vertex(2))); return t; } std::vector<float2> p2d; std::vector<int> polyIndices; for(int v = 0; v < NumVertices(); ++v) { p2d.push_back(MapTo2D(v)); polyIndices.push_back(v); } // Clip ears of the polygon until it has been reduced to a triangle. int i = 0; int j = 1; int k = 2; size_t numTries = 0; // Avoid creating an infinite loop. while(p2d.size() > 3 && numTries < p2d.size()) { if (float2::OrientedCCW(p2d[i], p2d[j], p2d[k]) && IsAnEar(p2d, i, k)) { // The vertex j is an ear. Clip it off. t.push_back(Triangle(p[polyIndices[i]], p[polyIndices[j]], p[polyIndices[k]])); p2d.erase(p2d.begin() + j); polyIndices.erase(polyIndices.begin() + j); // The previous index might now have become an ear. Move back one index to see if so. if (i > 0) { i = (i + (int)p2d.size() - 1) % p2d.size(); j = (j + (int)p2d.size() - 1) % p2d.size(); k = (k + (int)p2d.size() - 1) % p2d.size(); } numTries = 0; } else { // The vertex at j is not an ear. Move to test next vertex. i = j; j = k; k = (k+1) % p2d.size(); ++numTries; } } assume(p2d.size() == 3); if (p2d.size() > 3) // If this occurs, then the polygon is NOT counter-clockwise oriented. return t; /* { // For conveniency, create a copy that has the winding order fixed, and triangulate that instead. // (Causes a large performance hit!) Polygon p2 = *this; for(size_t i = 0; i < p2.p.size()/2; ++i) std::swap(p2.p[i], p2.p[p2.p.size()-1-i]); return p2.Triangulate(); } */ // Add the last poly. t.push_back(Triangle(p[polyIndices[0]], p[polyIndices[1]], p[polyIndices[2]])); return t; }
_Use_decl_annotations_ HRESULT Decompress( const Image* cImages, size_t nimages, const TexMetadata& metadata, DXGI_FORMAT format, ScratchImage& images ) { if ( !cImages || !nimages ) return E_INVALIDARG; if ( !IsCompressed(metadata.format) || IsCompressed(format) ) return E_INVALIDARG; if ( format == DXGI_FORMAT_UNKNOWN ) { // Pick a default decompressed format based on BC input format format = _DefaultDecompress( cImages[0].format ); if ( format == DXGI_FORMAT_UNKNOWN ) { // Input is not a compressed format return E_FAIL; } } else { if ( !IsValid(format) ) return E_INVALIDARG; if ( IsTypeless(format) || IsPlanar(format) || IsPalettized(format) ) HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } images.Release(); TexMetadata mdata2 = metadata; mdata2.format = format; HRESULT hr = images.Initialize( mdata2 ); if ( FAILED(hr) ) return hr; if ( nimages != images.GetImageCount() ) { images.Release(); return E_FAIL; } const Image* dest = images.GetImages(); if ( !dest ) { images.Release(); return E_POINTER; } for( size_t index=0; index < nimages; ++index ) { assert( dest[ index ].format == format ); const Image& src = cImages[ index ]; if ( !IsCompressed( src.format ) ) { images.Release(); return E_FAIL; } if ( src.width != dest[ index ].width || src.height != dest[ index ].height ) { images.Release(); return E_FAIL; } hr = _DecompressBC( src, dest[ index ] ); if ( FAILED(hr) ) { images.Release(); return hr; } } return S_OK; }
_Use_decl_annotations_ HRESULT Compress( const Image* srcImages, size_t nimages, const TexMetadata& metadata, DXGI_FORMAT format, DWORD compress, float alphaRef, ScratchImage& cImages ) { if ( !srcImages || !nimages ) return E_INVALIDARG; if ( IsCompressed(metadata.format) || !IsCompressed(format) ) return E_INVALIDARG; if ( IsTypeless(format) || IsTypeless(metadata.format) || IsPlanar(metadata.format) || IsPalettized(metadata.format) ) return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); cImages.Release(); TexMetadata mdata2 = metadata; mdata2.format = format; HRESULT hr = cImages.Initialize( mdata2 ); if ( FAILED(hr) ) return hr; if ( nimages != cImages.GetImageCount() ) { cImages.Release(); return E_FAIL; } const Image* dest = cImages.GetImages(); if ( !dest ) { cImages.Release(); return E_POINTER; } for( size_t index=0; index < nimages; ++index ) { assert( dest[ index ].format == format ); const Image& src = srcImages[ index ]; if ( src.width != dest[ index ].width || src.height != dest[ index ].height ) { cImages.Release(); return E_FAIL; } if ( (compress & TEX_COMPRESS_PARALLEL) ) { #ifndef _OPENMP return E_NOTIMPL; #else if ( compress & TEX_COMPRESS_PARALLEL ) { hr = _CompressBC_Parallel( src, dest[ index ], _GetBCFlags( compress ), _GetSRGBFlags( compress ), alphaRef ); if ( FAILED(hr) ) { cImages.Release(); return hr; } } #endif // _OPENMP } else { hr = _CompressBC( src, dest[ index ], _GetBCFlags( compress ), _GetSRGBFlags( compress ), alphaRef ); if ( FAILED(hr) ) { cImages.Release(); return hr; } } } return S_OK; }
bool OpenGLSurface::HasTexture () { return texture != 0 || IsPlanar (); }
std::string Format::Name() const { static const std::string funcName = "Format::Name"; if (IsPlanar()) { std::string name; switch (type) { case Gray: name += "Gray"; break; case RGB: name += "RGB"; break; case RGBA: name += "RGBA"; break; case YUV: name += "YUV"; if (subsample_w == 0 && subsample_h == 0) { name += "444P"; } else if (subsample_w == 1 && subsample_h == 0) { name += "422P"; } else if (subsample_w == 1 && subsample_h == 1) { name += "420P"; } else if (subsample_w == 2 && subsample_h == 0) { name += "411P"; } else if (subsample_w == 2 && subsample_h == 1) { name += "410P"; } else { name += "ssw" + std::to_string(subsample_w) + "ssh" + std::to_string(subsample_h) + "P"; } break; default: throw std::invalid_argument(funcName + ": unrecognized Planar type!"); } name += std::to_string(bps); switch (sample) { case UInteger: name += "U"; break; case Integer: name += "I"; break; case Float: name += "F"; break; case Complex: name += "C"; break; default: throw std::invalid_argument(funcName + ": unrecognized sample!"); } return name; } else if (IsPacked()) { switch (type) { case RGB24: return "RGB24"; case RGB32: return "RGB32"; case RGBA32: return "RGBA32"; case GBR24: return "GBR24"; case GBR32: return "GBR32"; case GBRA32: return "GBRA32"; case BGR24: return "BGR24"; case BGR32: return "BGR32"; case BGRA32: return "BGRA32"; case YUYV: return "YUYV"; case YVYU: return "YVYU"; case UYVY: return "UYVY"; case VYUY: return "VYUY"; case AYUV: return "AYUV"; default: throw std::invalid_argument(funcName + ": unrecognized Packed type!"); } } else if (type == Undef) { return "Undefined"; } else { throw std::invalid_argument(funcName + ": unrecognized type!"); } return std::string(); }