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
» 2025-09-01 11:13:27
Tak też zrobiłem, ale problem polega na tym że przesuwa mi punkty nieregularnego zaznaczenia.
P-182939
DejaVu
» 2025-09-01 11:34:43
Jeżeli zaznaczenie jest dobre, a prostokąt jest zły, to nie zrobiłeś tego dobrze.
P-182940
tBane
Temat założony przez niniejszego użytkownika
» 2025-09-01 11:36:13
Zobacz jak to wygląda na nagraniu na youtube.

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

Zaznaczanie jest dobre, po czym gdy kończymy zaznaczać to zaznaczenie nieregularne się przesuwa a prostokąt jest generowany poprawnie
P-182941
DejaVu
» 2025-09-01 14:38:09
No to jeżeli prostokąt rysuje się dobrze, a lasso źle, to masz zły punkt początkowy wyznaczony dla lasso. może jest wyznaczony względem punktu startowego w którym zaczynasz rysować, zamiast wyliczyć offset do lewego górnego rogu.
P-182942
tBane
Temat założony przez niniejszego użytkownika
» 2025-09-01 15:36:25
Spróbuję dodać offset i dam znać
P-182943
tBane
Temat założony przez niniejszego użytkownika
» 2025-09-01 16:01:34
Nie mam pojęcia jak to zrobić.
Dodałem zmienną maskOffset i przypisuje jej wartosć podczas dodawania pierwszego punktu czyli offset = firstPointCoords.  Następnie w funkcji draw rysuje lasso z przesunięciem o offset ale nie działa.

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;
   
sf::Vector2f maskOffset;
   
   
// 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 );
       
maskOffset = sf::Vector2f( 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 );
       
       
if( points.empty() ) {
           
offset = sf::Vector2i( mx, my );
       
}
       
       
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( p + sf::Vector2f( offset ) * scale, 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( offset ) );
               
std::cout << "canvas: " << canvasPosition.x << ", " << canvasPosition.y << "\n";
               
std::cout << "mask: " << maskSprite.getGlobalBounds().getPosition().x << ", " << maskSprite.getGlobalBounds().getPosition().y << "\n";
               
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-182944
tBane
Temat założony przez niniejszego użytkownika
» 2025-09-01 19:37:55
Skasuję ten kod i zacznę od nowa... :-/
P-182945
tBane
Temat założony przez niniejszego użytkownika
» 2025-09-02 16:02:59
Prawie napisałem od nowa zaznaczanie lasso, tylko jest bug - czasami ucina fragment zaznaczenia :-/
Jeśli kodu jest za mało to zaktualizowałem repo:
https://github.com/tBane1995/Anim-Paint.git

kod jest w plikach Anim-Paint/lasso.hpp oraz Anim-Paint/canvas.hpp



C/C++
#ifndef Lasso_hpp
#define Lasso_hpp

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

class Lasso {
public:
   
LassoState state;
   
   
std::vector < sf::Vector2i > points;
   
   
// image
   
sf::Image * image;
   
sf::Texture texture;
   
sf::Sprite sprite;
   
   
// mask
   
sf::RenderTexture mask;
   
sf::Sprite maskSprite;
   
sf::Vector2i maskOffset;
   
   
// outside rect and offset
   
sf::IntRect rect;
   
   
Lasso() {
       
state = LassoState::None;
       
       
points.clear();
       
       
image = nullptr;
       
rect = sf::IntRect( 0, 0, 0, 0 );
       
maskOffset = sf::Vector2i( 0, 0 );
   
}
   
   
~Lasso() { }
   
   
void shiftOriginIfNeeded( sf::Vector2i & point )
   
{
       
sf::Vector2i shift( 0, 0 );
       
if( point.x < 0 ) { shift.x = point.x; point.x = 0; }
       
if( point.y < 0 ) { shift.y = point.y; point.y = 0; }
       
       
if( shift.x != 0 || shift.y != 0 ) {
           
           
maskOffset += shift;
           
           
for( auto & p: points )
               
 p -= shift;
           
       
}
    }
   
   
void addPoint( sf::Vector2i point ) // tile = globalny punkt w kafelkach
   
{
       
point = point - maskOffset;
       
       
shiftOriginIfNeeded( point );
       
       
if( points.empty() ||
       
std::hypot( float( points.back().x - point.x ), float( points.back().y - point.y ) ) >= 1.0f )
       
{
           
points.push_back( point );
           
// std::cout << "add point local: " << local.x << ", " << local.y << "\n";
       
}
    }
   
   
void unselect() {
       
points.clear();
       
generateRect();
       
//image = nullptr;
   
}
   
   
void generateRect() {
       
if( points.size() > 2 ) {
           
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, p.x );
               
maxX = std::max( maxX, p.x );
               
minY = std::min( minY, p.y );
               
maxY = std::max( maxY, p.y );
           
}
           
           
int left = minX + maskOffset.x;
           
int top = minY + maskOffset.y;
           
int width =( maxX - minX + 1 );
           
int height =( maxY - minY + 1 );
           
           
if( width > 0 && height > 0 )
               
 rect = sf::IntRect( left, top, width, height );
           
else
               
 rect = sf::IntRect( 0, 0, 0, 0 );
           
       
}
       
else {
           
rect = sf::IntRect( 0, 0, 0, 0 );
       
}
    }
   
   
bool clickOnSelection( sf::Vector2i point ) {
       
       
return rect.contains( point );
   
}
   
   
   
   
void generateOutline( float scale ) {
       
if( points.size() < 3 ) return;
       
       
sf::VertexArray lines( sf::LineStrip );
       
       
for( auto & p: points )
           
 lines.append( sf::Vertex( sf::Vector2f( p ), sf::Color::White ) );
       
       
//lines.append(sf::Vertex(sf::Vector2f(points.front()), sf::Color::White));
       
       
if( image != nullptr ) {
           
texture = sf::Texture();
           
texture.create( image->getSize().x, image->getSize().y );
           
texture.loadFromImage( * image );
           
           
           
mask.create( texture.getSize().x, texture.getSize().y );
           
mask.clear( sf::Color( 127, 47, 47, 127 ) );
           
sf::RenderStates rs;
           
rs.blendMode = sf::BlendNone;
           
rs.transform.translate( 0.5f, 0.5f ); // <<— kluczowe
           
mask.draw( lines, rs );
           
mask.display();
       
}
    }
   
   
void drawOutline( sf::Vector2f canvasPosition, float scale ) {
       
maskSprite = sf::Sprite( mask.getTexture() );
       
maskSprite.setScale( scale, scale );
       
maskSprite.setPosition( canvasPosition + sf::Vector2f( maskOffset ) * scale );
       
window->draw( maskSprite );
       
   
}
   
   
   
void draw( sf::Vector2f canvasPosition, float scale ) {
       
       
if( points.size() >= 3 ) {
           
if( true ) { //state == LassoState::Selecting || state == LassoState::Selected) {
               
generateOutline( scale );
               
drawOutline( canvasPosition, scale );
           
}
        }
       
    }
}
;

Lasso * lasso = nullptr;
#endif
P-182946
1 « 2 » 3 4 5
Poprzednia strona Strona 2 z 5 Następna strona