2024-11-03 16:09:48 +08:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KICAD, a free EDA CAD application.
|
|
|
|
*
|
2025-01-01 13:30:11 -08:00
|
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
2024-11-03 16:09:48 +08:00
|
|
|
*
|
|
|
|
* 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 "tool/point_editor_behavior.h"
|
|
|
|
|
2025-05-01 23:08:52 +08:00
|
|
|
#include <advanced_config.h>
|
2024-11-03 16:09:48 +08:00
|
|
|
#include <geometry/shape_poly_set.h>
|
|
|
|
|
|
|
|
|
|
|
|
void POLYGON_POINT_EDIT_BEHAVIOR::BuildForPolyOutline( EDIT_POINTS& aPoints,
|
|
|
|
const SHAPE_POLY_SET& aOutline )
|
|
|
|
{
|
|
|
|
const int cornersCount = aOutline.TotalVertices();
|
|
|
|
|
|
|
|
for( auto iterator = aOutline.CIterateWithHoles(); iterator; iterator++ )
|
|
|
|
{
|
|
|
|
aPoints.AddPoint( *iterator );
|
|
|
|
|
|
|
|
if( iterator.IsEndContour() )
|
|
|
|
aPoints.AddBreak();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lines have to be added after creating edit points, as they use EDIT_POINT references
|
|
|
|
for( int i = 0; i < cornersCount - 1; ++i )
|
|
|
|
{
|
|
|
|
if( aPoints.IsContourEnd( i ) )
|
|
|
|
aPoints.AddLine( aPoints.Point( i ), aPoints.Point( aPoints.GetContourStartIdx( i ) ) );
|
|
|
|
else
|
|
|
|
aPoints.AddLine( aPoints.Point( i ), aPoints.Point( i + 1 ) );
|
|
|
|
|
|
|
|
aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// The last missing line, connecting the last and the first polygon point
|
|
|
|
aPoints.AddLine( aPoints.Point( cornersCount - 1 ),
|
|
|
|
aPoints.Point( aPoints.GetContourStartIdx( cornersCount - 1 ) ) );
|
|
|
|
|
|
|
|
aPoints.Line( aPoints.LinesSize() - 1 )
|
|
|
|
.SetConstraint( new EC_PERPLINE( aPoints.Line( aPoints.LinesSize() - 1 ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void POLYGON_POINT_EDIT_BEHAVIOR::UpdatePointsFromOutline( const SHAPE_POLY_SET& aOutline,
|
|
|
|
EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
// No size check here, as we can and will rebuild if that fails
|
|
|
|
if( aPoints.PointsSize() != (unsigned) aOutline.TotalVertices() )
|
|
|
|
{
|
|
|
|
// Rebuild the points list
|
|
|
|
aPoints.Clear();
|
|
|
|
BuildForPolyOutline( aPoints, aOutline );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for( int i = 0; i < aOutline.TotalVertices(); ++i )
|
|
|
|
{
|
|
|
|
aPoints.Point( i ).SetPosition( aOutline.CVertex( i ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void POLYGON_POINT_EDIT_BEHAVIOR::UpdateOutlineFromPoints( SHAPE_POLY_SET& aOutline,
|
|
|
|
const EDIT_POINT& aEditedPoint,
|
|
|
|
EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
CHECK_POINT_COUNT_GE( aPoints, (unsigned) aOutline.TotalVertices() );
|
|
|
|
|
|
|
|
for( int i = 0; i < aOutline.TotalVertices(); ++i )
|
|
|
|
{
|
|
|
|
aOutline.SetVertex( i, aPoints.Point( i ).GetPosition() );
|
|
|
|
}
|
|
|
|
|
|
|
|
for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
|
|
|
|
{
|
|
|
|
if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
|
|
|
|
{
|
|
|
|
aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Point editor: unify segment, circle, bezier and tablecells (partial)
For segment, circle, beziers, these are the same in all editors and only
need access to the EDA_SHAPE nature, so we can remove the duplication
entirely.
For TABLECELLs, while the cells are polymorphic in that PCB and SCH
cells are both EDA_SHAPEs (via the TEXTBOXes), the parent TABLES
are not polymorphic, and thus the implementation can't be trivially
de-duplicated. Rather than do something with templates, just keep
it simple for now and maybe look at unifying tables later on.
2024-11-03 23:15:32 +08:00
|
|
|
|
|
|
|
|
|
|
|
void EDA_SEGMENT_POINT_EDIT_BEHAVIOR::MakePoints( EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
aPoints.AddPoint( m_segment.GetStart() );
|
|
|
|
aPoints.AddPoint( m_segment.GetEnd() );
|
|
|
|
}
|
|
|
|
|
2025-01-16 07:37:05 -05:00
|
|
|
|
Point editor: unify segment, circle, bezier and tablecells (partial)
For segment, circle, beziers, these are the same in all editors and only
need access to the EDA_SHAPE nature, so we can remove the duplication
entirely.
For TABLECELLs, while the cells are polymorphic in that PCB and SCH
cells are both EDA_SHAPEs (via the TEXTBOXes), the parent TABLES
are not polymorphic, and thus the implementation can't be trivially
de-duplicated. Rather than do something with templates, just keep
it simple for now and maybe look at unifying tables later on.
2024-11-03 23:15:32 +08:00
|
|
|
void EDA_SEGMENT_POINT_EDIT_BEHAVIOR::UpdatePoints( EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
CHECK_POINT_COUNT( aPoints, SEGMENT_MAX_POINTS );
|
|
|
|
|
|
|
|
aPoints.Point( SEGMENT_START ) = m_segment.GetStart();
|
|
|
|
aPoints.Point( SEGMENT_END ) = m_segment.GetEnd();
|
|
|
|
}
|
|
|
|
|
2025-01-16 07:37:05 -05:00
|
|
|
|
Point editor: unify segment, circle, bezier and tablecells (partial)
For segment, circle, beziers, these are the same in all editors and only
need access to the EDA_SHAPE nature, so we can remove the duplication
entirely.
For TABLECELLs, while the cells are polymorphic in that PCB and SCH
cells are both EDA_SHAPEs (via the TEXTBOXes), the parent TABLES
are not polymorphic, and thus the implementation can't be trivially
de-duplicated. Rather than do something with templates, just keep
it simple for now and maybe look at unifying tables later on.
2024-11-03 23:15:32 +08:00
|
|
|
void EDA_SEGMENT_POINT_EDIT_BEHAVIOR::UpdateItem( const EDIT_POINT& aEditedPoint,
|
|
|
|
EDIT_POINTS& aPoints, COMMIT& aCommit,
|
|
|
|
std::vector<EDA_ITEM*>& aUpdatedItems )
|
|
|
|
{
|
|
|
|
CHECK_POINT_COUNT( aPoints, SEGMENT_MAX_POINTS );
|
|
|
|
|
|
|
|
if( isModified( aEditedPoint, aPoints.Point( SEGMENT_START ) ) )
|
|
|
|
m_segment.SetStart( aPoints.Point( SEGMENT_START ).GetPosition() );
|
|
|
|
|
|
|
|
else if( isModified( aEditedPoint, aPoints.Point( SEGMENT_END ) ) )
|
|
|
|
m_segment.SetEnd( aPoints.Point( SEGMENT_END ).GetPosition() );
|
|
|
|
}
|
|
|
|
|
|
|
|
OPT_VECTOR2I
|
|
|
|
EDA_SEGMENT_POINT_EDIT_BEHAVIOR::Get45DegreeConstrainer( const EDIT_POINT& aEditedPoint,
|
|
|
|
EDIT_POINTS& aPoints ) const
|
|
|
|
{
|
|
|
|
// Select the other end of line
|
|
|
|
return aPoints.Next( aEditedPoint )->GetPosition();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EDA_CIRCLE_POINT_EDIT_BEHAVIOR::MakePoints( EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
aPoints.AddPoint( m_circle.getCenter() );
|
|
|
|
aPoints.AddPoint( m_circle.GetEnd() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EDA_CIRCLE_POINT_EDIT_BEHAVIOR::UpdatePoints( EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
CHECK_POINT_COUNT( aPoints, CIRC_MAX_POINTS );
|
|
|
|
|
|
|
|
aPoints.Point( CIRC_CENTER ).SetPosition( m_circle.getCenter() );
|
|
|
|
aPoints.Point( CIRC_END ).SetPosition( m_circle.GetEnd() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EDA_CIRCLE_POINT_EDIT_BEHAVIOR::UpdateItem( const EDIT_POINT& aEditedPoint,
|
|
|
|
EDIT_POINTS& aPoints, COMMIT& aCommit,
|
|
|
|
std::vector<EDA_ITEM*>& aUpdatedItems )
|
|
|
|
{
|
|
|
|
CHECK_POINT_COUNT( aPoints, CIRC_MAX_POINTS );
|
|
|
|
|
|
|
|
const VECTOR2I& center = aPoints.Point( CIRC_CENTER ).GetPosition();
|
|
|
|
const VECTOR2I& end = aPoints.Point( CIRC_END ).GetPosition();
|
|
|
|
|
|
|
|
if( isModified( aEditedPoint, aPoints.Point( CIRC_CENTER ) ) )
|
|
|
|
{
|
|
|
|
m_circle.SetCenter( center );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_circle.SetEnd( VECTOR2I( end.x, end.y ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
OPT_VECTOR2I
|
|
|
|
EDA_CIRCLE_POINT_EDIT_BEHAVIOR::Get45DegreeConstrainer( const EDIT_POINT& aEditedPoint,
|
|
|
|
EDIT_POINTS& aPoints ) const
|
|
|
|
{
|
|
|
|
return aPoints.Point( CIRC_CENTER ).GetPosition();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EDA_BEZIER_POINT_EDIT_BEHAVIOR::MakePoints( EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
aPoints.AddPoint( m_bezier.GetStart() );
|
|
|
|
aPoints.AddPoint( m_bezier.GetBezierC1() );
|
|
|
|
aPoints.AddPoint( m_bezier.GetBezierC2() );
|
|
|
|
aPoints.AddPoint( m_bezier.GetEnd() );
|
|
|
|
|
|
|
|
aPoints.AddIndicatorLine( aPoints.Point( BEZIER_START ), aPoints.Point( BEZIER_CTRL_PT1 ) );
|
|
|
|
aPoints.AddIndicatorLine( aPoints.Point( BEZIER_CTRL_PT2 ), aPoints.Point( BEZIER_END ) );
|
|
|
|
}
|
|
|
|
|
2025-01-16 07:37:05 -05:00
|
|
|
|
Point editor: unify segment, circle, bezier and tablecells (partial)
For segment, circle, beziers, these are the same in all editors and only
need access to the EDA_SHAPE nature, so we can remove the duplication
entirely.
For TABLECELLs, while the cells are polymorphic in that PCB and SCH
cells are both EDA_SHAPEs (via the TEXTBOXes), the parent TABLES
are not polymorphic, and thus the implementation can't be trivially
de-duplicated. Rather than do something with templates, just keep
it simple for now and maybe look at unifying tables later on.
2024-11-03 23:15:32 +08:00
|
|
|
void EDA_BEZIER_POINT_EDIT_BEHAVIOR::UpdatePoints( EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
CHECK_POINT_COUNT( aPoints, BEZIER_MAX_POINTS );
|
|
|
|
|
|
|
|
aPoints.Point( BEZIER_START ).SetPosition( m_bezier.GetStart() );
|
|
|
|
aPoints.Point( BEZIER_CTRL_PT1 ).SetPosition( m_bezier.GetBezierC1() );
|
|
|
|
aPoints.Point( BEZIER_CTRL_PT2 ).SetPosition( m_bezier.GetBezierC2() );
|
|
|
|
aPoints.Point( BEZIER_END ).SetPosition( m_bezier.GetEnd() );
|
|
|
|
}
|
|
|
|
|
2025-01-16 07:37:05 -05:00
|
|
|
|
Point editor: unify segment, circle, bezier and tablecells (partial)
For segment, circle, beziers, these are the same in all editors and only
need access to the EDA_SHAPE nature, so we can remove the duplication
entirely.
For TABLECELLs, while the cells are polymorphic in that PCB and SCH
cells are both EDA_SHAPEs (via the TEXTBOXes), the parent TABLES
are not polymorphic, and thus the implementation can't be trivially
de-duplicated. Rather than do something with templates, just keep
it simple for now and maybe look at unifying tables later on.
2024-11-03 23:15:32 +08:00
|
|
|
void EDA_BEZIER_POINT_EDIT_BEHAVIOR::UpdateItem( const EDIT_POINT& aEditedPoint,
|
|
|
|
EDIT_POINTS& aPoints, COMMIT& aCommit,
|
|
|
|
std::vector<EDA_ITEM*>& aUpdatedItems )
|
|
|
|
{
|
|
|
|
CHECK_POINT_COUNT( aPoints, BEZIER_MAX_POINTS );
|
|
|
|
|
|
|
|
if( isModified( aEditedPoint, aPoints.Point( BEZIER_START ) ) )
|
|
|
|
{
|
|
|
|
m_bezier.SetStart( aPoints.Point( BEZIER_START ).GetPosition() );
|
|
|
|
}
|
|
|
|
else if( isModified( aEditedPoint, aPoints.Point( BEZIER_CTRL_PT1 ) ) )
|
|
|
|
{
|
|
|
|
m_bezier.SetBezierC1( aPoints.Point( BEZIER_CTRL_PT1 ).GetPosition() );
|
|
|
|
}
|
|
|
|
else if( isModified( aEditedPoint, aPoints.Point( BEZIER_CTRL_PT2 ) ) )
|
|
|
|
{
|
|
|
|
m_bezier.SetBezierC2( aPoints.Point( BEZIER_CTRL_PT2 ).GetPosition() );
|
|
|
|
}
|
|
|
|
else if( isModified( aEditedPoint, aPoints.Point( BEZIER_END ) ) )
|
|
|
|
{
|
|
|
|
m_bezier.SetEnd( aPoints.Point( BEZIER_END ).GetPosition() );
|
|
|
|
}
|
|
|
|
|
2025-05-26 17:30:37 +01:00
|
|
|
m_bezier.RebuildBezierToSegmentsPointsList( m_maxError );
|
Point editor: unify segment, circle, bezier and tablecells (partial)
For segment, circle, beziers, these are the same in all editors and only
need access to the EDA_SHAPE nature, so we can remove the duplication
entirely.
For TABLECELLs, while the cells are polymorphic in that PCB and SCH
cells are both EDA_SHAPEs (via the TEXTBOXes), the parent TABLES
are not polymorphic, and thus the implementation can't be trivially
de-duplicated. Rather than do something with templates, just keep
it simple for now and maybe look at unifying tables later on.
2024-11-03 23:15:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-05-01 23:08:52 +08:00
|
|
|
// Note: these static arc functions don't have to be in here - we could ship them out
|
|
|
|
// to a utils area for use by other code (e.g. polygon fillet editing).
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move an end point of the arc, while keeping the tangent at the other endpoint.
|
|
|
|
*/
|
|
|
|
static void editArcEndpointKeepTangent( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
|
|
|
|
const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCursor )
|
|
|
|
{
|
|
|
|
VECTOR2I center = aCenter;
|
|
|
|
bool movingStart;
|
|
|
|
bool arcValid = true;
|
|
|
|
|
|
|
|
VECTOR2I p1, p2, p3;
|
|
|
|
// p1 does not move, p2 does.
|
|
|
|
|
|
|
|
if( aStart != aArc.GetStart() )
|
|
|
|
{
|
|
|
|
p1 = aEnd;
|
|
|
|
p2 = aStart;
|
|
|
|
p3 = aMid;
|
|
|
|
movingStart = true;
|
|
|
|
}
|
|
|
|
else if( aEnd != aArc.GetEnd() )
|
|
|
|
{
|
|
|
|
p1 = aStart;
|
|
|
|
p2 = aEnd;
|
|
|
|
p3 = aMid;
|
|
|
|
movingStart = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VECTOR2D v1, v2, v3, v4;
|
|
|
|
|
|
|
|
// Move the coordinate system
|
|
|
|
v1 = p1 - aCenter;
|
|
|
|
v2 = p2 - aCenter;
|
|
|
|
v3 = p3 - aCenter;
|
|
|
|
|
|
|
|
VECTOR2D u1, u2;
|
|
|
|
|
|
|
|
// A point cannot be both the center and on the arc.
|
|
|
|
if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
u1 = v1 / v1.EuclideanNorm();
|
|
|
|
u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
|
|
|
|
u2 = u2 / u2.EuclideanNorm();
|
|
|
|
|
|
|
|
// [ u1, u3 ] is a base centered on the circle with:
|
|
|
|
// u1 : unit vector toward the point that does not move
|
|
|
|
// u2 : unit vector toward the mid point.
|
|
|
|
|
|
|
|
// Get vectors v1, and v2 in that coordinate system.
|
|
|
|
|
|
|
|
double det = u1.x * u2.y - u2.x * u1.y;
|
|
|
|
|
|
|
|
// u1 and u2 are unit vectors, and perpendicular.
|
|
|
|
// det should not be 0. In case it is, do not change the arc.
|
|
|
|
if( det == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
double tmpx = v1.x * u2.y - v1.y * u2.x;
|
|
|
|
double tmpy = -v1.x * u1.y + v1.y * u1.x;
|
|
|
|
v1.x = tmpx;
|
|
|
|
v1.y = tmpy;
|
|
|
|
v1 = v1 / det;
|
|
|
|
|
|
|
|
tmpx = v2.x * u2.y - v2.y * u2.x;
|
|
|
|
tmpy = -v2.x * u1.y + v2.y * u1.x;
|
|
|
|
v2.x = tmpx;
|
|
|
|
v2.y = tmpy;
|
|
|
|
v2 = v2 / det;
|
|
|
|
|
|
|
|
double R = v1.EuclideanNorm();
|
|
|
|
bool transformCircle = false;
|
|
|
|
|
|
|
|
/* p2
|
|
|
|
* X***
|
|
|
|
* ** <---- This is the arc
|
|
|
|
* y ^ **
|
|
|
|
* | R *
|
|
|
|
* | <-----------> *
|
|
|
|
* x------x------>--------x p1
|
|
|
|
* C' <----> C x
|
|
|
|
* delta
|
|
|
|
*
|
|
|
|
* p1 does not move, and the tangent at p1 remains the same.
|
|
|
|
* => The new center, C', will be on the C-p1 axis.
|
|
|
|
* p2 moves
|
|
|
|
*
|
|
|
|
* The radius of the new circle is delta + R
|
|
|
|
*
|
|
|
|
* || C' p2 || = || C' P1 ||
|
|
|
|
* is the same as :
|
|
|
|
* ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
|
|
|
|
*
|
|
|
|
* delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
|
|
|
|
*
|
|
|
|
* We can use this equation for any point p2 with p2.x < R
|
|
|
|
*/
|
|
|
|
|
|
|
|
if( v2.x == R )
|
|
|
|
{
|
|
|
|
// Straight line, do nothing
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( v2.x > R )
|
|
|
|
{
|
|
|
|
// If we need to invert the curvature.
|
|
|
|
// We modify the input so we can use the same equation
|
|
|
|
transformCircle = true;
|
|
|
|
v2.x = 2 * R - v2.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We can keep the tangent constraint.
|
|
|
|
double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
|
|
|
|
|
|
|
|
// This is just to limit the radius, so nothing overflows later when drawing.
|
|
|
|
if( abs( v2.y / ( R - v2.x ) ) > ADVANCED_CFG::GetCfg().m_DrawArcCenterMaxAngle )
|
|
|
|
arcValid = false;
|
|
|
|
|
|
|
|
// Never recorded a problem, but still checking.
|
|
|
|
if( !std::isfinite( delta ) )
|
|
|
|
arcValid = false;
|
|
|
|
|
|
|
|
// v4 is the new center
|
|
|
|
v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
|
|
|
|
|
|
|
|
tmpx = v4.x * u1.x + v4.y * u2.x;
|
|
|
|
tmpy = v4.x * u1.y + v4.y * u2.y;
|
|
|
|
v4.x = tmpx;
|
|
|
|
v4.y = tmpy;
|
|
|
|
|
|
|
|
center = v4 + aCenter;
|
|
|
|
|
|
|
|
if( arcValid )
|
|
|
|
{
|
|
|
|
aArc.SetCenter( center );
|
|
|
|
|
|
|
|
if( movingStart )
|
|
|
|
aArc.SetStart( aStart );
|
|
|
|
else
|
|
|
|
aArc.SetEnd( aEnd );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move the arc center but keep endpoint locations.
|
|
|
|
*/
|
|
|
|
static void editArcCenterKeepEndpoints( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
|
|
|
|
const VECTOR2I& aMid, const VECTOR2I& aEnd )
|
|
|
|
{
|
|
|
|
const int c_snapEpsilon_sq = 4;
|
|
|
|
|
|
|
|
VECTOR2I m = ( aStart / 2 + aEnd / 2 );
|
|
|
|
VECTOR2I perp = ( aEnd - aStart ).Perpendicular().Resize( INT_MAX / 2 );
|
|
|
|
|
|
|
|
SEG legal( m - perp, m + perp );
|
|
|
|
|
|
|
|
const SEG testSegments[] = {
|
|
|
|
SEG( aCenter, aCenter + VECTOR2( 1, 0 ) ),
|
|
|
|
SEG( aCenter, aCenter + VECTOR2( 0, 1 ) ),
|
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<VECTOR2I> points = { legal.A, legal.B };
|
|
|
|
|
|
|
|
for( const SEG& seg : testSegments )
|
|
|
|
{
|
|
|
|
OPT_VECTOR2I vec = legal.IntersectLines( seg );
|
|
|
|
|
|
|
|
if( vec && legal.SquaredDistance( *vec ) <= c_snapEpsilon_sq )
|
|
|
|
points.push_back( *vec );
|
|
|
|
}
|
|
|
|
|
|
|
|
OPT_VECTOR2I nearest;
|
|
|
|
SEG::ecoord min_d_sq = VECTOR2I::ECOORD_MAX;
|
|
|
|
|
|
|
|
// Snap by distance between cursor and intersections
|
|
|
|
for( const VECTOR2I& pt : points )
|
|
|
|
{
|
|
|
|
SEG::ecoord d_sq = ( pt - aCenter ).SquaredEuclideanNorm();
|
|
|
|
|
|
|
|
if( d_sq < min_d_sq - c_snapEpsilon_sq )
|
|
|
|
{
|
|
|
|
min_d_sq = d_sq;
|
|
|
|
nearest = pt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( nearest )
|
|
|
|
aArc.SetCenter( *nearest );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move an end point of the arc around the circumference.
|
|
|
|
*/
|
|
|
|
static void editArcEndpointKeepCenter( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
|
|
|
|
const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCursor )
|
|
|
|
{
|
|
|
|
int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
|
|
|
|
bool movingStart;
|
|
|
|
|
|
|
|
VECTOR2I p1, p2, prev_p1;
|
|
|
|
|
|
|
|
// user is moving p1, we want to move p2 to the new radius.
|
|
|
|
|
|
|
|
if( aStart != aArc.GetStart() )
|
|
|
|
{
|
|
|
|
prev_p1 = aArc.GetStart();
|
|
|
|
p1 = aStart;
|
|
|
|
p2 = aEnd;
|
|
|
|
movingStart = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
prev_p1 = aArc.GetEnd();
|
|
|
|
p1 = aEnd;
|
|
|
|
p2 = aStart;
|
|
|
|
movingStart = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
p1 = p1 - aCenter;
|
|
|
|
p2 = p2 - aCenter;
|
|
|
|
|
|
|
|
if( p1.x == 0 && p1.y == 0 )
|
|
|
|
p1 = prev_p1 - aCenter;
|
|
|
|
|
|
|
|
if( p2.x == 0 && p2.y == 0 )
|
|
|
|
p2 = { 1, 0 };
|
|
|
|
|
|
|
|
double radius = p1.EuclideanNorm();
|
|
|
|
|
|
|
|
if( radius < minRadius )
|
|
|
|
radius = minRadius;
|
|
|
|
|
|
|
|
p1 = aCenter + p1.Resize( radius );
|
|
|
|
p2 = aCenter + p2.Resize( radius );
|
|
|
|
|
|
|
|
aArc.SetCenter( aCenter );
|
|
|
|
|
|
|
|
if( movingStart )
|
|
|
|
{
|
|
|
|
aArc.SetStart( p1 );
|
|
|
|
aArc.SetEnd( p2 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aArc.SetStart( p2 );
|
|
|
|
aArc.SetEnd( p1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void editArcEndpointKeepCenterAndRadius( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
|
|
|
|
const VECTOR2I& aMid, const VECTOR2I& aEnd )
|
|
|
|
{
|
|
|
|
VECTOR2I p1;
|
|
|
|
bool movingStart = false;
|
|
|
|
|
|
|
|
// User is moving p1, we need to update whichever end that is
|
|
|
|
// The other end won't move.
|
|
|
|
|
|
|
|
if( aStart != aArc.GetStart() )
|
|
|
|
{
|
|
|
|
p1 = aStart;
|
|
|
|
movingStart = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p1 = aEnd;
|
|
|
|
movingStart = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do not change the radius
|
|
|
|
p1 = p1 - aCenter;
|
|
|
|
p1 = aCenter + p1.Resize( aArc.GetRadius() );
|
|
|
|
|
|
|
|
if( movingStart )
|
|
|
|
{
|
|
|
|
aArc.SetStart( p1 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aArc.SetEnd( p1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move the mid point of the arc, while keeping the two endpoints.
|
|
|
|
*/
|
|
|
|
static void editArcMidKeepCenter( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
|
|
|
|
const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCursor )
|
|
|
|
{
|
|
|
|
int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
|
|
|
|
|
|
|
|
SEG chord( aStart, aEnd );
|
|
|
|
|
|
|
|
// Now, update the edit point position
|
|
|
|
// Express the point in a circle-centered coordinate system.
|
|
|
|
VECTOR2I start = aStart - aCenter;
|
|
|
|
VECTOR2I end = aEnd - aCenter;
|
|
|
|
|
|
|
|
double radius = ( aCursor - aCenter ).EuclideanNorm();
|
|
|
|
|
|
|
|
if( radius < minRadius )
|
|
|
|
radius = minRadius;
|
|
|
|
|
|
|
|
start = start.Resize( radius );
|
|
|
|
end = end.Resize( radius );
|
|
|
|
|
|
|
|
start = start + aCenter;
|
|
|
|
end = end + aCenter;
|
|
|
|
|
|
|
|
aArc.SetStart( start );
|
|
|
|
aArc.SetEnd( end );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move the mid point of the arc, while keeping the angle.
|
|
|
|
*/
|
|
|
|
static void editArcMidKeepEndpoints( EDA_SHAPE& aArc, const VECTOR2I& aStart, const VECTOR2I& aEnd,
|
|
|
|
const VECTOR2I& aCursor )
|
|
|
|
{
|
|
|
|
// Let 'm' be the middle point of the chord between the start and end points
|
|
|
|
VECTOR2I m = ( aStart + aEnd ) / 2;
|
|
|
|
|
|
|
|
// Legal midpoints lie on a vector starting just off the chord midpoint and extending out
|
|
|
|
// past the existing midpoint. We do not allow arc inflection while point editing.
|
|
|
|
const int JUST_OFF = ( aStart - aEnd ).EuclideanNorm() / 100;
|
|
|
|
VECTOR2I v = (VECTOR2I) aArc.GetArcMid() - m;
|
|
|
|
SEG legal( m + v.Resize( JUST_OFF ), m + v.Resize( INT_MAX / 2 ) );
|
|
|
|
VECTOR2I mid = legal.NearestPoint( aCursor );
|
|
|
|
|
|
|
|
aArc.SetArcGeometry( aStart, mid, aEnd );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EDA_ARC_POINT_EDIT_BEHAVIOR::EDA_ARC_POINT_EDIT_BEHAVIOR( EDA_SHAPE& aArc, const ARC_EDIT_MODE& aArcEditMode,
|
|
|
|
KIGFX::VIEW_CONTROLS& aViewContols ) :
|
|
|
|
m_arc( aArc ), m_arcEditMode( aArcEditMode ), m_viewControls( aViewContols )
|
|
|
|
{
|
|
|
|
wxASSERT( m_arc.GetShape() == SHAPE_T::ARC );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EDA_ARC_POINT_EDIT_BEHAVIOR::MakePoints( EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
aPoints.AddPoint( m_arc.GetStart() );
|
|
|
|
aPoints.AddPoint( m_arc.GetArcMid() );
|
|
|
|
aPoints.AddPoint( m_arc.GetEnd() );
|
|
|
|
aPoints.AddPoint( m_arc.getCenter() );
|
|
|
|
|
|
|
|
aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_START ) );
|
|
|
|
aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_END ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EDA_ARC_POINT_EDIT_BEHAVIOR::UpdatePoints( EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
CHECK_POINT_COUNT( aPoints, 4 );
|
|
|
|
|
|
|
|
aPoints.Point( ARC_START ).SetPosition( m_arc.GetStart() );
|
|
|
|
aPoints.Point( ARC_MID ).SetPosition( m_arc.GetArcMid() );
|
|
|
|
aPoints.Point( ARC_END ).SetPosition( m_arc.GetEnd() );
|
|
|
|
aPoints.Point( ARC_CENTER ).SetPosition( m_arc.getCenter() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EDA_ARC_POINT_EDIT_BEHAVIOR::UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
|
|
|
|
std::vector<EDA_ITEM*>& aUpdatedItems )
|
|
|
|
{
|
|
|
|
CHECK_POINT_COUNT( aPoints, 4 );
|
|
|
|
|
|
|
|
VECTOR2I center = aPoints.Point( ARC_CENTER ).GetPosition();
|
|
|
|
VECTOR2I mid = aPoints.Point( ARC_MID ).GetPosition();
|
|
|
|
VECTOR2I start = aPoints.Point( ARC_START ).GetPosition();
|
|
|
|
VECTOR2I end = aPoints.Point( ARC_END ).GetPosition();
|
|
|
|
|
|
|
|
if( isModified( aEditedPoint, aPoints.Point( ARC_CENTER ) ) )
|
|
|
|
{
|
|
|
|
switch( m_arcEditMode )
|
|
|
|
{
|
|
|
|
case ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION:
|
|
|
|
editArcCenterKeepEndpoints( m_arc, center, start, mid, end );
|
|
|
|
break;
|
|
|
|
case ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS:
|
|
|
|
case ARC_EDIT_MODE::KEEP_CENTER_ENDS_ADJUST_ANGLE:
|
|
|
|
{
|
|
|
|
// Both these modes just move the arc
|
|
|
|
VECTOR2I moveVector = VECTOR2I( center.x, center.y ) - m_arc.getCenter();
|
|
|
|
|
|
|
|
m_arc.SetArcGeometry( m_arc.GetStart() + moveVector, m_arc.GetArcMid() + moveVector,
|
|
|
|
m_arc.GetEnd() + moveVector );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( isModified( aEditedPoint, aPoints.Point( ARC_MID ) ) )
|
|
|
|
{
|
|
|
|
const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition( false );
|
|
|
|
|
|
|
|
switch( m_arcEditMode )
|
|
|
|
{
|
|
|
|
case ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION:
|
|
|
|
editArcMidKeepEndpoints( m_arc, start, end, cursorPos );
|
|
|
|
break;
|
|
|
|
case ARC_EDIT_MODE::KEEP_CENTER_ENDS_ADJUST_ANGLE:
|
|
|
|
case ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS:
|
|
|
|
editArcMidKeepCenter( m_arc, center, start, mid, end, cursorPos );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( isModified( aEditedPoint, aPoints.Point( ARC_START ) )
|
|
|
|
|| isModified( aEditedPoint, aPoints.Point( ARC_END ) ) )
|
|
|
|
{
|
|
|
|
const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition();
|
|
|
|
|
|
|
|
switch( m_arcEditMode )
|
|
|
|
{
|
|
|
|
case ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS:
|
|
|
|
editArcEndpointKeepCenter( m_arc, center, start, mid, end, cursorPos );
|
|
|
|
break;
|
|
|
|
case ARC_EDIT_MODE::KEEP_CENTER_ENDS_ADJUST_ANGLE:
|
|
|
|
editArcEndpointKeepCenterAndRadius( m_arc, center, start, mid, end );
|
|
|
|
break;
|
|
|
|
case ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION:
|
|
|
|
editArcEndpointKeepTangent( m_arc, center, start, mid, end, cursorPos );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
OPT_VECTOR2I EDA_ARC_POINT_EDIT_BEHAVIOR::Get45DegreeConstrainer( const EDIT_POINT& aEditedPoint,
|
|
|
|
EDIT_POINTS& aPoints ) const
|
|
|
|
{
|
|
|
|
return aPoints.Point( ARC_CENTER ).GetPosition();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Point editor: unify segment, circle, bezier and tablecells (partial)
For segment, circle, beziers, these are the same in all editors and only
need access to the EDA_SHAPE nature, so we can remove the duplication
entirely.
For TABLECELLs, while the cells are polymorphic in that PCB and SCH
cells are both EDA_SHAPEs (via the TEXTBOXes), the parent TABLES
are not polymorphic, and thus the implementation can't be trivially
de-duplicated. Rather than do something with templates, just keep
it simple for now and maybe look at unifying tables later on.
2024-11-03 23:15:32 +08:00
|
|
|
void EDA_TABLECELL_POINT_EDIT_BEHAVIOR::MakePoints( EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
aPoints.AddPoint( m_cell.GetEnd() - VECTOR2I( 0, m_cell.GetRectangleHeight() / 2 ) );
|
|
|
|
aPoints.AddPoint( m_cell.GetEnd() - VECTOR2I( m_cell.GetRectangleWidth() / 2, 0 ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EDA_TABLECELL_POINT_EDIT_BEHAVIOR::UpdatePoints( EDIT_POINTS& aPoints )
|
|
|
|
{
|
|
|
|
aPoints.Point( COL_WIDTH )
|
|
|
|
.SetPosition( m_cell.GetEnd() - VECTOR2I( 0, m_cell.GetRectangleHeight() / 2 ) );
|
|
|
|
aPoints.Point( ROW_HEIGHT )
|
|
|
|
.SetPosition( m_cell.GetEnd() - VECTOR2I( m_cell.GetRectangleWidth() / 2, 0 ) );
|
|
|
|
}
|
2025-05-02 14:49:21 +08:00
|
|
|
|
|
|
|
|
|
|
|
ARC_EDIT_MODE IncrementArcEditMode( ARC_EDIT_MODE aMode )
|
|
|
|
{
|
|
|
|
switch( aMode )
|
|
|
|
{
|
|
|
|
case ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS:
|
|
|
|
return ARC_EDIT_MODE::KEEP_CENTER_ENDS_ADJUST_ANGLE;
|
|
|
|
case ARC_EDIT_MODE::KEEP_CENTER_ENDS_ADJUST_ANGLE:
|
|
|
|
return ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION;
|
|
|
|
case ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION:
|
|
|
|
return ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS;
|
|
|
|
default:
|
|
|
|
wxFAIL_MSG( "Invalid arc edit mode" );
|
|
|
|
return aMode;
|
|
|
|
}
|
|
|
|
}
|