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

[SFML 2.X] Narzędzie Lasso w programie do edycji grafiki

Ostatnio zmodyfikowano 2025-09-06 17:18
Autor Wiadomość
tBane
Temat założony przez niniejszego użytkownika
[SFML 2.X] Narzędzie Lasso w programie do edycji grafiki
» 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
tBane
Temat założony przez niniejszego użytkownika
» 2025-08-31 14:41:24
Cześć ponownie :-)
Nie potrafię pozycjonować zaznaczenia kształtu względem zewnętrznego prostokątnego zaznaczenia.
Pierwsza funkcja do generowania outline (podczas zaznaczania) działa ale już druga - ta która jest odpowiedzialna za renderowanie zaznaczonego już zaznaczenia nie działa. Walczę z tym kodem od kilku godzin i nic nie wymyśliłem. Pomożecie ?

link do repozytorium:
https://github.com/tBane1995/Anim-Paint.git

Plik z kodem to Lasso.hpp

video z programu:
https://www.youtube.com/watch?v=HVOmRZ01Tv8

tak to niestety wygląda


a tak to powinno wyglądać:




co nie działa:
w funkcji lasso::draw()
C/C++
maskSprite.setScale( scale, scale );
maskSprite.setPosition( canvasPosition + sf::Vector2f( normRect.left * scale, normRect.top * scale ) );
lub
C/C++
void generateMovedOutline( float scale ) {
   
if( points.size() < 3 ) return;
   
   
sf::VertexArray lines( sf::LineStrip );
   
   
sf::IntRect normRect = normalizeRect();
   
   
for( auto & p: points )
       
 lines.append( sf::Vertex( sf::Vector2f( p.x - normRect.width / 2, p.y - normRect.height / 2 ), sf::Color::White ) );
   
   
//...
   

kod programu:
C/C++
#ifndef Lasso_hpp
#define Lasso_hpp

std::string lasso_only_outline_source = R"( uniform sampler2D mask; uniform vec2 maskSize; // px uniform float outlineSize; // PROMIEŃ w px uniform vec4 outlineColor; void main() { vec2 uv = gl_TexCoord[0].xy; vec2 pix = uv * maskSize; vec2 base = floor(pix); // 1) r zaokrąglij do najbliższej całości int r = int(floor(outlineSize + 0.5)); float aMax = 0.0; float aMin = 1.0; // 2) zakres ZAMKNIĘTY [-r .. r] for (int j = -r; j < r; ++j) { for (int i = -r; i < r; ++i) { vec2 p = base + vec2(float(i), float(j)); // out-of-bounds = 0 (shader ogranicza współrzędne) if (p.x < 0.0 || p.y < 0.0 || p.x > maskSize.x - 3.0 || p.y > maskSize.y - 1.0) { aMax = max(aMax, 0.0); aMin = min(aMin, 0.0); } else { vec2 coords = (p + vec2(0.5, 0.5)) / maskSize; float s = texture2D(mask, coords).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); } )";

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

class Lasso {
public:
   
LassoState state;
   
   
std::vector < sf::Vector2f > points;
   
sf::VertexArray outline;
   
   
// image
   
sf::Image * image;
   
sf::Texture texture;
   
sf::Sprite sprite;
   
   
// mask
   
float margin = 1;
   
sf::RenderTexture mask;
   
sf::Sprite maskSprite;
   
sf::Shader shaderOutline;
   
   
// outside rect and offset
   
sf::IntRect rect;
   
sf::Vector2i offset;
   
   
Lasso() {
       
state = LassoState::None;
       
points.clear();
       
outline = sf::VertexArray( sf::LineStrip );
       
       
image = nullptr;
       
rect = sf::IntRect( 0, 0, 0, 0 );
       
offset = sf::Vector2i( 0, 0 );
   
}
   
   
~Lasso() { }
   
   
void startSelecting( sf::Vector2f canvas_position, float scale ) {
       
       
       
texture.loadFromImage( layers_dialog->getCurrentLayer()->image );
       
texture.setSmooth( false );
       
       
sprite = sf::Sprite( texture );
       
sprite.setScale( scale, scale );
       
sprite.setPosition( canvas_position );
       
margin = 1.0f;
       
       
mask.create( texture.getSize().x + 2 * margin, texture.getSize().y + 2 * margin );
       
mask.setSmooth( false );
       
mask.clear( sf::Color( 127, 47, 47, 127 ) ); // czerwone tlo żeby było widać
       
mask.display();
       
       
maskSprite = sf::Sprite( mask.getTexture() );
       
maskSprite.setScale( scale, scale );
       
       
maskSprite.setPosition( canvas_position - sf::Vector2f( margin * scale, margin * scale ) );
       
       
shaderOutline.loadFromMemory( lasso_only_outline_source, sf::Shader::Fragment );
       
shaderOutline.setUniform( "mask", mask.getTexture() );
       
shaderOutline.setUniform( "maskSize", sf::Glsl::Vec2( mask.getSize().x, mask.getSize().y ) );
       
shaderOutline.setUniform( "outlineSize", 1.0f );
       
shaderOutline.setUniform( "outlineColor", sf::Glsl::Vec4( 47.0f / 255.0f, 127.0f / 255.0f, 127.0f / 255.0f, 255.0f / 255.0f ) ); // cyan
       
       
points.clear();
       
outline.clear();
   
}
   
   
sf::IntRect normalizeRect() {
       
sf::IntRect r = rect;
       
if( r.width < 0 ) { r.left += r.width; r.width = - r.width; }
       
if( r.height < 0 ) { r.top += r.height; r.height = - r.height; }
       
return r;
   
}
   
   
void addPoint( sf::Vector2f worldMousePosition, sf::Vector2f canvas_position, float scale ) {
       
// pozycja w pikselach obrazu (bez pada)
       
float lx =( worldMousePosition.x - canvas_position.x ) / scale;
       
float ly =( worldMousePosition.y - canvas_position.y ) / scale;
       
       
// pozycja w pikselach maski (z marginem)
       
float mx = lx + margin;
       
float my = ly + margin;
       
       
int w = int( mask.getSize().x );
       
int h = int( mask.getSize().y );
       
       
// dolna/górna granica – centra próbek w [margin .. (size-1 - margin)]
       
float loX = margin;
       
float hiX = float( w ) - 1.0f - margin;
       
float loY = margin;
       
float hiY = float( h ) - 1.0f - margin;
       
       
// zabezpieczenie gdy obraz jest zbyt mały
       
if( hiX < loX ) hiX = loX;
       
       
if( hiY < loY ) hiY = loY;
       
       
mx = std::clamp( std::floor( mx ), loX, hiX );
       
my = std::clamp( std::floor( my ), loY, hiY );
       
       
sf::Vector2f q( mx, my );
       
if( points.empty() || std::hypot( points.back().x - q.x, points.back().y - q.y ) >= 1.0f ) {
           
points.push_back( q );
           
outline.append( sf::Vertex( q, sf::Color::Cyan ) );
       
}
    }
   
   
void unselect() {
       
points.clear();
       
outline.clear();
   
}
   
   
bool clickOnSelection( sf::Vector2i point ) {
       
       
return rect.contains( point );
   
}
   
   
void completeSelection( sf::Image & image ) {
       
if( !points.empty() && outline.getVertexCount() > 2 ) {
           
points.push_back( points.front() );
           
outline.append( outline[ 0 ] );
           
           
int minX = std::numeric_limits < int >::max();
           
int maxX = std::numeric_limits < int >::lowest();
           
int minY = std::numeric_limits < int >::max();
           
int maxY = std::numeric_limits < int >::lowest();
           
           
for( auto & p: points ) {
               
minX = std::min( minX, int( p.x - 2 * margin ) );
               
maxX = std::max( maxX, int( p.x + 2 * margin ) );
               
minY = std::min( minY, int( p.y - 2 * margin ) );
               
maxY = std::max( maxY, int( p.y + 2 * margin ) );
           
}
           
           
rect = sf::IntRect( minX, minY, maxX - minX, maxY - minY );
           
       
}
       
    }
   
   
void generateOutline() {
       
if( points.size() < 3 ) return;
       
       
sf::VertexArray lines( sf::LineStrip );
       
       
for( auto & p: points )
           
 lines.append( sf::Vertex( sf::Vector2f( p ), sf::Color::White ) );
       
       
mask.create( texture.getSize().x + 2 * margin, texture.getSize().y + 2 * margin );
       
mask.clear( sf::Color( 127, 47, 47, 127 ) );
       
mask.draw( lines, sf::RenderStates( sf::BlendNone ) );
       
mask.display();
       
       
shaderOutline.setUniform( "mask", mask.getTexture() );
       
shaderOutline.setUniform( "maskSize", sf::Glsl::Vec2( mask.getSize().x, mask.getSize().y ) );
   
}
   
   
void generateMovedOutline( float scale ) {
       
if( points.size() < 3 ) return;
       
       
sf::VertexArray lines( sf::LineStrip );
       
       
sf::IntRect normRect = normalizeRect();
       
       
for( auto & p: points )
           
 lines.append( sf::Vertex( sf::Vector2f( p.x - normRect.width / 2, p.y - normRect.height / 2 ), sf::Color::White ) );
       
       
mask.create( texture.getSize().x + 2 * margin, texture.getSize().y + 2 * margin );
       
mask.clear( sf::Color( 127, 47, 47, 127 ) );
       
mask.draw( lines, sf::RenderStates( sf::BlendNone ) );
       
mask.display();
       
       
shaderOutline.setUniform( "mask", mask.getTexture() );
       
shaderOutline.setUniform( "maskSize", sf::Glsl::Vec2( mask.getSize().x, mask.getSize().y ) );
   
}
   
   
void draw( sf::Vector2f canvasPosition, sf::Vector2i canvasSize, float scale ) {
       
       
if( outline.getVertexCount() >= 3 ) {
           
if( state == LassoState::Selecting ) {
               
generateOutline();
               
               
maskSprite.setScale( scale, scale );
               
maskSprite.setPosition( canvasPosition - sf::Vector2f( margin * scale, margin * scale ) );
               
sf::RenderStates rs;
               
rs.shader = & shaderOutline;
               
window->draw( maskSprite, rs );
           
}
           
           
if( state == LassoState::Selected || state == LassoState::Moving ) {
               
generateMovedOutline( scale );
               
sf::IntRect normRect = normalizeRect();
               
               
if( image != nullptr ) {
                   
texture = sf::Texture();
                   
texture.loadFromImage( * image );
                   
sprite = sf::Sprite( texture );
                   
sprite.setPosition( canvasPosition + sf::Vector2f( normRect.left * scale, normRect.top * scale ) );
                   
sprite.setScale( scale, scale );
                   
window->draw( sprite );
               
}
               
               
maskSprite.setScale( scale, scale );
               
maskSprite.setPosition( canvasPosition + sf::Vector2f( normRect.left * scale, normRect.top * scale ) );
               
               
sf::RenderStates rs;
               
rs.shader = & shaderOutline;
               
window->draw( maskSprite, rs );
               
               
// ramka bbox (to już w układzie canvasa + rect)
               
sf::RectangleShape r( sf::Vector2f( normRect.width * scale, normRect.height * scale ) );
               
r.setPosition( canvasPosition + sf::Vector2f( normRect.left * scale, normRect.top * scale ) );
               
r.setFillColor( sf::Color::Transparent );
               
r.setOutlineThickness( 4.0f );
               
r.setOutlineColor( sf::Color( 47, 127, 127, 255 ) );
               
window->draw( r );
           
}
        }
       
    }
}
;

Lasso * lasso = nullptr;
#endif
P-182936
DejaVu
» 2025-09-01 09:10:33
Jeżeli masz lasso, które przekłada się już na konkretne pixele, to jedyne co musisz zrobić to znaleźć na obrazku osobno:
- min x,
- min y,
- max x,
- max y

Te wartości wyznaczą Ci:
- lewy górny róg
- wysokość
- szerokość

To umożliwi Ci poprawne wyrenderowanie prostokąta.
P-182938
« 1 » 2 3 4 5
  Strona 1 z 5 Następna strona