# pragma once
# include "olcPGEX_TTF.h"
# include "olcUTIL_Geometry2D.h"
# include <algorithm>
# include <array>
# include <cmath>
# include <cstdint>
# include <iostream>
# include <vector>
# include <limits>
// Declarations
namespace olc {
class ViewPort : public olc : : PGEX {
public :
ViewPort ( ) ;
ViewPort ( std : : vector < vf2d > vertices , vf2d offset = { 0 , 0 } ) ;
geom2d : : rect < float > rect { } ;
virtual ~ ViewPort ( ) ;
void addPoint ( vf2d point ) ;
void clear ( ) ;
void drawEdges ( ) ;
void setOffset ( vf2d offset ) ;
const vf2d & GetOffset ( ) ;
static ViewPort rectViewPort ( vf2d topLeft ,
vf2d size ,
olc : : vf2d offset = { 0 , 0 } ) ;
void DrawDecal ( const olc : : vf2d & pos ,
olc : : Decal * decal ,
const olc : : vf2d & scale = { 1.0f , 1.0f } ,
const olc : : Pixel & tint = olc : : WHITE ) const ;
void DrawRectDecal ( const olc : : vf2d & pos , const olc : : vf2d & size , const olc : : Pixel col = olc : : WHITE ) const ;
void DrawPartialDecal ( const olc : : vf2d & pos ,
olc : : Decal * decal ,
const olc : : vf2d & source_pos ,
const olc : : vf2d & source_size ,
const olc : : vf2d & scale = { 1.0f , 1.0f } ,
const olc : : Pixel & tint = olc : : WHITE ) const ;
void DrawPartialDecal ( const vf2d & pos ,
const vf2d & size ,
Decal * decal ,
const vf2d source_pos ,
const vf2d & source_size ,
const Pixel & tint = olc : : WHITE ) const ;
void DrawExplicitDecal ( olc : : Decal * decal ,
const olc : : vf2d * pos ,
const olc : : vf2d * uv ,
const olc : : Pixel * col ,
const float * ws ,
uint32_t elements = 4 ) const ;
void DrawWarpedDecal ( Decal * decal ,
const vf2d ( & pos ) [ 4 ] ,
const Pixel & tint = WHITE ) const ;
void DrawWarpedDecal ( Decal * decal ,
const vf2d * pos ,
const Pixel & tint = WHITE ) const ;
void DrawWarpedDecal ( Decal * decal ,
const std : : array < vf2d , 4 > & pos ,
const Pixel & tint = WHITE ) const ;
void DrawPartialWarpedDecal ( Decal * decal ,
const vf2d ( & pos ) [ 4 ] ,
const vf2d & source_pos ,
const vf2d & source_size ,
const Pixel & tint = WHITE ) const ;
void DrawPartialWarpedDecal ( Decal * decal ,
const vf2d * pos ,
const vf2d & source_pos ,
const vf2d & source_size ,
const Pixel & tint = WHITE ) const ;
void DrawPartialWarpedDecal ( Decal * decal ,
const std : : array < vf2d , 4 > & pos ,
const vf2d & source_pos ,
const vf2d & source_size ,
const Pixel & tint = WHITE ) const ;
void DrawRotatedDecal ( const vf2d & pos ,
Decal * decal ,
const float fAngle ,
const vf2d & center = { 0.0f , 0.0f } ,
const vf2d & scale = { 1.0f , 1.0f } ,
const Pixel & tint = WHITE ) const ;
void DrawPartialRotatedDecal ( const vf2d & pos ,
Decal * decal ,
const float fAngle ,
const vf2d & center ,
const vf2d & source_pos ,
const vf2d & source_size ,
const vf2d & scale = { 1.0f , 1.0f } ,
const Pixel & tint = WHITE ) const ;
void FillRectDecal ( const vf2d & pos ,
const vf2d & size ,
const Pixel col = WHITE ) const ;
void GradientFillRectDecal ( const vf2d & pos ,
const vf2d & size ,
const Pixel colTL ,
const Pixel colBL ,
const Pixel colBR ,
const Pixel colTR ) const ;
void DrawPolygonDecal ( Decal * decal ,
const std : : vector < vf2d > & pos ,
const std : : vector < vf2d > & uv ,
const Pixel tint = WHITE ) const ;
void DrawPolygonDecal ( Decal * decal ,
const std : : vector < vf2d > & pos ,
const std : : vector < float > & depth ,
const std : : vector < vf2d > & uv ,
const Pixel tint = WHITE ) const ;
void DrawPolygonDecal ( Decal * decal ,
const std : : vector < vf2d > & pos ,
const std : : vector < vf2d > & uv ,
const std : : vector < Pixel > & tint ) const ;
void DrawLineDecal ( const vf2d & pos1 ,
const vf2d & pos2 ,
Pixel p = WHITE ) const ;
// Draws a multiline string as a decal, with tinting and scaling
void DrawStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = olc : : WHITE , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float width = std : : numeric_limits < float > : : max ( ) , const bool disableDynamicScaling = false ) ;
void DrawStringDecal ( Font & font , const olc : : vf2d & pos , const std : : u32string & sText , const Pixel col = olc : : WHITE , const olc : : vf2d & scale = { 1.0f , 1.0f } ) ;
void DrawStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = olc : : WHITE , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float width = std : : numeric_limits < float > : : max ( ) , const bool disableDynamicScaling = false ) ;
void DrawShadowStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = olc : : WHITE , const Pixel shadowCol = olc : : BLACK , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float width = std : : numeric_limits < float > : : max ( ) , const float shadowSizeFactor = 1 , const bool disableDynamicScaling = false ) ;
void DrawShadowStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = olc : : WHITE , const Pixel shadowCol = olc : : BLACK , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float width = std : : numeric_limits < float > : : max ( ) , const float shadowSizeFactor = 1 , const bool disableDynamicScaling = false ) ;
void DrawShadowStringDecal ( Font & font , const olc : : vf2d & pos , const std : : u32string & sText , const Pixel col = olc : : WHITE , const Pixel shadowCol = olc : : BLACK , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float shadowSizeFactor = 1 ) ;
void DrawDropShadowStringDecal ( Font & font , const olc : : vf2d & pos , const std : : u32string & sText , const Pixel col = olc : : WHITE , const Pixel shadowCol = olc : : BLACK , const olc : : vf2d & scale = { 1.0f , 1.0f } ) ;
private :
void drawClippedDecal ( Decal * decal ,
const vf2d * points ,
const vf2d * uvs ,
const Pixel * col ,
const float * ws ,
uint32_t elements = 0 ) const ;
static float lineSegmentIntersect ( vf2d lineA ,
vf2d lineB ,
vf2d segmentA ,
vf2d segmentB ) ;
static float directionFromLine ( vf2d lineA , vf2d lineB , vf2d point ) ;
std : : vector < vf2d > clipVertices ;
olc : : vf2d offset ;
} ;
} // namespace olc
// Definitions
# ifdef OLC_PGEX_VIEWPORT
# undef OLC_PGEX_VIEWPORT
olc : : ViewPort : : ViewPort ( ) {
}
olc : : ViewPort : : ~ ViewPort ( ) {
}
olc : : ViewPort : : ViewPort ( std : : vector < vf2d > vertices , olc : : vf2d offset )
: clipVertices { vertices } ,
offset { offset } {
}
void olc : : ViewPort : : addPoint ( vf2d point ) {
clipVertices . push_back ( point ) ;
}
void olc : : ViewPort : : clear ( ) {
clipVertices . clear ( ) ;
}
void olc : : ViewPort : : drawEdges ( ) {
for ( auto i = 0u ; i < clipVertices . size ( ) ; i + + ) {
auto current = clipVertices [ i ] + offset ;
auto next = clipVertices [ ( i + 1 ) % clipVertices . size ( ) ] + offset ;
pge - > DrawLineDecal ( current , next , olc : : RED ) ;
}
}
void olc : : ViewPort : : setOffset ( vf2d offset ) {
this - > offset = offset ;
}
const vf2d & olc : : ViewPort : : GetOffset ( ) {
return offset ;
}
olc : : ViewPort
olc : : ViewPort : : rectViewPort ( vf2d topLeft , vf2d size , olc : : vf2d offset ) {
olc : : ViewPort newPort = { {
topLeft ,
{ topLeft . x , topLeft . y + size . y } ,
topLeft + size ,
{ topLeft . x + size . x , topLeft . y } ,
} ,
offset } ;
newPort . rect = { topLeft , size } ;
return newPort ;
}
void olc : : ViewPort : : DrawDecal ( const olc : : vf2d & pos ,
olc : : Decal * decal ,
const olc : : vf2d & scale ,
const olc : : Pixel & tint ) const {
std : : vector < olc : : vf2d > points {
pos ,
{ pos . x , pos . y + decal - > sprite - > height * scale . y } ,
{ pos . x + decal - > sprite - > width * scale . x ,
pos . y + decal - > sprite - > height * scale . y } ,
{ pos . x + decal - > sprite - > width * scale . x , pos . y } ,
} ;
DrawWarpedDecal ( decal , points . data ( ) , tint ) ;
}
void olc : : ViewPort : : DrawPartialDecal ( const olc : : vf2d & pos ,
olc : : Decal * decal ,
const olc : : vf2d & source_pos ,
const olc : : vf2d & source_size ,
const olc : : vf2d & scale ,
const olc : : Pixel & tint ) const {
DrawPartialDecal ( pos , source_size * scale , decal , source_pos , source_size , tint ) ;
}
void olc : : ViewPort : : DrawPartialDecal ( const vf2d & pos ,
const vf2d & size ,
Decal * decal ,
const vf2d source_pos ,
const vf2d & source_size ,
const Pixel & tint ) const {
std : : vector < vf2d > points {
pos ,
{ pos . x , pos . y + size . y } ,
pos + size ,
{ pos . x + size . x , pos . y } ,
} ;
DrawPartialWarpedDecal ( decal , points . data ( ) , source_pos , source_size , tint ) ;
}
void olc : : ViewPort : : DrawExplicitDecal ( olc : : Decal * decal ,
const olc : : vf2d * pos ,
const olc : : vf2d * uv ,
const olc : : Pixel * col ,
const float * ws ,
uint32_t elements ) const {
drawClippedDecal ( decal , pos , uv , col , ws , elements ) ;
}
void olc : : ViewPort : : DrawWarpedDecal ( Decal * decal ,
const vf2d ( & pos ) [ 4 ] ,
const Pixel & tint ) const {
DrawWarpedDecal ( decal , ( const vf2d * ) pos , tint ) ;
}
void olc : : ViewPort : : DrawWarpedDecal ( Decal * decal ,
const vf2d * pos ,
const Pixel & tint ) const {
std : : vector < float > w { 1 , 1 , 1 , 1 } ;
std : : vector < olc : : vf2d > newPos ;
newPos . resize ( 4 ) ;
std : : vector < vf2d > uvs {
{ 0 , 0 } ,
{ 0 , 1 } ,
{ 1 , 1 } ,
{ 1 , 0 } ,
} ;
std : : vector < Pixel > cols {
tint ,
tint ,
tint ,
tint ,
} ;
olc : : vf2d vInvScreenSize = { 1.0f / pge - > GetScreenSize ( ) . x , 1.0f / pge - > GetScreenSize ( ) . y } ;
olc : : vf2d center ;
float rd = ( ( pos [ 2 ] . x - pos [ 0 ] . x ) * ( pos [ 3 ] . y - pos [ 1 ] . y ) - ( pos [ 3 ] . x - pos [ 1 ] . x ) * ( pos [ 2 ] . y - pos [ 0 ] . y ) ) ;
if ( rd ! = 0 )
{
rd = 1.0f / rd ;
float rn = ( ( pos [ 3 ] . x - pos [ 1 ] . x ) * ( pos [ 0 ] . y - pos [ 1 ] . y ) - ( pos [ 3 ] . y - pos [ 1 ] . y ) * ( pos [ 0 ] . x - pos [ 1 ] . x ) ) * rd ;
float sn = ( ( pos [ 2 ] . x - pos [ 0 ] . x ) * ( pos [ 0 ] . y - pos [ 1 ] . y ) - ( pos [ 2 ] . y - pos [ 0 ] . y ) * ( pos [ 0 ] . x - pos [ 1 ] . x ) ) * rd ;
if ( ! ( rn < 0.f | | rn > 1.f | | sn < 0.f | | sn > 1.f ) ) center = pos [ 0 ] + rn * ( pos [ 2 ] - pos [ 0 ] ) ;
float d [ 4 ] ; for ( int i = 0 ; i < 4 ; i + + ) d [ i ] = ( pos [ i ] - center ) . mag ( ) ;
for ( int i = 0 ; i < 4 ; i + + )
{
float q = d [ i ] = = 0.0f ? 1.0f : ( d [ i ] + d [ ( i + 2 ) & 3 ] ) / d [ ( i + 2 ) & 3 ] ;
uvs [ i ] * = q ; w [ i ] * = q ;
}
drawClippedDecal ( decal , pos , uvs . data ( ) , cols . data ( ) , w . data ( ) , 4 ) ;
}
}
void olc : : ViewPort : : DrawWarpedDecal ( Decal * decal ,
const std : : array < vf2d , 4 > & pos ,
const Pixel & tint ) const {
DrawWarpedDecal ( decal , pos . data ( ) , tint ) ;
}
void olc : : ViewPort : : DrawPartialWarpedDecal ( Decal * decal ,
const vf2d ( & pos ) [ 4 ] ,
const vf2d & source_pos ,
const vf2d & source_size ,
const Pixel & tint ) const {
DrawPartialWarpedDecal ( decal ,
( const vf2d * ) pos ,
source_pos ,
source_size ,
tint ) ;
}
void olc : : ViewPort : : DrawPartialWarpedDecal ( Decal * decal ,
const vf2d * pos ,
const vf2d & source_pos ,
const vf2d & source_size ,
const Pixel & tint ) const {
olc : : vf2d sourceUvPos =
source_pos
/ olc : : vf2d { static_cast < float > ( decal - > sprite - > width ) ,
static_cast < float > ( decal - > sprite - > height ) } ;
olc : : vf2d sourceUvSize =
source_size
/ olc : : vf2d { static_cast < float > ( decal - > sprite - > width ) ,
static_cast < float > ( decal - > sprite - > height ) } ;
std : : vector < vf2d > uvs {
sourceUvPos ,
{ sourceUvPos . x , sourceUvPos . y + sourceUvSize . y } ,
sourceUvPos + sourceUvSize ,
{ sourceUvPos . x + sourceUvSize . x , sourceUvPos . y } ,
} ;
std : : vector < Pixel > cols {
tint ,
tint ,
tint ,
tint ,
} ;
std : : vector < float > ws { 1 , 1 , 1 , 1 } ;
olc : : vf2d center ;
float rd = ( ( pos [ 2 ] . x - pos [ 0 ] . x ) * ( pos [ 3 ] . y - pos [ 1 ] . y ) - ( pos [ 3 ] . x - pos [ 1 ] . x ) * ( pos [ 2 ] . y - pos [ 0 ] . y ) ) ;
if ( rd ! = 0 )
{
rd = 1.0f / rd ;
float rn = ( ( pos [ 3 ] . x - pos [ 1 ] . x ) * ( pos [ 0 ] . y - pos [ 1 ] . y ) - ( pos [ 3 ] . y - pos [ 1 ] . y ) * ( pos [ 0 ] . x - pos [ 1 ] . x ) ) * rd ;
float sn = ( ( pos [ 2 ] . x - pos [ 0 ] . x ) * ( pos [ 0 ] . y - pos [ 1 ] . y ) - ( pos [ 2 ] . y - pos [ 0 ] . y ) * ( pos [ 0 ] . x - pos [ 1 ] . x ) ) * rd ;
if ( ! ( rn < 0.f | | rn > 1.f | | sn < 0.f | | sn > 1.f ) ) center = pos [ 0 ] + rn * ( pos [ 2 ] - pos [ 0 ] ) ;
float d [ 4 ] ; for ( int i = 0 ; i < 4 ; i + + ) d [ i ] = ( pos [ i ] - center ) . mag ( ) ;
for ( int i = 0 ; i < 4 ; i + + )
{
float q = d [ i ] = = 0.0f ? 1.0f : ( d [ i ] + d [ ( i + 2 ) & 3 ] ) / d [ ( i + 2 ) & 3 ] ;
uvs [ i ] * = q ; ws [ i ] * = q ;
}
drawClippedDecal ( decal , pos , uvs . data ( ) , cols . data ( ) , ws . data ( ) , 4 ) ;
}
}
void olc : : ViewPort : : DrawPartialWarpedDecal ( Decal * decal ,
const std : : array < vf2d , 4 > & pos ,
const vf2d & source_pos ,
const vf2d & source_size ,
const Pixel & tint ) const {
DrawPartialWarpedDecal ( decal , pos . data ( ) , source_pos , source_size , tint ) ;
}
void olc : : ViewPort : : DrawRotatedDecal ( const vf2d & pos ,
Decal * decal ,
const float fAngle ,
const vf2d & center ,
const vf2d & scale ,
const Pixel & tint ) const {
auto sin = std : : sin ( fAngle ) ;
auto cos = std : : cos ( fAngle ) ;
std : : vector < vf2d > points {
- center * scale ,
olc : : vf2d { - center . x , decal - > sprite - > height - center . y } * scale ,
olc : : vf2d { decal - > sprite - > width - center . x ,
decal - > sprite - > height - center . y }
* scale ,
olc : : vf2d { decal - > sprite - > width - center . x , - center . y } * scale ,
} ;
for ( auto i = 0u ; i < points . size ( ) ; i + + ) {
points [ i ] = pos
+ olc : : vf2d { points [ i ] . x * cos - points [ i ] . y * sin ,
points [ i ] . x * sin + points [ i ] . y * cos } ;
}
DrawWarpedDecal ( decal , points . data ( ) , tint ) ;
}
void olc : : ViewPort : : DrawPartialRotatedDecal ( const vf2d & pos ,
Decal * decal ,
const float fAngle ,
const vf2d & center ,
const vf2d & source_pos ,
const vf2d & source_size ,
const vf2d & scale ,
const Pixel & tint ) const {
auto sin = std : : sin ( fAngle ) ;
auto cos = std : : cos ( fAngle ) ;
std : : vector < vf2d > points {
- center * scale ,
olc : : vf2d { - center . x , source_size . y - center . y } * scale ,
( source_size - center ) * scale ,
olc : : vf2d { source_size . x - center . x , - center . y } * scale ,
} ;
for ( auto i = 0u ; i < points . size ( ) ; i + + ) {
points [ i ] = pos
+ olc : : vf2d { points [ i ] . x * cos - points [ i ] . y * sin ,
points [ i ] . x * sin + points [ i ] . y * cos } ;
}
DrawPartialWarpedDecal ( decal , points . data ( ) , source_pos , source_size , tint ) ;
}
void olc : : ViewPort : : FillRectDecal ( const vf2d & pos ,
const vf2d & size ,
const Pixel col ) const {
std : : vector < vf2d > points {
pos ,
{ pos . x , pos . y + size . y } ,
pos + size ,
{ pos . x + size . x , pos . y } ,
} ;
std : : vector < vf2d > uvs {
{ 0 , 0 } ,
{ 0 , 1 } ,
{ 1 , 1 } ,
{ 1 , 0 } ,
} ;
DrawPolygonDecal ( nullptr , points , uvs , col ) ;
}
void olc : : ViewPort : : GradientFillRectDecal ( const vf2d & pos ,
const vf2d & size ,
const Pixel colTL ,
const Pixel colBL ,
const Pixel colBR ,
const Pixel colTR ) const {
std : : vector < vf2d > points {
pos ,
{ pos . x , pos . y + size . y } ,
pos + size ,
{ pos . x + size . x , pos . y } ,
} ;
std : : vector < vf2d > uvs {
{ 0 , 0 } ,
{ 0 , 1 } ,
{ 1 , 1 } ,
{ 1 , 0 } ,
} ;
std : : vector < Pixel > colors {
colTL ,
colBL ,
colBR ,
colTR ,
} ;
std : : vector < float > w { 1 , 1 , 1 , 1 } ;
drawClippedDecal ( nullptr , points . data ( ) , uvs . data ( ) , colors . data ( ) , w . data ( ) , points . size ( ) ) ;
}
void olc : : ViewPort : : DrawPolygonDecal ( Decal * decal ,
const std : : vector < vf2d > & pos ,
const std : : vector < vf2d > & uv ,
const Pixel tint ) const {
std : : vector < Pixel > colors ;
colors . resize ( pos . size ( ) ) ;
for ( auto i = 0u ; i < colors . size ( ) ; i + + ) {
colors [ i ] = tint ;
}
std : : vector < float > w { 1 , 1 , 1 , 1 } ;
drawClippedDecal ( decal , pos . data ( ) , uv . data ( ) , colors . data ( ) , w . data ( ) , pos . size ( ) ) ;
}
void olc : : ViewPort : : DrawPolygonDecal ( Decal * decal ,
const std : : vector < vf2d > & pos ,
const std : : vector < float > & ,
const std : : vector < vf2d > & uv ,
const Pixel tint ) const {
DrawPolygonDecal ( decal , pos , uv , tint ) ;
}
void olc : : ViewPort : : DrawPolygonDecal ( Decal * decal ,
const std : : vector < vf2d > & pos ,
const std : : vector < vf2d > & uv ,
const std : : vector < Pixel > & tint ) const {
std : : vector < float > w { 1 , 1 , 1 , 1 } ;
drawClippedDecal ( decal , pos . data ( ) , uv . data ( ) , tint . data ( ) , w . data ( ) , pos . size ( ) ) ;
}
void olc : : ViewPort : : DrawLineDecal ( const vf2d & pos1 ,
const vf2d & pos2 ,
Pixel p ) const {
vf2d posA = pos1 ;
vf2d posB = pos2 ;
for ( auto i = 0u ; i < clipVertices . size ( ) ; i + + ) {
auto clipA = clipVertices [ i ] ;
auto clipB = clipVertices [ ( i + 1 ) % clipVertices . size ( ) ] ;
auto intersection = lineSegmentIntersect ( clipA , clipB , posA , posB ) ;
if ( intersection < 0 | | intersection > 1 ) {
continue ;
}
auto clipDirection = directionFromLine ( clipA , clipB , posA ) ;
auto intersectionPoint = posA + ( posB - posA ) * intersection ;
if ( clipDirection > = 0 ) {
posA = intersectionPoint ;
} else {
posB = intersectionPoint ;
}
}
pge - > DrawLineDecal ( posA + offset , posB + offset , p ) ;
}
void olc : : ViewPort : : drawClippedDecal ( Decal * decal ,
const vf2d * points ,
const vf2d * uvs ,
const Pixel * col ,
const float * ws ,
uint32_t elements ) const {
std : : vector < vf2d > outputList { points , points + elements } ;
std : : vector < vf2d > outputUvs { uvs , uvs + elements } ;
std : : vector < float > outputWs { ws , ws + elements } ;
std : : vector < Pixel > outputCols { col , col + elements } ;
vf2d min = { std : : numeric_limits < float > : : max ( ) , std : : numeric_limits < float > : : max ( ) } , max ;
bool pointsOutside = false ;
if ( rect ! = geom2d : : rect < float > { } ) {
for ( vf2d & points : outputList ) {
if ( ! geom2d : : contains ( rect , points ) ) {
pointsOutside = true ;
break ;
}
}
} else { pointsOutside = true ; }
if ( ! pointsOutside ) goto render ;
for ( auto i = 0u ; i < clipVertices . size ( ) ; i + + ) {
auto clipA = clipVertices [ i ] ;
auto clipB = clipVertices [ ( i + 1 ) % clipVertices . size ( ) ] ;
auto inputList { outputList } ;
auto inputUvs { outputUvs } ;
auto inputWs { outputWs } ;
auto inputCols { outputCols } ;
outputList . clear ( ) ;
outputUvs . clear ( ) ;
outputWs . clear ( ) ;
outputCols . clear ( ) ;
for ( auto i = 0u ; i < inputList . size ( ) ; i + + ) {
auto polygonA = inputList [ i ] ;
auto polygonB = inputList [ ( i + 1 ) % inputList . size ( ) ] ;
auto uvA = inputUvs [ i ] ;
auto uvB = inputUvs [ ( i + 1 ) % inputList . size ( ) ] ;
auto Wa = inputWs [ i ] ;
auto Wb = inputWs [ ( i + 1 ) % inputList . size ( ) ] ;
auto colA = inputCols [ i ] ;
auto colB = inputCols [ ( i + 1 ) % inputList . size ( ) ] ;
auto intersection =
lineSegmentIntersect ( clipA , clipB , polygonA , polygonB ) ;
auto intersectionPoint =
polygonA + ( polygonB - polygonA ) * intersection ;
auto intersectionUv = uvA + ( uvB - uvA ) * intersection ;
auto intersectionW = Wa + ( Wb - Wa ) * intersection ;
auto intersectionCol = PixelLerp ( colA , colB , intersection ) ;
float aDirection = directionFromLine ( clipA , clipB , polygonA ) ;
float bDirection = directionFromLine ( clipA , clipB , polygonB ) ;
if ( bDirection < = 0 ) {
if ( aDirection > 0 ) {
outputList . push_back ( intersectionPoint ) ;
outputUvs . push_back ( intersectionUv ) ;
outputWs . push_back ( intersectionW ) ;
outputCols . push_back ( intersectionCol ) ;
}
outputList . push_back ( polygonB ) ;
outputUvs . push_back ( uvB ) ;
outputWs . push_back ( Wb ) ;
outputCols . push_back ( colB ) ;
} else if ( aDirection < = 0 ) {
outputList . push_back ( intersectionPoint ) ;
outputUvs . push_back ( intersectionUv ) ;
outputWs . push_back ( intersectionW ) ;
outputCols . push_back ( intersectionCol ) ;
}
}
}
if ( outputList . size ( ) = = 0 ) {
return ;
}
render :
for ( auto & point : outputList ) {
point + = offset ;
}
pge - > DrawExplicitDecal ( decal ,
outputList . data ( ) ,
outputUvs . data ( ) ,
outputCols . data ( ) ,
outputWs . data ( ) ,
outputList . size ( ) ) ;
}
float olc : : ViewPort : : lineSegmentIntersect ( vf2d lineA ,
vf2d lineB ,
vf2d segmentA ,
vf2d segmentB ) {
return ( ( lineA . x - segmentA . x ) * ( lineA . y - lineB . y )
- ( lineA . y - segmentA . y ) * ( lineA . x - lineB . x ) )
/ ( ( lineA . x - lineB . x ) * ( segmentA . y - segmentB . y )
- ( lineA . y - lineB . y ) * ( segmentA . x - segmentB . x ) ) ;
}
float olc : : ViewPort : : directionFromLine ( vf2d lineA , vf2d lineB , vf2d point ) {
return ( lineB . x - lineA . x ) * ( point . y - lineA . y )
- ( point . x - lineA . x ) * ( lineB . y - lineA . y ) ;
}
void olc : : ViewPort : : DrawStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const olc : : vf2d & scale , const float width , const bool disableDynamicScaling ) {
if ( sText . length ( ) = = 0 ) return ;
std : : string originalKey { pge - > stripCol ( sText ) } ;
std : : string renderStr { pge - > stripLeadingCol ( sText ) } ;
std : : string key { " DSD_ " + originalKey } ;
key + = std : : to_string ( width ) ;
if ( ! disableDynamicScaling ) {
key + = scale . str ( ) ;
}
const bool RerenderRequired = pge - > garbageCollector . count ( key ) & & pge - > garbageCollector [ key ] . originalStr ! = renderStr ;
const bool ShadowRerenderRequired = pge - > garbageCollector . count ( key + " _SHADOW " ) & & pge - > garbageCollector [ key + " _SHADOW " ] . originalStr ! = renderStr ;
if ( ! pge - > garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
vf2d imageSize = pge - > GetWrappedTextSize ( originalKey , width , scale ) ;
if ( imageSize . x < 1 | | imageSize . y < 1 ) return ;
Decal * newDecal = nullptr ;
if ( ! RerenderRequired ) {
newDecal = new Decal ( new Sprite ( imageSize . x / scale . x , imageSize . y / scale . y ) ) ;
pge - > garbageCollector [ key ] . decal = newDecal ;
} else {
newDecal = pge - > garbageCollector [ key ] . decal ;
}
pge - > garbageCollector [ key ] . originalStr = originalKey ;
pge - > SetDrawTarget ( newDecal - > sprite ) ;
pge - > Clear ( BLANK ) ;
pge - > DrawString ( { 0 , 0 } , renderStr , WHITE , 1U , width / scale . x ) ;
pge - > SetDrawTarget ( nullptr ) ;
newDecal - > Update ( ) ;
}
pge - > garbageCollector [ key ] . expireTime = pge - > GetRunTime ( ) + 120.0f ;
DrawDecal ( pos , pge - > garbageCollector [ key ] . decal , scale , pge - > GetFinalRenderColor ( col , sText ) ) ;
}
void olc : : ViewPort : : DrawStringDecal ( Font & font , const olc : : vf2d & pos , const std : : u32string & sText , const Pixel col , const olc : : vf2d & scale ) {
if ( sText . length ( ) = = 0 ) return ;
std : : u32string originalKey { pge - > stripCol ( sText ) } ;
std : : u32string renderStr { pge - > stripLeadingCol ( sText ) } ;
std : : u32string Ukey = U " DSD_ " + font . GetFontName ( ) + U " _ " + originalKey ;
std : : string key = std : : string ( Ukey . begin ( ) , Ukey . end ( ) ) ;
const bool RerenderRequired = pge - > garbageCollector . count ( key ) & & pge - > garbageCollector [ key ] . originalStr ! = std : : string ( renderStr . begin ( ) , renderStr . end ( ) ) ;
if ( ! pge - > garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete pge - > garbageCollector [ key ] . decal ;
pge - > garbageCollector [ key ] . decal = font . RenderStringToDecal ( renderStr , WHITE ) ;
pge - > garbageCollector [ key ] . originalStr = std : : string ( originalKey . begin ( ) , originalKey . end ( ) ) ;
}
pge - > garbageCollector [ key ] . expireTime = pge - > GetRunTime ( ) + 120.0f ;
DrawDecal ( pos , pge - > garbageCollector [ key ] . decal , scale / 4 , pge - > GetFinalRenderColor ( col , sText ) ) ;
}
void olc : : ViewPort : : DrawStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const olc : : vf2d & scale , const float width , const bool disableDynamicScaling ) {
if ( sText . length ( ) = = 0 ) return ;
std : : string originalKey { pge - > stripCol ( sText ) } ;
std : : string renderStr { pge - > stripLeadingCol ( sText ) } ;
std : : string key { " DSPD " + originalKey } ;
key + = std : : to_string ( width ) ;
if ( ! disableDynamicScaling ) {
key + = scale . str ( ) ;
}
const bool RerenderRequired = pge - > garbageCollector . count ( key ) & & pge - > garbageCollector [ key ] . originalStr ! = renderStr ;
const bool ShadowRerenderRequired = pge - > garbageCollector . count ( key + " _SHADOW " ) & & pge - > garbageCollector [ key + " _SHADOW " ] . originalStr ! = renderStr ;
if ( ! pge - > garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
vf2d imageSize = pge - > GetWrappedTextSizeProp ( originalKey , width , scale ) ;
if ( imageSize . x < 1 | | imageSize . y < 1 ) return ;
Decal * newDecal = nullptr ;
if ( ! RerenderRequired ) {
newDecal = new Decal ( new Sprite ( imageSize . x / scale . x , imageSize . y / scale . y ) ) ;
pge - > garbageCollector [ key ] . decal = newDecal ;
} else {
newDecal = pge - > garbageCollector [ key ] . decal ;
}
pge - > garbageCollector [ key ] . originalStr = originalKey ;
pge - > SetDrawTarget ( newDecal - > sprite ) ;
pge - > Clear ( BLANK ) ;
pge - > DrawStringProp ( { 0 , 0 } , renderStr , WHITE , 1U , width / scale . x ) ;
pge - > SetDrawTarget ( nullptr ) ;
newDecal - > Update ( ) ;
}
pge - > garbageCollector [ key ] . expireTime = pge - > GetRunTime ( ) + 120.0f ;
DrawDecal ( pos , pge - > garbageCollector [ key ] . decal , scale , pge - > GetFinalRenderColor ( col , sText ) ) ;
}
void olc : : ViewPort : : DrawShadowStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale , const float width , const float shadowSizeFactor , const bool disableDynamicScaling ) {
if ( sText . length ( ) = = 0 ) return ;
std : : string originalKey { pge - > stripCol ( sText ) } ;
std : : string renderStr { pge - > stripLeadingCol ( sText ) } ;
std : : string key { " DSSD_ " + originalKey } ;
key + = std : : to_string ( width ) ;
if ( ! disableDynamicScaling ) {
key + = scale . str ( ) ;
}
const bool RerenderRequired = pge - > garbageCollector . count ( key ) & & pge - > garbageCollector [ key ] . originalStr ! = renderStr ;
const bool ShadowRerenderRequired = pge - > garbageCollector . count ( key + " _SHADOW " ) & & pge - > garbageCollector [ key + " _SHADOW " ] . originalStr ! = renderStr ;
if ( ! pge - > garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
vf2d imageSize = pge - > GetWrappedTextSize ( originalKey , width , scale ) ;
if ( imageSize . x < 1 | | imageSize . y < 1 ) return ;
Decal * newDecal = nullptr ;
if ( ! RerenderRequired ) {
newDecal = new Decal ( new Sprite ( imageSize . x / scale . x , imageSize . y / scale . y ) ) ;
pge - > garbageCollector [ key ] . decal = newDecal ;
} else {
newDecal = pge - > garbageCollector [ key ] . decal ;
}
pge - > garbageCollector [ key ] . originalStr = originalKey ;
pge - > SetDrawTarget ( newDecal - > sprite ) ;
pge - > Clear ( BLANK ) ;
pge - > DrawString ( { 0 , 0 } , renderStr , WHITE , 1U , width / scale . x ) ;
newDecal - > Update ( ) ;
vf2d adjustedShadowSizeFactor = vf2d { shadowSizeFactor , shadowSizeFactor } * 4 / scale ;
Decal * newShadowDecal = nullptr ;
if ( ! ShadowRerenderRequired ) {
newShadowDecal = new Decal ( new Sprite ( ( imageSize . x / scale . x * 4 ) + adjustedShadowSizeFactor . x * 2 , ( imageSize . y / scale . y * 4 ) + adjustedShadowSizeFactor . y * 2 ) ) ;
pge - > garbageCollector [ key + " _SHADOW " ] . decal = newShadowDecal ;
} else {
newShadowDecal = pge - > garbageCollector [ key + " _SHADOW " ] . decal ;
}
pge - > SetDrawTarget ( newShadowDecal - > sprite ) ;
pge - > Clear ( BLANK ) ;
for ( float y = - adjustedShadowSizeFactor . y ; y < = adjustedShadowSizeFactor . y + 0.1 ; y + = adjustedShadowSizeFactor . y / 2 ) {
for ( float x = - adjustedShadowSizeFactor . x ; x < = adjustedShadowSizeFactor . x + 0.1 ; x + = adjustedShadowSizeFactor . x / 2 ) {
if ( x ! = 0 | | y ! = 0 ) {
pge - > DrawString ( vf2d { x , y } + adjustedShadowSizeFactor , renderStr , WHITE , 4U , width / scale . x * 4 ) ;
}
}
}
pge - > SetDrawTarget ( nullptr ) ;
newShadowDecal - > Update ( ) ;
}
pge - > garbageCollector [ key ] . expireTime = pge - > GetRunTime ( ) + 120.0f ;
pge - > garbageCollector [ key + " _SHADOW " ] . expireTime = pge - > GetRunTime ( ) + 120.0f ;
DrawDecal ( pos - vf2d { shadowSizeFactor , shadowSizeFactor } , pge - > garbageCollector [ key + " _SHADOW " ] . decal , scale / 4 , shadowCol ) ;
DrawDecal ( pos , pge - > garbageCollector [ key ] . decal , scale , pge - > GetFinalRenderColor ( col , sText ) ) ;
}
void olc : : ViewPort : : DrawShadowStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale , const float width , const float shadowSizeFactor , const bool disableDynamicScaling ) {
if ( sText . length ( ) = = 0 ) return ;
std : : string originalKey { pge - > stripCol ( sText ) } ;
std : : string renderStr { pge - > stripLeadingCol ( sText ) } ;
std : : string key { " DSSPD " + originalKey } ;
key + = std : : to_string ( width ) ;
if ( ! disableDynamicScaling ) {
key + = scale . str ( ) ;
}
const bool RerenderRequired = pge - > garbageCollector . count ( key ) & & pge - > garbageCollector [ key ] . originalStr ! = renderStr ;
const bool ShadowRerenderRequired = pge - > garbageCollector . count ( key + " _SHADOW " ) & & pge - > garbageCollector [ key + " _SHADOW " ] . originalStr ! = renderStr ;
if ( ! pge - > garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
vf2d imageSize = pge - > GetWrappedTextSizeProp ( originalKey , width , scale ) ;
if ( imageSize . x < 1 | | imageSize . y < 1 ) return ;
Decal * newDecal = nullptr ;
if ( ! RerenderRequired ) {
newDecal = new Decal ( new Sprite ( imageSize . x / scale . x , imageSize . y / scale . y ) ) ;
pge - > garbageCollector [ key ] . decal = newDecal ;
} else {
newDecal = pge - > garbageCollector [ key ] . decal ;
}
pge - > garbageCollector [ key ] . originalStr = originalKey ;
pge - > SetDrawTarget ( newDecal - > sprite ) ;
pge - > Clear ( BLANK ) ;
pge - > DrawStringProp ( { 0 , 0 } , renderStr , WHITE , 1U , width / scale . x ) ;
newDecal - > Update ( ) ;
vf2d adjustedShadowSizeFactor = vf2d { shadowSizeFactor , shadowSizeFactor } * 4 / scale ;
Decal * newShadowDecal = nullptr ;
if ( ! ShadowRerenderRequired ) {
newShadowDecal = new Decal ( new Sprite ( ( imageSize . x / scale . x * 4 ) + adjustedShadowSizeFactor . x * 2 , ( imageSize . y / scale . y * 4 ) + adjustedShadowSizeFactor . y * 2 ) ) ;
pge - > garbageCollector [ key + " _SHADOW " ] . decal = newShadowDecal ;
} else {
newShadowDecal = pge - > garbageCollector [ key + " _SHADOW " ] . decal ;
}
pge - > SetDrawTarget ( newShadowDecal - > sprite ) ;
pge - > Clear ( BLANK ) ;
for ( float y = - adjustedShadowSizeFactor . y ; y < = adjustedShadowSizeFactor . y + 0.1 ; y + = adjustedShadowSizeFactor . y / 2 ) {
for ( float x = - adjustedShadowSizeFactor . x ; x < = adjustedShadowSizeFactor . x + 0.1 ; x + = adjustedShadowSizeFactor . x / 2 ) {
if ( x ! = 0 | | y ! = 0 ) {
pge - > DrawStringProp ( vf2d { x , y } + adjustedShadowSizeFactor , renderStr , WHITE , 4U , width / scale . x * 4 ) ;
}
}
}
pge - > SetDrawTarget ( nullptr ) ;
newShadowDecal - > Update ( ) ;
}
pge - > garbageCollector [ key ] . expireTime = pge - > GetRunTime ( ) + 120.0f ;
pge - > garbageCollector [ key + " _SHADOW " ] . expireTime = pge - > GetRunTime ( ) + 120.0f ;
DrawDecal ( pos - vf2d { shadowSizeFactor , shadowSizeFactor } , pge - > garbageCollector [ key + " _SHADOW " ] . decal , scale / 4 , shadowCol ) ;
DrawDecal ( pos , pge - > garbageCollector [ key ] . decal , scale , pge - > GetFinalRenderColor ( col , sText ) ) ;
}
void olc : : ViewPort : : DrawShadowStringDecal ( Font & font , const olc : : vf2d & pos , const std : : u32string & sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale , const float shadowSizeFactor ) {
if ( sText . length ( ) = = 0 ) return ;
std : : u32string originalKey { pge - > stripCol ( sText ) } ;
std : : u32string renderStr { pge - > stripLeadingCol ( sText ) } ;
std : : u32string Ukey = U " DSSD_ " + font . GetFontName ( ) + U " _ " + originalKey ;
std : : string key = std : : string ( Ukey . begin ( ) , Ukey . end ( ) ) ;
const bool RerenderRequired = pge - > garbageCollector . count ( key ) & & pge - > garbageCollector [ key ] . originalStr ! = std : : string ( renderStr . begin ( ) , renderStr . end ( ) ) ;
if ( ! pge - > garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete pge - > garbageCollector [ key ] . decal ;
pge - > garbageCollector [ key ] . decal = font . RenderStringToDecal ( renderStr , WHITE ) ;
pge - > garbageCollector [ key ] . originalStr = std : : string ( originalKey . begin ( ) , originalKey . end ( ) ) ;
}
pge - > garbageCollector [ key ] . expireTime = pge - > GetRunTime ( ) + 120.0f ;
std : : erase_if ( pge - > garbageCollector , [ & ] ( auto & key ) {
if ( key . second . expireTime < pge - > GetRunTime ( ) ) {
delete key . second . decal ;
return true ;
}
return false ;
} ) ;
for ( float y = - shadowSizeFactor ; y < = shadowSizeFactor + 0.1 ; y + = shadowSizeFactor / 2 ) {
for ( float x = - shadowSizeFactor ; x < = shadowSizeFactor + 0.1 ; x + = shadowSizeFactor / 2 ) {
if ( x ! = 0 | | y ! = 0 ) {
DrawDecal ( pos + vf2d { x , y } , pge - > garbageCollector [ key ] . decal , scale / 4 , shadowCol ) ;
}
}
}
DrawDecal ( pos , pge - > garbageCollector [ key ] . decal , scale / 4 , pge - > GetFinalRenderColor ( col , sText ) ) ;
}
void olc : : ViewPort : : DrawDropShadowStringDecal ( Font & font , const olc : : vf2d & pos , const std : : u32string & sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale ) {
if ( sText . length ( ) = = 0 ) return ;
std : : u32string originalKey { pge - > stripCol ( sText ) } ;
std : : u32string renderStr { pge - > stripLeadingCol ( sText ) } ;
std : : u32string Ukey = U " DDSSD_ " + font . GetFontName ( ) + U " _ " + originalKey ;
std : : string key = std : : string ( Ukey . begin ( ) , Ukey . end ( ) ) ;
const bool RerenderRequired = pge - > garbageCollector . count ( key ) & & pge - > garbageCollector [ key ] . originalStr ! = std : : string ( renderStr . begin ( ) , renderStr . end ( ) ) ;
if ( ! pge - > garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete pge - > garbageCollector [ key ] . decal ;
pge - > garbageCollector [ key ] . decal = font . RenderStringToDecal ( renderStr , WHITE ) ;
pge - > garbageCollector [ key ] . originalStr = std : : string ( originalKey . begin ( ) , originalKey . end ( ) ) ;
}
pge - > garbageCollector [ key ] . expireTime = pge - > GetRunTime ( ) + 120.0f ;
DrawDecal ( pos + vf2d { 0 , 0.5f } , pge - > garbageCollector [ key ] . decal , scale / 4 , shadowCol ) ;
DrawDecal ( pos + vf2d { 0.5f , 0 } , pge - > garbageCollector [ key ] . decal , scale / 4 , shadowCol ) ;
DrawDecal ( pos + vf2d { 0.5f , 0.5f } , pge - > garbageCollector [ key ] . decal , scale / 4 , shadowCol ) ;
DrawDecal ( pos , pge - > garbageCollector [ key ] . decal , scale / 4 , pge - > GetFinalRenderColor ( col , sText ) ) ;
}
void olc : : ViewPort : : DrawRectDecal ( const olc : : vf2d & pos , const olc : : vf2d & size , const olc : : Pixel col ) const {
FillRectDecal ( pos , { size . x + 1 , 1 } , col ) ;
FillRectDecal ( pos + vf2d { 0 , size . y - 1 + 1 } , { size . x + 1 , 1 } , col ) ;
FillRectDecal ( pos + vf2d { 0 , 1 } , { 1 , size . y - 1 * 2 + 1 } , col ) ;
FillRectDecal ( pos + vf2d { size . x - 1 + 1 , 1 } , { 1 , size . y - 1 * 2 + 1 } , col ) ;
}
# endif