Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML 2.X] Narzędzie Lasso z użyciem sf::ConvexShape

Ostatnio zmodyfikowano wczoraj o godz. 19:55
Autor Wiadomość
tBane
Temat założony przez niniejszego użytkownika
[SFML 2.X] Narzędzie Lasso z użyciem sf::ConvexShape
» 2025-08-28 14:14:03
Witam. Próbuję napisać narzędzie Lasso. Narzędzie to ma działać w taki sposób, że można zaznaczać przy jego użyciu dowolny kształt poprzez przesuwanie kursora z naciśniętym lewym przyciskiem myszy. Narzędzie działa poprawnie dla większej ilości punktów, ale przy małej ilości punktów się wykrzacza. Dlaczego ?

zarysowany cały kształt


tu także więc jest ok


a tu dopiero co zacząłem zaznaczać w lewo w dół i się wykrzaczyło


C/C++
#ifndef Lasso_hpp
#define Lasso_hpp

enum class LassoState { None, Selecting, Selected, Moving };

class Lasso {
public:
   
LassoState state;
   
std::vector < sf::Vector2f > points;
   
sf::ConvexShape outline;
   
   
Lasso() {
       
state = LassoState::None;
       
points.clear();
       
outline = sf::ConvexShape();
       
outline.setOutlineColor( sf::Color::Cyan );
       
outline.setOutlineThickness( 2.0f );
       
outline.setFillColor( sf::Color::Transparent );
   
}
   
   
~Lasso() { }
   
   
void addPoint( sf::Vector2f p ) {
       
if( points.empty() || sqrt( pow( points.back().x - p.x, 2 ) + pow( points.back().y - p.y, 2 ) ) > 4.0f ) {
           
points.push_back( p );
           
outline.setPointCount( points.size() );
           
outline.setPoint( points.size() - 1, p );
       
}
    }
   
   
void draw() {
       
if( outline.getPointCount() > 2 )
           
 window->draw( outline );
       
else {
           
        }
    }
}
;

Lasso * lasso = nullptr;
#endif
P-182918
DejaVu
» 2025-08-28 16:01:50
A czy przeczytałeś czy dozwolone jest tworzenie kształtu, gdzie 'obwódka' rysowana się przecina sama ze sobą? np. jeżeli liczbę 8 chciałbyś narysować.
P-182919
tBane
Temat założony przez niniejszego użytkownika
» 2025-08-28 16:13:34
No właśnie nie wiem, ale wtedy też się wykrzacza. Czyli co, zastąpić sf::ConvexShape? Tylko czym?

P-182920
tBane
Temat założony przez niniejszego użytkownika
» 2025-08-28 18:36:06
Dobra. Zrobię to z użyciem sf::VertexArray(sf::LineStrip);. Pomęczę ChatGPT z shaderami na obwódkę (właśnie dlatego chciałem użyć sf::ConvexShape) i dam znać czy się udało
P-182921
tBane
Temat założony przez niniejszego użytkownika
» 2025-08-28 19:00:30
Mam taki kod. Ale czasem przesuwa krawędzie ..

C/C++
#include <SFML/Graphics.hpp>
#include <vector>
#include <cmath>

std::string lasso_image_width_outline_source = R"( uniform sampler2D texture; uniform sampler2D mask; uniform vec2 maskSize; uniform float outlineSize; uniform vec4 outlineColor; void main() { vec2 uv = gl_TexCoord[0].xy; vec2 muv = uv; muv.y = 1.0 - muv.y; vec4 base = texture2D(texture, uv); float aCenter = texture2D(mask, muv).a; vec2 texel = 1.0 / maskSize; int r = int(floor(outlineSize + 0.5)); float aMax = 0.0; float aMin = 1.0; for (int j = -r; j <= r; ++j) { for (int i = -r; i <= r; ++i) { vec2 off = vec2(float(i), float(j)) * texel; float s = texture2D(mask, muv + off).a; aMax = max(aMax, s); aMin = min(aMin, s); } } float dilated = step(0.5, aMax); float eroded = step(0.5, aMin); float edge = clamp(dilated - eroded, 0.0, 1.0); vec4 masked = vec4(base.rgb, base.a * aCenter); vec3 outRGB = mix(masked.rgb, outlineColor.rgb, edge * outlineColor.a); float outA = max(masked.a, edge * outlineColor.a); gl_FragColor = vec4(outRGB, outA); } )";

std::string lasso_only_outline_source = R"( uniform sampler2D mask; uniform vec2 maskSize; uniform float outlineSize; uniform vec4 outlineColor; void main() { vec2 uv = gl_TexCoord[0].xy; float aCenter = texture2D(mask, uv).a; vec2 texel = 1.0 / maskSize; int r = int(floor(outlineSize + 0.5)); float aMax = 0.0; float aMin = 1.0; for (int j = -r; j <= r; ++j) { for (int i = -r; i <= r; ++i) { vec2 off = vec2(float(i), float(j)) * texel; float s = texture2D(mask, uv + off).a; aMax = max(aMax, s); aMin = min(aMin, s); } } float dilated = step(0.5, aMax); float eroded = step(0.5, aMin); float edge = clamp(dilated - eroded, 0.0, 1.0); gl_FragColor = vec4(outlineColor.rgb, outlineColor.a * edge); } )";

sf::RenderWindow * window;


// background
sf::Texture texture;
sf::Sprite sprite;

// mask
sf::RenderTexture mask;
sf::Sprite maskSprite;

sf::Shader shader1;
sf::Shader shader2;

std::vector < sf::Vector2f > points;
sf::VertexArray outline;

bool pointInPolygon( std::vector < sf::Vector2f > & points, sf::Vector2f p ) {
   
bool inside = false;
   
for( int i = 0, j = points.size() - 1; i < points.size(); j = i++ ) {
       
sf::Vector2f A = points[ i ];
       
sf::Vector2f B = points[ j ];
       
bool inter =(( A.y > p.y ) !=( B.y > p.y ) ) &&( p.x <( B.x - A.x ) *( p.y - A.y ) /( B.y - A.y ) + A.x );
       
       
if( inter )
           
 inside = !inside;
       
   
}
   
return inside;
}

void pushPoint( sf::Vector2f p, std::vector < sf::Vector2f > & points, sf::VertexArray & outline ) {
   
if( points.empty() || std::hypot( points.back().x - p.x, points.back().y - p.y ) > 2.0f ) {
       
points.push_back( p );
       
outline.append( sf::Vertex( p, sf::Color::Cyan ) );
   
}
}

void generateImage( std::vector < sf::Vector2f > points ) {
   
if( points.size() < 3 ) return;
   
   
sf::Vector2f centroid( 0.f, 0.f );
   
   
for( auto & p: points )
       
 centroid += p;
   
   
centroid.x /= points.size();
   
centroid.y /= points.size();
   
   
sf::VertexArray fan( sf::TriangleFan );
   
fan.append( sf::Vertex( centroid, sf::Color::White ) );
   
   
for( auto & p: points )
       
 fan.append( sf::Vertex( p, sf::Color::White ) );
   
   
fan.append( sf::Vertex( points.front(), sf::Color::White ) ); // zamknięcie
   
   
mask.clear( sf::Color::Transparent );
   
mask.draw( fan, sf::RenderStates( sf::BlendNone ) );
   
mask.display();
   
   
shader1.setUniform( "mask", mask.getTexture() );
}

void generateOutline( std::vector < sf::Vector2f > points ) {
   
if( points.size() < 3 ) return;
   
   
sf::Vector2f centroid( 0.f, 0.f );
   
   
for( auto & p: points )
       
 centroid += p;
   
   
centroid.x /= points.size();
   
centroid.y /= points.size();
   
   
sf::VertexArray fan( sf::TrianglesFan );
   
fan.append( sf::Vertex( centroid, sf::Color::White ) );
   
   
for( auto & p: points )
       
 fan.append( sf::Vertex( p, sf::Color::White ) );
   
   
fan.append( sf::Vertex( points.front(), sf::Color::White ) ); // zamknięcie
   
   
mask.clear( sf::Color::Transparent );
   
mask.draw( fan, sf::RenderStates( sf::BlendNone ) );
   
mask.display();
   
   
shader2.setUniform( "mask", mask.getTexture() );
}

int main() {
   
   
window = new sf::RenderWindow( sf::VideoMode( 1000, 600 ), "Lasso" );
   
   
texture.loadFromFile( "image.png" );
   
sprite = sf::Sprite( texture );
   
   
mask.create( texture.getSize().x, texture.getSize().y );
   
mask.clear( sf::Color::Transparent );
   
mask.display();
   
   
maskSprite = sf::Sprite( mask.getTexture() );
   
   
shader1.loadFromMemory( lasso_image_width_outline_source, sf::Shader::Fragment );
   
shader1.setUniform( "texture", texture );
   
shader1.setUniform( "mask", mask.getTexture() );
   
shader1.setUniform( "maskSize", sf::Glsl::Vec2( mask.getSize().x, mask.getSize().y ) );
   
shader1.setUniform( "outlineSize", 1.f );
   
shader1.setUniform( "outlineColor", sf::Glsl::Vec4( 0, 1, 1, 1 ) ); // cyan
   
   
shader2.loadFromMemory( lasso_only_outline_source, sf::Shader::Fragment );
   
shader2.setUniform( "maskSize", sf::Glsl::Vec2( mask.getSize().x, mask.getSize().y ) );
   
shader2.setUniform( "outlineSize", 1.f );
   
shader2.setUniform( "outlineColor", sf::Glsl::Vec4( 0, 1, 1, 1 ) ); // cyan
   
   
   
points.clear();
   
bool drawing = false;
   
outline = sf::VertexArray( sf::LineStrip );
   
   
while( window->isOpen() ) {
       
       
sf::Vector2i mousePosition = sf::Mouse::getPosition( * window );
       
sf::Vector2f worldMousePosition = window->mapPixelToCoords( mousePosition );
       
       
sf::Event ev;
       
while( window->pollEvent( ev ) ) {
           
if( ev.type == sf::Event::Closed ) window->close();
           
           
if( ev.type == sf::Event::MouseButtonPressed && ev.mouseButton.button == sf::Mouse::Left ) {
               
drawing = true;
               
points.clear();
               
outline.clear();
               
pushPoint( worldMousePosition, points, outline );
           
}
           
           
if( ev.type == sf::Event::MouseMoved && drawing ) {
               
pushPoint( worldMousePosition, points, outline );
               
generateOutline( points );
           
}
           
           
if( ev.type == sf::Event::MouseButtonReleased && ev.mouseButton.button == sf::Mouse::Left ) {
               
drawing = false;
               
generateImage( points );
           
}
           
           
if( ev.type == sf::Event::KeyPressed && ev.key.code == sf::Keyboard::C ) {
               
mask.clear( sf::Color::Transparent );
               
mask.display();
               
shader1.setUniform( "mask", mask.getTexture() );
               
points.clear();
               
outline.clear();
           
}
        }
       
       
window->clear( sf::Color( 30, 30, 34 ) );
       
       
       
if( !drawing && outline.getVertexCount() >= 3 ) {
           
sf::RenderStates rs;
           
rs.shader = & shader1;
           
window->draw( sprite, rs );
       
}
       
       
       
if( drawing && outline.getVertexCount() >= 3 ) {
           
sf::RenderStates rs;
           
rs.shader = & shader2;
           
window->draw( maskSprite, rs );
       
}
       
       
       
window->display();
   
}
   
return 0;
}
P-182922
tBane
Temat założony przez niniejszego użytkownika
» 2025-08-28 19:55:49
Dobra. Udało sie napisać.
Obwódka to
sf::VertexArray fan( sf::LineStrip );

a obraz to
sf::VertexArray fan( sf::TriangleFan );


C/C++
#include <SFML/Graphics.hpp>
#include <vector>
#include <cmath>

std::string lasso_image_width_outline_source = R"( uniform sampler2D texture; uniform sampler2D mask; uniform vec2 maskSize; uniform float outlineSize; uniform vec4 outlineColor; void main() { vec2 uv = gl_TexCoord[0].xy; vec2 muv = uv; muv.y = 1.0 - muv.y; vec4 base = texture2D(texture, uv); float aCenter = texture2D(mask, muv).a; vec2 texel = 1.0 / maskSize; int r = int(floor(outlineSize + 0.5)); float aMax = 0.0; float aMin = 1.0; for (int j = -r; j <= r; ++j) { for (int i = -r; i <= r; ++i) { vec2 off = vec2(float(i), float(j)) * texel; float s = texture2D(mask, muv + off).a; aMax = max(aMax, s); aMin = min(aMin, s); } } float dilated = step(0.5, aMax); float eroded = step(0.5, aMin); float edge = clamp(dilated - eroded, 0.0, 1.0); vec4 masked = vec4(base.rgb, base.a * aCenter); vec3 outRGB = mix(masked.rgb, outlineColor.rgb, edge * outlineColor.a); float outA = max(masked.a, edge * outlineColor.a); gl_FragColor = vec4(outRGB, outA); } )";

std::string lasso_only_outline_source = R"( uniform sampler2D mask; uniform vec2 maskSize; uniform float outlineSize; uniform vec4 outlineColor; void main() { vec2 uv = gl_TexCoord[0].xy; float aCenter = texture2D(mask, uv).a; vec2 texel = 1.0 / maskSize; int r = int(floor(outlineSize + 0.5)); float aMax = 0.0; float aMin = 1.0; for (int j = -r; j <= r; ++j) { for (int i = -r; i <= r; ++i) { vec2 off = vec2(float(i), float(j)) * texel; float s = texture2D(mask, uv + off).a; aMax = max(aMax, s); aMin = min(aMin, s); } } float dilated = step(0.5, aMax); float eroded = step(0.5, aMin); float edge = clamp(dilated - eroded, 0.0, 1.0); gl_FragColor = vec4(outlineColor.rgb, outlineColor.a * edge); } )";

sf::RenderWindow * window;


// background
sf::Texture texture;
sf::Sprite sprite;

// mask
sf::RenderTexture mask;
sf::Sprite maskSprite;

sf::Shader shader1;
sf::Shader shader2;

std::vector < sf::Vector2f > points;
sf::VertexArray outline;

bool pointInPolygon( std::vector < sf::Vector2f > & points, sf::Vector2f p ) {
   
bool inside = false;
   
for( int i = 0, j = points.size() - 1; i < points.size(); j = i++ ) {
       
sf::Vector2f A = points[ i ];
       
sf::Vector2f B = points[ j ];
       
bool inter =(( A.y > p.y ) !=( B.y > p.y ) ) &&( p.x <( B.x - A.x ) *( p.y - A.y ) /( B.y - A.y ) + A.x );
       
       
if( inter )
           
 inside = !inside;
       
   
}
   
return inside;
}

void pushPoint( sf::Vector2f p, std::vector < sf::Vector2f > & points, sf::VertexArray & outline ) {
   
if( points.empty() || std::hypot( points.back().x - p.x, points.back().y - p.y ) > 2.0f ) {
       
points.push_back( p );
       
outline.append( sf::Vertex( p, sf::Color::Cyan ) );
   
}
}

void generateImage( std::vector < sf::Vector2f > points ) {
   
if( points.size() < 3 ) return;
   
   
sf::Vector2f centroid( 0.f, 0.f );
   
   
for( auto & p: points )
       
 centroid += p;
   
   
centroid.x /= points.size();
   
centroid.y /= points.size();
   
   
sf::VertexArray fan( sf::TriangleFan );
   
fan.append( sf::Vertex( centroid, sf::Color::White ) );
   
   
for( auto & p: points )
       
 fan.append( sf::Vertex( p, sf::Color::White ) );
   
   
fan.append( sf::Vertex( points.front(), sf::Color::White ) ); // zamknięcie
   
   
mask.clear( sf::Color::Transparent );
   
mask.draw( fan, sf::RenderStates( sf::BlendNone ) );
   
mask.display();
   
   
shader1.setUniform( "mask", mask.getTexture() );
}

void generateOutline( std::vector < sf::Vector2f > points ) {
   
if( points.size() < 3 ) return;
   
   
sf::VertexArray fan( sf::LineStrip );
   
//fan.append(sf::Vertex(centroid, sf::Color::White));
   
   
for( auto & p: points )
       
 fan.append( sf::Vertex( p, sf::Color::White ) );
   
   
mask.clear( sf::Color::Transparent );
   
mask.draw( fan, sf::RenderStates( sf::BlendNone ) );
   
mask.display();
   
   
shader2.setUniform( "mask", mask.getTexture() );
}

int main() {
   
   
window = new sf::RenderWindow( sf::VideoMode( 1000, 600 ), "Lasso" );
   
   
texture.loadFromFile( "image.png" );
   
sprite = sf::Sprite( texture );
   
   
mask.create( texture.getSize().x, texture.getSize().y );
   
mask.clear( sf::Color::Transparent );
   
mask.display();
   
   
maskSprite = sf::Sprite( mask.getTexture() );
   
   
shader1.loadFromMemory( lasso_image_width_outline_source, sf::Shader::Fragment );
   
shader1.setUniform( "texture", texture );
   
shader1.setUniform( "mask", mask.getTexture() );
   
shader1.setUniform( "maskSize", sf::Glsl::Vec2( mask.getSize().x, mask.getSize().y ) );
   
shader1.setUniform( "outlineSize", 1.f );
   
shader1.setUniform( "outlineColor", sf::Glsl::Vec4( 0, 1, 1, 1 ) ); // cyan
   
   
shader2.loadFromMemory( lasso_only_outline_source, sf::Shader::Fragment );
   
shader2.setUniform( "maskSize", sf::Glsl::Vec2( mask.getSize().x, mask.getSize().y ) );
   
shader2.setUniform( "outlineSize", 1.f );
   
shader2.setUniform( "outlineColor", sf::Glsl::Vec4( 0, 1, 1, 1 ) ); // cyan
   
   
   
points.clear();
   
bool drawing = false;
   
outline = sf::VertexArray( sf::LineStrip );
   
   
while( window->isOpen() ) {
       
       
sf::Vector2i mousePosition = sf::Mouse::getPosition( * window );
       
sf::Vector2f worldMousePosition = window->mapPixelToCoords( mousePosition );
       
       
sf::Event ev;
       
while( window->pollEvent( ev ) ) {
           
if( ev.type == sf::Event::Closed ) window->close();
           
           
if( ev.type == sf::Event::MouseButtonPressed && ev.mouseButton.button == sf::Mouse::Left ) {
               
drawing = true;
               
points.clear();
               
outline.clear();
               
pushPoint( worldMousePosition, points, outline );
           
}
           
           
if( ev.type == sf::Event::MouseMoved && drawing ) {
               
pushPoint( worldMousePosition, points, outline );
               
generateOutline( points );
           
}
           
           
if( ev.type == sf::Event::MouseButtonReleased && ev.mouseButton.button == sf::Mouse::Left ) {
               
drawing = false;
               
generateImage( points );
           
}
           
           
if( ev.type == sf::Event::KeyPressed && ev.key.code == sf::Keyboard::C ) {
               
mask.clear( sf::Color::Transparent );
               
mask.display();
               
shader1.setUniform( "mask", mask.getTexture() );
               
points.clear();
               
outline.clear();
           
}
        }
       
       
window->clear( sf::Color( 30, 30, 34 ) );
       
       
       
if( !drawing && outline.getVertexCount() >= 3 ) {
           
sf::RenderStates rs;
           
rs.shader = & shader1;
           
window->draw( sprite, rs );
       
}
       
       
       
if( drawing && outline.getVertexCount() >= 3 ) {
           
sf::RenderStates rs;
           
rs.shader = & shader2;
           
window->draw( maskSprite, rs );
       
}
       
       
       
window->display();
   
}
   
return 0;
}
P-182927
« 1 »
  Strona 1 z 1