mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 18:23:15 +02:00
Some shapes, like arcs and beziers have "lines" that can be useful to see when editing, but aren't directly editable and may not overlap the object's own lines. So make it possible to, indepedently: - Turn off the centre-point drag handle affordance - Show the actual line segment on screen.
334 lines
8.6 KiB
C++
334 lines
8.6 KiB
C++
/*
|
|
* This program source code file is part of KICAD, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2014-2019 CERN
|
|
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
|
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, you may find one here:
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <gal/graphics_abstraction_layer.h>
|
|
#include <gal/color4d.h>
|
|
#include <gal/painter.h>
|
|
#include <math/util.h> // for KiROUND
|
|
#include "tool/edit_points.h"
|
|
|
|
|
|
bool EDIT_POINT::WithinPoint( const VECTOR2I& aPoint, unsigned int aSize ) const
|
|
{
|
|
// Corners of the EDIT_POINT square
|
|
VECTOR2I topLeft = GetPosition() - aSize;
|
|
VECTOR2I bottomRight = GetPosition() + aSize;
|
|
|
|
return ( aPoint.x > topLeft.x && aPoint.y > topLeft.y &&
|
|
aPoint.x < bottomRight.x && aPoint.y < bottomRight.y );
|
|
}
|
|
|
|
|
|
EDIT_POINTS::EDIT_POINTS( EDA_ITEM* aParent ) :
|
|
EDA_ITEM( NOT_USED ),
|
|
m_parent( aParent ),
|
|
m_swapX( false ),
|
|
m_swapY( false ),
|
|
m_allowPoints( true )
|
|
{
|
|
}
|
|
|
|
|
|
EDIT_POINT* EDIT_POINTS::FindPoint( const VECTOR2I& aLocation, KIGFX::VIEW *aView ) // fixme: ugly
|
|
{
|
|
unsigned size = std::abs( KiROUND( aView->ToWorld( EDIT_POINT::POINT_SIZE ) ) );
|
|
|
|
if( m_allowPoints )
|
|
{
|
|
for( EDIT_POINT& point : m_points )
|
|
{
|
|
if( point.WithinPoint( aLocation, size ) )
|
|
return &point;
|
|
}
|
|
}
|
|
|
|
for( EDIT_LINE& line : m_lines )
|
|
{
|
|
if( line.WithinPoint( aLocation, size ) )
|
|
return &line;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
int EDIT_POINTS::GetContourStartIdx( int aPointIdx ) const
|
|
{
|
|
int lastIdx = 0;
|
|
|
|
for( int idx : m_contours )
|
|
{
|
|
if( idx >= aPointIdx )
|
|
return lastIdx;
|
|
|
|
lastIdx = idx + 1;
|
|
}
|
|
|
|
return lastIdx;
|
|
}
|
|
|
|
|
|
int EDIT_POINTS::GetContourEndIdx( int aPointIdx ) const
|
|
{
|
|
for( int idx : m_contours )
|
|
{
|
|
if( idx >= aPointIdx )
|
|
return idx;
|
|
}
|
|
|
|
return m_points.size() - 1;
|
|
}
|
|
|
|
|
|
bool EDIT_POINTS::IsContourStart( int aPointIdx ) const
|
|
{
|
|
for( int idx : m_contours )
|
|
{
|
|
if( idx + 1 == aPointIdx )
|
|
return true;
|
|
|
|
// the list is sorted, so we cannot expect it any further
|
|
if( idx > aPointIdx )
|
|
break;
|
|
}
|
|
|
|
return ( aPointIdx == 0 );
|
|
}
|
|
|
|
|
|
bool EDIT_POINTS::IsContourEnd( int aPointIdx ) const
|
|
{
|
|
for( int idx : m_contours )
|
|
{
|
|
if( idx == aPointIdx )
|
|
return true;
|
|
|
|
// the list is sorted, so we cannot expect it any further
|
|
if( idx > aPointIdx )
|
|
break;
|
|
}
|
|
|
|
// the end of the list surely is the end of a contour
|
|
return ( aPointIdx == (int) m_points.size() - 1 );
|
|
}
|
|
|
|
|
|
EDIT_POINT* EDIT_POINTS::Previous( const EDIT_POINT& aPoint, bool aTraverseContours )
|
|
{
|
|
for( unsigned int i = 0; i < m_points.size(); ++i )
|
|
{
|
|
if( m_points[i] == aPoint )
|
|
{
|
|
if( !aTraverseContours && IsContourStart( i ) )
|
|
return &m_points[GetContourEndIdx( i )];
|
|
|
|
if( i == 0 )
|
|
return &m_points[m_points.size() - 1];
|
|
else
|
|
return &m_points[i - 1];
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
EDIT_LINE* EDIT_POINTS::Previous( const EDIT_LINE& aLine )
|
|
{
|
|
for( unsigned int i = 0; i < m_lines.size(); ++i )
|
|
{
|
|
if( m_lines[i] == aLine )
|
|
{
|
|
if( i == 0 )
|
|
return &m_lines[m_lines.size() - 1];
|
|
else
|
|
return &m_lines[i - 1];
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
EDIT_POINT* EDIT_POINTS::Next( const EDIT_POINT& aPoint, bool aTraverseContours )
|
|
{
|
|
for( unsigned int i = 0; i < m_points.size(); ++i )
|
|
{
|
|
if( m_points[i] == aPoint )
|
|
{
|
|
if( !aTraverseContours && IsContourEnd( i ) )
|
|
return &m_points[GetContourStartIdx( i )];
|
|
|
|
if( i == m_points.size() - 1 )
|
|
return &m_points[0];
|
|
else
|
|
return &m_points[i + 1];
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
EDIT_LINE* EDIT_POINTS::Next( const EDIT_LINE& aLine )
|
|
{
|
|
for( unsigned int i = 0; i < m_lines.size(); ++i )
|
|
{
|
|
if( m_lines[i] == aLine )
|
|
{
|
|
if( i == m_lines.size() - 1 )
|
|
return &m_lines[0];
|
|
else
|
|
return &m_lines[i + 1];
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
const BOX2I EDIT_POINTS::ViewBBox() const
|
|
{
|
|
BOX2I box;
|
|
bool empty = true;
|
|
|
|
for( const auto& point : m_points )
|
|
{
|
|
if( empty )
|
|
{
|
|
box.SetOrigin( point.GetPosition() );
|
|
empty = false;
|
|
}
|
|
else
|
|
{
|
|
box.Merge( point.GetPosition() );
|
|
}
|
|
}
|
|
|
|
for( const auto& line : m_lines )
|
|
{
|
|
if( empty )
|
|
{
|
|
box.SetOrigin( line.GetOrigin().GetPosition() );
|
|
box.SetEnd( line.GetEnd().GetPosition() );
|
|
empty = false;
|
|
}
|
|
else
|
|
{
|
|
box.Merge( line.GetOrigin().GetPosition() );
|
|
box.Merge( line.GetEnd().GetPosition() );
|
|
}
|
|
}
|
|
|
|
return box;
|
|
}
|
|
|
|
|
|
void EDIT_POINTS::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
|
|
{
|
|
KIGFX::GAL* gal = aView->GetGAL();
|
|
KIGFX::RENDER_SETTINGS* settings = aView->GetPainter()->GetSettings();
|
|
KIGFX::COLOR4D drawColor = settings->GetLayerColor( LAYER_AUX_ITEMS );
|
|
|
|
// Don't assume LAYER_AUX_ITEMS is always a good choice. Compare with background.
|
|
if( aView->GetGAL()->GetClearColor().Distance( drawColor ) < 0.5 )
|
|
drawColor.Invert();
|
|
|
|
// Linear darkening doesn't fit well with human color perception, and there's no guarantee
|
|
// that there's enough room for contrast either.
|
|
KIGFX::COLOR4D borderColor;
|
|
KIGFX::COLOR4D highlightColor;
|
|
double brightness = drawColor.GetBrightness();
|
|
|
|
if( brightness > 0.5 )
|
|
{
|
|
borderColor = drawColor.Darkened( 0.7 ).WithAlpha( 0.8 );
|
|
highlightColor = drawColor.Darkened( 0.5 ).WithAlpha( 0.8 );
|
|
}
|
|
else if( brightness > 0.2 )
|
|
{
|
|
borderColor = drawColor.Brightened( 0.4 ).WithAlpha( 0.8 );
|
|
highlightColor = drawColor.Brightened( 0.3 ).WithAlpha( 0.8 );
|
|
}
|
|
else
|
|
{
|
|
borderColor = drawColor.Brightened( 0.7 ).WithAlpha( 0.8 );
|
|
highlightColor = drawColor.Brightened( 0.5 ).WithAlpha( 0.8 );
|
|
}
|
|
|
|
gal->SetFillColor( drawColor );
|
|
gal->SetStrokeColor( borderColor );
|
|
gal->SetIsFill( true );
|
|
gal->SetIsStroke( true );
|
|
gal->PushDepth();
|
|
gal->SetLayerDepth( gal->GetMinDepth() );
|
|
|
|
double size = aView->ToWorld( EDIT_POINT::POINT_SIZE ) / 2.0;
|
|
double borderSize = aView->ToWorld( EDIT_POINT::BORDER_SIZE );
|
|
double hoverSize = aView->ToWorld( EDIT_POINT::HOVER_SIZE );
|
|
|
|
auto drawPoint =
|
|
[&]( const EDIT_POINT& aPoint, bool aDrawCircle = false )
|
|
{
|
|
if( aPoint.IsHover() || aPoint.IsActive() )
|
|
{
|
|
gal->SetStrokeColor( highlightColor );
|
|
gal->SetLineWidth( hoverSize );
|
|
}
|
|
else
|
|
{
|
|
gal->SetStrokeColor( borderColor );
|
|
gal->SetLineWidth( borderSize );
|
|
}
|
|
|
|
gal->SetFillColor( drawColor );
|
|
|
|
if( aDrawCircle )
|
|
gal->DrawCircle( aPoint.GetPosition(), size );
|
|
else
|
|
gal->DrawRectangle( aPoint.GetPosition() - size, aPoint.GetPosition() + size );
|
|
};
|
|
|
|
for( const EDIT_POINT& point : m_points )
|
|
drawPoint( point );
|
|
|
|
for( const EDIT_LINE& line : m_lines )
|
|
{
|
|
if( line.HasCenterPoint() )
|
|
{
|
|
drawPoint( line.GetPosition(), true );
|
|
}
|
|
|
|
if( line.DrawLine() )
|
|
{
|
|
gal->SetLineWidth( borderSize );
|
|
gal->SetStrokeColor( borderColor );
|
|
gal->DrawLine( line.GetOrigin().GetPosition(), line.GetEnd().GetPosition() );
|
|
}
|
|
}
|
|
|
|
gal->PopDepth();
|
|
}
|