/* * Copyright 2026 Forschungszentrum Jülich * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #pragma once #include #include #include #include struct CageSpring { int a; int b; qreal restLength; }; /* * CageMesh * -------- * Repräsentiert ein regelmäßiges 2D-Gitter über einem Layer. * - speichert Original- und aktuelle Punkte * - verhindert Punktüberlappungen (Spring-Constraint) * - Grundlage für Piecewise-Affine-Warp (Triangle Warp) */ class CageMesh { public: CageMesh(); void create( const QRectF& bounds, int cols, int rows ); void update( const QRectF& bounds, int cols, int rows ); QImage& image() { return m_image; } void setImage( const QImage& image ); void rebuildSprings(); int cols() const { return m_cols; } int rows() const { return m_rows; } int getRadius() const { return m_controlPointRadius; } void setRadius( int rad ) { m_controlPointRadius = rad; } const QVector& points() const { return m_points; } const QVector& originalPoints() const { return m_originalPoints; } int pointCount() const { return m_points.size(); } bool isActive() const { return m_active; } void setActive( bool ); QPointF originalPoint( int i ) const; QPointF point( int i ) const; void setPoint( int i, const QPointF& pos ); void relax(); void setPoints( const QVector& pts ); void enforceConstraints( int idx ); QRectF boundingRect() const; void addOffset( qreal x, qreal y ) { m_offset += QPointF(x,y); } QPointF getOffset( bool isd=false ) const { return isd == false ? QPointF(0.0,0.0) : m_offset; } void setOffset( qreal x = -0.5, qreal y = -0.5 ) { m_offset = QPointF(x,y); } void setStiffness( double value ) { m_stiffness = value; } void setNumberOfRelaxationsSteps( int nsteps ) { m_relaxationSteps = nsteps; } void setFixedBoundaries( bool isFixed ) { m_fixedBoundaries = isFixed; } void needUpdate() { m_needUpdate = true; } void setIsInitialized() { m_isInitialized = true; } bool isInitialized( bool forced = false ) const { return forced ? ( !m_image.isNull() && m_isInitialized ) : m_isInitialized; } void printself(); private: bool isBoundaryPoint( int index ) const; void coarsen( const QRectF& bounds, int cols, int rows ); void refine( const QRectF& bounds, int cols, int rows ); void addNewSpring( int idxA, int idxB ); private: bool m_active = false; bool m_needUpdate = false; bool m_isInitialized = false; bool m_fixedBoundaries = true; double m_stiffness = 0.0; int m_relaxationSteps = 0; int m_cols = 3; int m_rows = 3; int m_controlPointRadius = 4; // should allow to change this via config file or main menu option QImage m_image; QPointF m_offset = QPointF(0,0); QVector m_points; QVector m_originalPoints; QVector m_springs; qreal m_minSpacing = 1.0; void addSpring( int a, int b ); };