CHANGED: Dragging a curved track starts an interactive resizing of it, keeping start an end points tangent
This commit is contained in:
parent
25956152c6
commit
2b5c1bae97
|
@ -4,6 +4,7 @@ set( KIMATH_SRCS
|
||||||
src/md5_hash.cpp
|
src/md5_hash.cpp
|
||||||
src/trigo.cpp
|
src/trigo.cpp
|
||||||
|
|
||||||
|
src/geometry/circle.cpp
|
||||||
src/geometry/convex_hull.cpp
|
src/geometry/convex_hull.cpp
|
||||||
src/geometry/direction_45.cpp
|
src/geometry/direction_45.cpp
|
||||||
src/geometry/geometry_utils.cpp
|
src/geometry/geometry_utils.cpp
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
|
||||||
|
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __CIRCLE_H
|
||||||
|
#define __CIRCLE_H
|
||||||
|
|
||||||
|
#include <math/vector2d.h> // for VECTOR2I
|
||||||
|
#include <vector> // for std::vector
|
||||||
|
|
||||||
|
class SEG;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Circle
|
||||||
|
* Represents basic circle geometry with utility geometry functions.
|
||||||
|
*/
|
||||||
|
class CIRCLE
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int Radius; ///< Public to make access simpler
|
||||||
|
VECTOR2I Center; ///< Public to make access simpler
|
||||||
|
|
||||||
|
CIRCLE();
|
||||||
|
|
||||||
|
CIRCLE( const VECTOR2I& aCenter, int aRadius );
|
||||||
|
|
||||||
|
CIRCLE( const CIRCLE& aOther );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs this circle such that it is tangent to the given lines and passes through the
|
||||||
|
* given point. There are two possible solutions, controlled by aAlternateSolution.
|
||||||
|
*
|
||||||
|
* When aAlternateSolution is false, find the best solution that can be used to fillet both
|
||||||
|
* lines (i.e. choose the most likely quadrant and find the solution with smallest arc angle
|
||||||
|
* between the tangent points on the lines)
|
||||||
|
*
|
||||||
|
* @param aLineA is the first tangent line. Treated as an infinite line except for the purpose
|
||||||
|
* of selecting the solution to return.
|
||||||
|
* @param aLineB is the second tangent line. Treated as an infinite line except for the purpose
|
||||||
|
* of selecting the solution to return.
|
||||||
|
* @param aP is the point to pass through
|
||||||
|
* @param aAlternateSolution If true, returns the other solution.
|
||||||
|
* @return *this
|
||||||
|
*/
|
||||||
|
CIRCLE& ConstructFromTanTanPt( const SEG& aLineA, const SEG& aLineB, const VECTOR2I& aP,
|
||||||
|
bool aAlternateSolution = false );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function NearestPoint()
|
||||||
|
*
|
||||||
|
* Computes the point on the circumference of the circle that is the closest to aP.
|
||||||
|
*
|
||||||
|
* In other words: finds the intersection point of this circle and a line that passes through
|
||||||
|
* both this circle's center and aP.
|
||||||
|
*
|
||||||
|
* @param aP
|
||||||
|
* @return nearest point to aP
|
||||||
|
*/
|
||||||
|
VECTOR2I NearestPoint( const VECTOR2I& aP ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function Intersect()
|
||||||
|
*
|
||||||
|
* Computes the intersection points between this circle and aCircle.
|
||||||
|
*
|
||||||
|
* @param aCircle The other circle to intersect with this.
|
||||||
|
* @return std::vector containing:
|
||||||
|
* - 0 elements if the circles do not intersect
|
||||||
|
* - 1 element if the circles are tangent
|
||||||
|
* - 2 elements if the circles intersect
|
||||||
|
*/
|
||||||
|
std::vector<VECTOR2I> Intersect( const CIRCLE& aCircle ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function Intersect()
|
||||||
|
*
|
||||||
|
* Computes the intersection points between this circle and aLine.
|
||||||
|
*
|
||||||
|
* @param aLine The line to intersect with this circle (end points ignored)
|
||||||
|
* @return std::vector containing:
|
||||||
|
* - 0 elements if there is no intersection
|
||||||
|
* - 1 element if the line is tangent to the circle
|
||||||
|
* - 2 elements if the line intersects the circle
|
||||||
|
*/
|
||||||
|
std::vector<VECTOR2I> Intersect( const SEG& aLine ) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __CIRCLE_H
|
||||||
|
|
|
@ -200,6 +200,24 @@ public:
|
||||||
return Intersect( aSeg, false, true );
|
return Intersect( aSeg, false, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function PerpendicularSeg()
|
||||||
|
*
|
||||||
|
* Computes a segment perpendicular to this one, passing through point aP
|
||||||
|
* @param aP Point through which the new segment will pass
|
||||||
|
* @return SEG perpendicular to this passing through point aP
|
||||||
|
*/
|
||||||
|
SEG PerpendicularSeg( const VECTOR2I& aP ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function ParallelSeg()
|
||||||
|
*
|
||||||
|
* Computes a segment parallel to this one, passing through point aP
|
||||||
|
* @param aP Point through which the new segment will pass
|
||||||
|
* @return SEG parallel to this passing through point aP
|
||||||
|
*/
|
||||||
|
SEG ParallelSeg( const VECTOR2I& aP ) const;
|
||||||
|
|
||||||
bool Collide( const SEG& aSeg, int aClearance, int* aActual = nullptr ) const;
|
bool Collide( const SEG& aSeg, int aClearance, int* aActual = nullptr ) const;
|
||||||
|
|
||||||
ecoord SquaredDistance( const SEG& aSeg ) const;
|
ecoord SquaredDistance( const SEG& aSeg ) const;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#define __SHAPE_CIRCLE_H
|
#define __SHAPE_CIRCLE_H
|
||||||
|
|
||||||
#include <geometry/shape.h>
|
#include <geometry/shape.h>
|
||||||
|
#include <geometry/circle.h>
|
||||||
#include <math/box2.h>
|
#include <math/box2.h>
|
||||||
#include <math/vector2d.h>
|
#include <math/vector2d.h>
|
||||||
|
|
||||||
|
@ -36,19 +37,17 @@ class SHAPE_CIRCLE : public SHAPE
|
||||||
public:
|
public:
|
||||||
SHAPE_CIRCLE() :
|
SHAPE_CIRCLE() :
|
||||||
SHAPE( SH_CIRCLE ),
|
SHAPE( SH_CIRCLE ),
|
||||||
m_radius( 0 )
|
m_circle()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
SHAPE_CIRCLE( const VECTOR2I& aCenter, int aRadius ) :
|
SHAPE_CIRCLE( const VECTOR2I& aCenter, int aRadius ) :
|
||||||
SHAPE( SH_CIRCLE ),
|
SHAPE( SH_CIRCLE ),
|
||||||
m_radius( aRadius ),
|
m_circle( aCenter, aRadius )
|
||||||
m_center( aCenter )
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
SHAPE_CIRCLE( const SHAPE_CIRCLE& aOther ) :
|
SHAPE_CIRCLE( const SHAPE_CIRCLE& aOther ) :
|
||||||
SHAPE( SH_CIRCLE ),
|
SHAPE( SH_CIRCLE ),
|
||||||
m_radius( aOther.m_radius ),
|
m_circle( aOther.m_circle )
|
||||||
m_center( aOther.m_center )
|
|
||||||
{};
|
{};
|
||||||
|
|
||||||
~SHAPE_CIRCLE()
|
~SHAPE_CIRCLE()
|
||||||
|
@ -63,17 +62,17 @@ public:
|
||||||
|
|
||||||
const BOX2I BBox( int aClearance = 0 ) const override
|
const BOX2I BBox( int aClearance = 0 ) const override
|
||||||
{
|
{
|
||||||
const VECTOR2I rc( m_radius + aClearance, m_radius + aClearance );
|
const VECTOR2I rc( m_circle.Radius + aClearance, m_circle.Radius + aClearance );
|
||||||
|
|
||||||
return BOX2I( m_center - rc, rc * 2 );
|
return BOX2I( m_circle.Center - rc, rc * 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr,
|
bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr,
|
||||||
VECTOR2I* aLocation = nullptr ) const override
|
VECTOR2I* aLocation = nullptr ) const override
|
||||||
{
|
{
|
||||||
int minDist = aClearance + m_radius;
|
int minDist = aClearance + m_circle.Radius;
|
||||||
VECTOR2I pn = aSeg.NearestPoint( m_center );
|
VECTOR2I pn = aSeg.NearestPoint( m_circle.Center );
|
||||||
ecoord dist_sq = ( pn - m_center ).SquaredEuclideanNorm();
|
ecoord dist_sq = ( pn - m_circle.Center ).SquaredEuclideanNorm();
|
||||||
|
|
||||||
if( dist_sq == 0 || dist_sq < SEG::Square( minDist ) )
|
if( dist_sq == 0 || dist_sq < SEG::Square( minDist ) )
|
||||||
{
|
{
|
||||||
|
@ -81,7 +80,7 @@ public:
|
||||||
*aLocation = pn;
|
*aLocation = pn;
|
||||||
|
|
||||||
if( aActual )
|
if( aActual )
|
||||||
*aActual = std::max( 0, (int) sqrt( dist_sq ) - m_radius );
|
*aActual = std::max( 0, (int) sqrt( dist_sq ) - m_circle.Radius );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -91,34 +90,39 @@ public:
|
||||||
|
|
||||||
void SetRadius( int aRadius )
|
void SetRadius( int aRadius )
|
||||||
{
|
{
|
||||||
m_radius = aRadius;
|
m_circle.Radius = aRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetCenter( const VECTOR2I& aCenter )
|
void SetCenter( const VECTOR2I& aCenter )
|
||||||
{
|
{
|
||||||
m_center = aCenter;
|
m_circle.Center = aCenter;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetRadius() const
|
int GetRadius() const
|
||||||
{
|
{
|
||||||
return m_radius;
|
return m_circle.Radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VECTOR2I GetCenter() const
|
const VECTOR2I GetCenter() const
|
||||||
{
|
{
|
||||||
return m_center;
|
return m_circle.Center;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CIRCLE GetCircle() const
|
||||||
|
{
|
||||||
|
return m_circle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Move( const VECTOR2I& aVector ) override
|
void Move( const VECTOR2I& aVector ) override
|
||||||
{
|
{
|
||||||
m_center += aVector;
|
m_circle.Center += aVector;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ) override
|
void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ) override
|
||||||
{
|
{
|
||||||
m_center -= aCenter;
|
m_circle.Center -= aCenter;
|
||||||
m_center = m_center.Rotate( aAngle );
|
m_circle.Center = m_circle.Center.Rotate( aAngle );
|
||||||
m_center += aCenter;
|
m_circle.Center += aCenter;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsSolid() const override
|
bool IsSolid() const override
|
||||||
|
@ -127,8 +131,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_radius;
|
CIRCLE m_circle;
|
||||||
VECTOR2I m_center;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -118,6 +118,20 @@ const wxPoint GetArcCenter( VECTOR2I aStart, VECTOR2I aEnd, double aAngle );
|
||||||
*/
|
*/
|
||||||
double GetArcAngle( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd );
|
double GetArcAngle( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the middle point of an arc, half-way between aStart and aEnd. There are two possible
|
||||||
|
* solutions which can be found by toggling aMinArcAngle. The behaviour is undefined for
|
||||||
|
* semicircles (i.e. 180 degree arcs).
|
||||||
|
*
|
||||||
|
* @param aStart The starting point of the arc (for calculating the radius)
|
||||||
|
* @param aEnd The end point of the arc (for determining the arc angle)
|
||||||
|
* @param aCenter The center point of the arc
|
||||||
|
* @param aMinArcAngle If true, returns the point that results in the smallest arc angle.
|
||||||
|
* @return The middle point of the arc
|
||||||
|
*/
|
||||||
|
const VECTOR2I GetArcMid( const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCenter,
|
||||||
|
bool aMinArcAngle = true );
|
||||||
|
|
||||||
/* Return the arc tangent of 0.1 degrees coord vector dx, dy
|
/* Return the arc tangent of 0.1 degrees coord vector dx, dy
|
||||||
* between -1800 and 1800
|
* between -1800 and 1800
|
||||||
* Equivalent to atan2 (but faster for calculations if
|
* Equivalent to atan2 (but faster for calculations if
|
||||||
|
|
|
@ -0,0 +1,320 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
|
||||||
|
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <geometry/circle.h>
|
||||||
|
#include <geometry/seg.h>
|
||||||
|
#include <geometry/shape_arc.h> // for MIN_PRECISION_IU
|
||||||
|
#include <math/util.h> // for KiROUND
|
||||||
|
#include <math/vector2d.h> // for VECTOR2I
|
||||||
|
#include <math.h> // for sqrt
|
||||||
|
#include <trigo.h> // for GetArcMid
|
||||||
|
|
||||||
|
|
||||||
|
CIRCLE::CIRCLE()
|
||||||
|
{
|
||||||
|
Radius = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CIRCLE::CIRCLE( const VECTOR2I& aCenter, int aRadius )
|
||||||
|
{
|
||||||
|
Center = aCenter;
|
||||||
|
Radius = aRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CIRCLE::CIRCLE( const CIRCLE& aOther )
|
||||||
|
{
|
||||||
|
Center = aOther.Center;
|
||||||
|
Radius = aOther.Radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CIRCLE& CIRCLE::ConstructFromTanTanPt( const SEG& aLineA, const SEG& aLineB, const VECTOR2I& aP,
|
||||||
|
bool aAlternateSolution )
|
||||||
|
{
|
||||||
|
//fixme: There might be more efficient / accurate solution than using geometrical constructs
|
||||||
|
|
||||||
|
SEG anglebisector;
|
||||||
|
VECTOR2I intersectPoint;
|
||||||
|
|
||||||
|
auto furthestFromIntersect =
|
||||||
|
[&]( VECTOR2I aPt1, VECTOR2I aPt2 ) -> VECTOR2I&
|
||||||
|
{
|
||||||
|
if( ( aPt1 - intersectPoint ).EuclideanNorm()
|
||||||
|
> ( aPt2 - intersectPoint ).EuclideanNorm() )
|
||||||
|
{
|
||||||
|
return aPt1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return aPt2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto closestToIntersect =
|
||||||
|
[&]( VECTOR2I aPt1, VECTOR2I aPt2 ) -> VECTOR2I&
|
||||||
|
{
|
||||||
|
if( ( aPt1 - intersectPoint ).EuclideanNorm()
|
||||||
|
<= ( aPt2 - intersectPoint ).EuclideanNorm() )
|
||||||
|
{
|
||||||
|
return aPt1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return aPt2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if( aLineA.ApproxParallel( aLineB ) )
|
||||||
|
{
|
||||||
|
// Special case, no intersection point between the two lines
|
||||||
|
// The center will be in the line equidistant between the two given lines
|
||||||
|
// The radius will be half the distance between the two lines
|
||||||
|
// The possible centers can be found by intersection
|
||||||
|
|
||||||
|
SEG perpendicularAtoB( aLineA.A, aLineB.LineProject( aLineA.A ) );
|
||||||
|
VECTOR2I midPt = perpendicularAtoB.Center();
|
||||||
|
Radius = ( midPt - aLineA.A ).EuclideanNorm();
|
||||||
|
|
||||||
|
anglebisector = aLineA.ParallelSeg( midPt );
|
||||||
|
|
||||||
|
Center = aP; // use this circle as a construction to find the actual centers
|
||||||
|
std::vector<VECTOR2I> possibleCenters = Intersect( anglebisector );
|
||||||
|
|
||||||
|
wxCHECK_MSG( possibleCenters.size() > 0, *this, "No solutions exist!" );
|
||||||
|
intersectPoint = aLineA.A; // just for the purpose of deciding which solution to return
|
||||||
|
|
||||||
|
if( aAlternateSolution )
|
||||||
|
Center = closestToIntersect( possibleCenters.front(), possibleCenters.back() );
|
||||||
|
else
|
||||||
|
Center = furthestFromIntersect( possibleCenters.front(), possibleCenters.back() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// General case, using homothety.
|
||||||
|
// All circles inscribed in the same angle are homothetic with center at the intersection
|
||||||
|
// In this code, the prefix "h" denotes "the homothetic image"
|
||||||
|
OPT_VECTOR2I intersectCalc = aLineA.IntersectLines( aLineB );
|
||||||
|
wxCHECK_MSG( intersectCalc, *this, "Lines do not intersect but are not parallel?" );
|
||||||
|
intersectPoint = intersectCalc.get();
|
||||||
|
|
||||||
|
if( aP == intersectPoint )
|
||||||
|
{
|
||||||
|
//Special case: The point is at the intersection of the two lines
|
||||||
|
Center = aP;
|
||||||
|
Radius = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate bisector
|
||||||
|
VECTOR2I& lineApt = furthestFromIntersect( aLineA.A, aLineA.B );
|
||||||
|
VECTOR2I& lineBpt = furthestFromIntersect( aLineB.A, aLineB.B );
|
||||||
|
VECTOR2I bisectorPt = GetArcMid( lineApt, lineBpt, intersectPoint, true );
|
||||||
|
|
||||||
|
anglebisector.A = intersectPoint;
|
||||||
|
anglebisector.B = bisectorPt;
|
||||||
|
|
||||||
|
if( aAlternateSolution && ( aLineA.Contains( aP ) || aLineB.Contains( aP ) ) )
|
||||||
|
anglebisector.PerpendicularSeg( intersectPoint );
|
||||||
|
|
||||||
|
// Create an arbitrary circle that is tangent to both lines
|
||||||
|
CIRCLE hSolution;
|
||||||
|
hSolution.Center = anglebisector.LineProject( aP );
|
||||||
|
hSolution.Radius = aLineA.LineDistance( hSolution.Center );
|
||||||
|
|
||||||
|
// Find the homothetic image of aP in the construction circle (hSolution)
|
||||||
|
SEG throughaP( intersectPoint, aP );
|
||||||
|
std::vector<VECTOR2I> hProjections = hSolution.Intersect( throughaP );
|
||||||
|
wxCHECK_MSG( hProjections.size() > 0, *this, "No solutions exist!" );
|
||||||
|
|
||||||
|
VECTOR2I hSelected;
|
||||||
|
|
||||||
|
if( aAlternateSolution )
|
||||||
|
hSelected = furthestFromIntersect( hProjections.front(), hProjections.back() );
|
||||||
|
else
|
||||||
|
hSelected = closestToIntersect( hProjections.front(), hProjections.back() );
|
||||||
|
|
||||||
|
VECTOR2I hTanLineA = aLineA.LineProject( hSolution.Center );
|
||||||
|
VECTOR2I hTanLineB = aLineB.LineProject( hSolution.Center );
|
||||||
|
|
||||||
|
// To minimise errors, use the furthest away tangent point from aP
|
||||||
|
if( ( hTanLineA - aP ).EuclideanNorm() > ( hTanLineB - aP ).EuclideanNorm() )
|
||||||
|
{
|
||||||
|
// Find the tangent at line A by homothetic inversion
|
||||||
|
SEG hT( hTanLineA, hSelected );
|
||||||
|
OPT_VECTOR2I actTanA = hT.ParallelSeg( aP ).IntersectLines( aLineA );
|
||||||
|
wxCHECK_MSG( actTanA, *this, "No solutions exist!" );
|
||||||
|
|
||||||
|
// Find circle center by perpendicular intersection with the angle bisector
|
||||||
|
SEG perpendicularToTanA = aLineA.PerpendicularSeg( actTanA.get() );
|
||||||
|
OPT_VECTOR2I actCenter = perpendicularToTanA.IntersectLines( anglebisector );
|
||||||
|
wxCHECK_MSG( actCenter, *this, "No solutions exist!" );
|
||||||
|
|
||||||
|
Center = actCenter.get();
|
||||||
|
Radius = aLineA.LineDistance( Center );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Find the tangent at line B by inversion
|
||||||
|
SEG hT( hTanLineB, hSelected );
|
||||||
|
OPT_VECTOR2I actTanB = hT.ParallelSeg( aP ).IntersectLines( aLineB );
|
||||||
|
wxCHECK_MSG( actTanB, *this, "No solutions exist!" );
|
||||||
|
|
||||||
|
// Find circle center by perpendicular intersection with the angle bisector
|
||||||
|
SEG perpendicularToTanB = aLineB.PerpendicularSeg( actTanB.get() );
|
||||||
|
OPT_VECTOR2I actCenter = perpendicularToTanB.IntersectLines( anglebisector );
|
||||||
|
wxCHECK_MSG( actCenter, *this, "No solutions exist!" );
|
||||||
|
|
||||||
|
Center = actCenter.get();
|
||||||
|
Radius = aLineB.LineDistance( Center );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VECTOR2I CIRCLE::NearestPoint( const VECTOR2I& aP ) const
|
||||||
|
{
|
||||||
|
VECTOR2I vec = aP - Center;
|
||||||
|
|
||||||
|
// Handle special case where aP is equal to this circle's center
|
||||||
|
if( vec.x == 0 && vec.y == 0 )
|
||||||
|
vec.x = 1; // Arbitrary, to ensure the return value is always on the circumference
|
||||||
|
|
||||||
|
return vec.Resize( Radius ) + Center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<VECTOR2I> CIRCLE::Intersect( const CIRCLE& aCircle ) const
|
||||||
|
{
|
||||||
|
// Simplify the problem:
|
||||||
|
// Let this circle be centered at (0,0), with radius r1
|
||||||
|
// Let aCircle be centered at (d, 0), with radius r2
|
||||||
|
// (i.e. d is the distance between the two circle centers)
|
||||||
|
//
|
||||||
|
// The equations of the two circles are
|
||||||
|
// (1) x^2 + y^2 = r1^2
|
||||||
|
// (2) (x - d)^2 + y^2 = r2^2
|
||||||
|
//
|
||||||
|
// Combining (1) into (2):
|
||||||
|
// (x - d)^2 + r1^2 - x^2 = r2^2
|
||||||
|
// Expanding:
|
||||||
|
// x^2 - 2*d*x + d^2 + r1^2 - x^2 = r2^2
|
||||||
|
// Rearranging for x:
|
||||||
|
// (3) x = (d^2 + r1^2 - r2^2) / (2 * d)
|
||||||
|
//
|
||||||
|
// Rearranging (1) gives:
|
||||||
|
// (4) y = sqrt(r1^2 - x^2)
|
||||||
|
|
||||||
|
std::vector<VECTOR2I> retval;
|
||||||
|
|
||||||
|
VECTOR2I vecCtoC = aCircle.Center - Center;
|
||||||
|
int64_t d = vecCtoC.EuclideanNorm();
|
||||||
|
int64_t r1 = Radius;
|
||||||
|
int64_t r2 = aCircle.Radius;
|
||||||
|
|
||||||
|
if( d > ( r1 + r2 ) || d == 0 )
|
||||||
|
return retval; //circles do not intersect
|
||||||
|
|
||||||
|
// Equation (3)
|
||||||
|
int64_t x = ( ( d * d ) + ( r1 * r1 ) - ( r2 * r2 ) ) / ( int64_t( 2 ) * d );
|
||||||
|
int64_t r1sqMinusXsq = ( r1 * r1 ) - ( x * x );
|
||||||
|
|
||||||
|
if( r1sqMinusXsq < 0 )
|
||||||
|
return retval; //circles do not intersect
|
||||||
|
|
||||||
|
// Equation (4)
|
||||||
|
int64_t y = KiROUND( sqrt( r1sqMinusXsq ) );
|
||||||
|
|
||||||
|
// Now correct back to original coordinates
|
||||||
|
double rotAngle = vecCtoC.Angle();
|
||||||
|
VECTOR2I solution1( x, y );
|
||||||
|
solution1 = solution1.Rotate( rotAngle );
|
||||||
|
solution1 += Center;
|
||||||
|
retval.push_back( solution1 );
|
||||||
|
|
||||||
|
if( y != 0 )
|
||||||
|
{
|
||||||
|
VECTOR2I solution2( x, -y );
|
||||||
|
solution2 = solution2.Rotate( rotAngle );
|
||||||
|
solution2 += Center;
|
||||||
|
retval.push_back( solution2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<VECTOR2I> CIRCLE::Intersect( const SEG& aLine ) const
|
||||||
|
{
|
||||||
|
std::vector<VECTOR2I> retval;
|
||||||
|
|
||||||
|
//
|
||||||
|
// . * .
|
||||||
|
// * *
|
||||||
|
// -----1-------m-------2----
|
||||||
|
// * *
|
||||||
|
// * O *
|
||||||
|
// * *
|
||||||
|
// * *
|
||||||
|
// * *
|
||||||
|
// * *
|
||||||
|
// ' * '
|
||||||
|
// Let O be the center of this circle, 1 and 2 the intersection points of the line
|
||||||
|
// and M be the center of the chord connecting points 1 and 2
|
||||||
|
//
|
||||||
|
// M will be O projected perpendicularly to the line since a chord is always perpendicular
|
||||||
|
// to the radius.
|
||||||
|
//
|
||||||
|
// The distance M1 = M2 can be computed by pythagoras since O1 = O2 = Radius
|
||||||
|
//
|
||||||
|
// O1= O2 = sqrt( Radius^2 - OM^2)
|
||||||
|
//
|
||||||
|
|
||||||
|
VECTOR2I m = aLine.LineProject( Center );
|
||||||
|
int64_t om = ( m - Center ).EuclideanNorm();
|
||||||
|
|
||||||
|
if( om > ( (int64_t) Radius + SHAPE_ARC::MIN_PRECISION_IU ) )
|
||||||
|
{
|
||||||
|
return retval; // does not intersect
|
||||||
|
}
|
||||||
|
else if( om <= ( (int64_t) Radius + SHAPE_ARC::MIN_PRECISION_IU )
|
||||||
|
&& om >= ( (int64_t) Radius - SHAPE_ARC::MIN_PRECISION_IU ) )
|
||||||
|
{
|
||||||
|
retval.push_back( m );
|
||||||
|
return retval; //tangent
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t radiusSquared = (int64_t) Radius * (int64_t) Radius;
|
||||||
|
int64_t omSquared = om * om;
|
||||||
|
|
||||||
|
int mTo1 = sqrt( radiusSquared - omSquared );
|
||||||
|
|
||||||
|
VECTOR2I mTo1vec = ( aLine.B - aLine.A ).Resize( mTo1 );
|
||||||
|
VECTOR2I mTo2vec = -mTo1vec;
|
||||||
|
|
||||||
|
retval.push_back( mTo1vec + m );
|
||||||
|
retval.push_back( mTo2vec + m );
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
|
@ -130,6 +130,24 @@ OPT_VECTOR2I SEG::Intersect( const SEG& aSeg, bool aIgnoreEndpoints, bool aLines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SEG SEG::PerpendicularSeg( const VECTOR2I& aP ) const
|
||||||
|
{
|
||||||
|
VECTOR2I slope( B - A );
|
||||||
|
VECTOR2I endPoint = slope.Perpendicular() + aP;
|
||||||
|
|
||||||
|
return SEG( aP, endPoint );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SEG SEG::ParallelSeg( const VECTOR2I& aP ) const
|
||||||
|
{
|
||||||
|
VECTOR2I slope( B - A );
|
||||||
|
VECTOR2I endPoint = slope + aP;
|
||||||
|
|
||||||
|
return SEG( aP, endPoint );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SEG::ccw( const VECTOR2I& aA, const VECTOR2I& aB, const VECTOR2I& aC ) const
|
bool SEG::ccw( const VECTOR2I& aA, const VECTOR2I& aB, const VECTOR2I& aC ) const
|
||||||
{
|
{
|
||||||
return (ecoord) ( aC.y - aA.y ) * ( aB.x - aA.x ) > (ecoord) ( aB.y - aA.y ) * ( aC.x - aA.x );
|
return (ecoord) ( aC.y - aA.y ) * ( aB.x - aA.x ) > (ecoord) ( aB.y - aA.y ) * ( aC.x - aA.x );
|
||||||
|
|
|
@ -159,6 +159,26 @@ bool TestSegmentHit( const wxPoint &aRefPoint, wxPoint aStart, wxPoint aEnd, int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const VECTOR2I GetArcMid( const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCenter,
|
||||||
|
bool aMinArcAngle )
|
||||||
|
{
|
||||||
|
VECTOR2I startVector = aStart - aCenter;
|
||||||
|
VECTOR2I endVector = aEnd - aCenter;
|
||||||
|
|
||||||
|
double startAngle = ArcTangente( startVector.y, startVector.x );
|
||||||
|
double endAngle = ArcTangente( endVector.y, endVector.x );
|
||||||
|
double midPointRotAngleDeciDeg = NormalizeAngle180( startAngle - endAngle ) / 2;
|
||||||
|
|
||||||
|
if( !aMinArcAngle )
|
||||||
|
midPointRotAngleDeciDeg += 1800.0;
|
||||||
|
|
||||||
|
VECTOR2I newMid = aStart;
|
||||||
|
RotatePoint( newMid, aCenter, midPointRotAngleDeciDeg );
|
||||||
|
|
||||||
|
return newMid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
double ArcTangente( int dy, int dx )
|
double ArcTangente( int dy, int dx )
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ using namespace std::placeholders;
|
||||||
#include <dialogs/dialog_track_via_properties.h>
|
#include <dialogs/dialog_track_via_properties.h>
|
||||||
#include <dialogs/dialog_unit_entry.h>
|
#include <dialogs/dialog_unit_entry.h>
|
||||||
#include <board_commit.h>
|
#include <board_commit.h>
|
||||||
|
#include <pcb_target.h>
|
||||||
#include <zone_filler.h>
|
#include <zone_filler.h>
|
||||||
|
|
||||||
|
|
||||||
|
@ -269,7 +270,294 @@ int EDIT_TOOL::Drag( const TOOL_EVENT& aEvent )
|
||||||
if( selection.Empty() )
|
if( selection.Empty() )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
invokeInlineRouter( mode );
|
if( selection.Size() == 1 && selection.Front()->Type() == PCB_ARC_T )
|
||||||
|
{
|
||||||
|
// TODO: This really should be done in PNS to ensure DRC is maintained, but for now
|
||||||
|
// it allows interactive editing of an arc track
|
||||||
|
return DragArcTrack( aEvent );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
invokeInlineRouter( mode );
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int EDIT_TOOL::DragArcTrack( const TOOL_EVENT& aEvent )
|
||||||
|
{
|
||||||
|
PCB_SELECTION& selection = m_selectionTool->GetSelection();
|
||||||
|
|
||||||
|
if( selection.Size() != 1 || selection.Front()->Type() != PCB_ARC_T )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Activate();
|
||||||
|
|
||||||
|
ARC* theArc = static_cast<ARC*>( selection.Front() );
|
||||||
|
m_commit->Modify( theArc );
|
||||||
|
|
||||||
|
KIGFX::VIEW_CONTROLS* controls = getViewControls();
|
||||||
|
|
||||||
|
controls->ShowCursor( true );
|
||||||
|
controls->SetAutoPan( true );
|
||||||
|
bool restore_state = false;
|
||||||
|
|
||||||
|
VECTOR2I arcCenter = theArc->GetCenter();
|
||||||
|
SEG tanStart = SEG( arcCenter, theArc->GetStart() ).PerpendicularSeg( theArc->GetStart() );
|
||||||
|
SEG tanEnd = SEG( arcCenter, theArc->GetEnd() ).PerpendicularSeg( theArc->GetEnd() );
|
||||||
|
|
||||||
|
if( !tanStart.ApproxParallel( tanEnd ) )
|
||||||
|
{
|
||||||
|
//Ensure the tangent segments are in the correct orientation
|
||||||
|
OPT_VECTOR2I tanIntersect = tanStart.IntersectLines( tanEnd );
|
||||||
|
tanStart.A = tanIntersect.get();
|
||||||
|
tanStart.B = theArc->GetStart();
|
||||||
|
tanEnd.A = tanIntersect.get();
|
||||||
|
tanEnd.B = theArc->GetEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
KICAD_T track_types[] = { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, EOT };
|
||||||
|
|
||||||
|
auto getUniqueConnectedTrack =
|
||||||
|
[&]( const VECTOR2I& aAnchor ) -> TRACK*
|
||||||
|
{
|
||||||
|
auto conn = board()->GetConnectivity();
|
||||||
|
auto itemsOnAnchor = conn->GetConnectedItemsAtAnchor( theArc, aAnchor, track_types );
|
||||||
|
TRACK* retval = nullptr;
|
||||||
|
|
||||||
|
if( itemsOnAnchor.size() == 1 && itemsOnAnchor.front()->Type() == PCB_TRACE_T )
|
||||||
|
{
|
||||||
|
retval = static_cast<TRACK*>( itemsOnAnchor.front() );
|
||||||
|
m_commit->Modify( retval );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retval = new TRACK( theArc->GetParent() );
|
||||||
|
retval->SetStart( (wxPoint) aAnchor );
|
||||||
|
retval->SetEnd( (wxPoint) aAnchor );
|
||||||
|
retval->SetNet( theArc->GetNet() );
|
||||||
|
retval->SetLayer( theArc->GetLayer() );
|
||||||
|
retval->SetWidth( theArc->GetWidth() );
|
||||||
|
retval->SetFlags( IS_NEW );
|
||||||
|
getView()->Add( retval );
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
};
|
||||||
|
|
||||||
|
TRACK* trackOnStart = getUniqueConnectedTrack( theArc->GetStart() );
|
||||||
|
TRACK* trackOnEnd = getUniqueConnectedTrack( theArc->GetEnd() );
|
||||||
|
|
||||||
|
if( !trackOnStart->IsNew() )
|
||||||
|
{
|
||||||
|
tanStart.A = trackOnStart->GetStart();
|
||||||
|
tanStart.B = trackOnStart->GetEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !trackOnEnd->IsNew() )
|
||||||
|
{
|
||||||
|
tanEnd.A = trackOnEnd->GetStart();
|
||||||
|
tanEnd.B = trackOnEnd->GetEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
OPT_VECTOR2I optInterectTan = tanStart.IntersectLines( tanEnd );
|
||||||
|
int tanStartSide = tanStart.Side( theArc->GetMid() );
|
||||||
|
int tanEndSide = tanEnd.Side( theArc->GetMid() );
|
||||||
|
|
||||||
|
bool isStartTrackOnStartPt = ( VECTOR2I( theArc->GetStart() ) - tanStart.A ).EuclideanNorm()
|
||||||
|
< ( VECTOR2I( theArc->GetStart() ) - tanStart.B ).EuclideanNorm();
|
||||||
|
|
||||||
|
bool isEndTrackOnStartPt = ( VECTOR2I( theArc->GetStart() ) - tanEnd.A ).EuclideanNorm()
|
||||||
|
< ( VECTOR2I( theArc->GetStart() ) - tanEnd.B ).EuclideanNorm();
|
||||||
|
|
||||||
|
// Calculate constraints
|
||||||
|
CIRCLE maxTangentCircle;
|
||||||
|
VECTOR2I startOther = ( isStartTrackOnStartPt ) ? tanStart.B : tanStart.A;
|
||||||
|
maxTangentCircle.ConstructFromTanTanPt( tanStart, tanEnd, startOther );
|
||||||
|
|
||||||
|
VECTOR2I maxTanPtStart = tanStart.LineProject( maxTangentCircle.Center );
|
||||||
|
VECTOR2I maxTanPtEnd = tanEnd.LineProject( maxTangentCircle.Center );
|
||||||
|
|
||||||
|
if( !tanEnd.Contains( maxTanPtEnd ) )
|
||||||
|
{
|
||||||
|
startOther = ( isEndTrackOnStartPt ) ? tanEnd.B : tanEnd.A;
|
||||||
|
maxTangentCircle.ConstructFromTanTanPt( tanStart, tanEnd, startOther );
|
||||||
|
maxTanPtStart = tanStart.LineProject( maxTangentCircle.Center );
|
||||||
|
maxTanPtEnd = tanEnd.LineProject( maxTangentCircle.Center );
|
||||||
|
}
|
||||||
|
|
||||||
|
SEG constraintSeg( maxTanPtStart, maxTanPtEnd );
|
||||||
|
int constraintSegSide = constraintSeg.Side( theArc->GetMid() );
|
||||||
|
|
||||||
|
while( TOOL_EVENT* evt = Wait() )
|
||||||
|
{
|
||||||
|
m_cursor = controls->GetMousePosition();
|
||||||
|
|
||||||
|
// Constrain cursor
|
||||||
|
// Fix-me: this is a bit ugly but it works
|
||||||
|
if( tanStartSide != tanStart.Side( m_cursor ) )
|
||||||
|
{
|
||||||
|
VECTOR2I projected = tanStart.LineProject( m_cursor );
|
||||||
|
|
||||||
|
if( tanEndSide != tanEnd.Side( projected ) )
|
||||||
|
{
|
||||||
|
m_cursor = tanEnd.LineProject( m_cursor );
|
||||||
|
|
||||||
|
if( tanStartSide != tanStart.Side( m_cursor ) )
|
||||||
|
m_cursor = optInterectTan.get();
|
||||||
|
}
|
||||||
|
else if( constraintSegSide != constraintSeg.Side( projected ) )
|
||||||
|
{
|
||||||
|
m_cursor = constraintSeg.LineProject( m_cursor );
|
||||||
|
|
||||||
|
if( tanStartSide != tanStart.Side( m_cursor ) )
|
||||||
|
m_cursor = maxTanPtStart;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_cursor = projected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( tanEndSide != tanEnd.Side( m_cursor ) )
|
||||||
|
{
|
||||||
|
VECTOR2I projected = tanEnd.LineProject( m_cursor );
|
||||||
|
|
||||||
|
if( tanStartSide != tanStart.Side( projected ) )
|
||||||
|
{
|
||||||
|
m_cursor = tanStart.LineProject( m_cursor );
|
||||||
|
|
||||||
|
if( tanEndSide != tanEnd.Side( m_cursor ) )
|
||||||
|
m_cursor = optInterectTan.get();
|
||||||
|
}
|
||||||
|
else if( constraintSegSide != constraintSeg.Side( projected ) )
|
||||||
|
{
|
||||||
|
m_cursor = constraintSeg.LineProject( m_cursor );
|
||||||
|
|
||||||
|
if( tanEndSide != tanEnd.Side( m_cursor ) )
|
||||||
|
m_cursor = maxTanPtEnd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_cursor = projected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( constraintSegSide != constraintSeg.Side( m_cursor ) )
|
||||||
|
{
|
||||||
|
VECTOR2I projected = constraintSeg.LineProject( m_cursor );
|
||||||
|
|
||||||
|
if( tanStartSide != tanStart.Side( projected ) )
|
||||||
|
{
|
||||||
|
m_cursor = tanStart.LineProject( m_cursor );
|
||||||
|
|
||||||
|
if( constraintSegSide != constraintSeg.Side( m_cursor ) )
|
||||||
|
m_cursor = maxTanPtStart;
|
||||||
|
}
|
||||||
|
else if( tanEndSide != tanEnd.Side( projected ) )
|
||||||
|
{
|
||||||
|
m_cursor = tanEnd.LineProject( m_cursor );
|
||||||
|
|
||||||
|
if( constraintSegSide != constraintSeg.Side( m_cursor ) )
|
||||||
|
m_cursor = maxTanPtEnd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_cursor = projected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( m_cursor - maxTangentCircle.Center ).EuclideanNorm() < maxTangentCircle.Radius )
|
||||||
|
m_cursor = maxTangentCircle.NearestPoint( m_cursor );
|
||||||
|
|
||||||
|
controls->ForceCursorPosition( true, m_cursor );
|
||||||
|
|
||||||
|
// Calculate resulting object coordinates
|
||||||
|
CIRCLE circlehelper;
|
||||||
|
circlehelper.ConstructFromTanTanPt( tanStart, tanEnd, m_cursor );
|
||||||
|
|
||||||
|
VECTOR2I newCenter = circlehelper.Center;
|
||||||
|
VECTOR2I newStart = tanStart.LineProject( newCenter );
|
||||||
|
VECTOR2I newEnd = tanEnd.LineProject( newCenter );
|
||||||
|
VECTOR2I newMid = GetArcMid( newStart, newEnd, newCenter );
|
||||||
|
|
||||||
|
//Update objects
|
||||||
|
theArc->SetStart( (wxPoint) newStart );
|
||||||
|
theArc->SetEnd( (wxPoint) newEnd );
|
||||||
|
theArc->SetMid( (wxPoint) newMid );
|
||||||
|
|
||||||
|
if( isStartTrackOnStartPt )
|
||||||
|
trackOnStart->SetStart( (wxPoint) newStart );
|
||||||
|
else
|
||||||
|
trackOnStart->SetEnd( (wxPoint) newStart );
|
||||||
|
|
||||||
|
if( isEndTrackOnStartPt )
|
||||||
|
trackOnEnd->SetStart( (wxPoint) newEnd );
|
||||||
|
else
|
||||||
|
trackOnEnd->SetEnd( (wxPoint) newEnd );
|
||||||
|
|
||||||
|
//Update view
|
||||||
|
getView()->Update( trackOnStart );
|
||||||
|
getView()->Update( trackOnEnd );
|
||||||
|
getView()->Update( theArc );
|
||||||
|
|
||||||
|
//Handle events
|
||||||
|
if( evt->IsCancelInteractive() || evt->IsActivate() )
|
||||||
|
{
|
||||||
|
restore_state = true; // Canceling the tool means that items have to be restored
|
||||||
|
break; // Finish
|
||||||
|
}
|
||||||
|
else if( evt->IsAction( &ACTIONS::undo ) )
|
||||||
|
{
|
||||||
|
restore_state = true; // Perform undo locally
|
||||||
|
break; // Finish
|
||||||
|
}
|
||||||
|
else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT )
|
||||||
|
|| evt->IsDblClick( BUT_LEFT ) )
|
||||||
|
{
|
||||||
|
break; // finish
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove zero length tracks
|
||||||
|
if( theArc->GetStart() == theArc->GetEnd() )
|
||||||
|
m_commit->Remove( theArc );
|
||||||
|
|
||||||
|
auto cleanupTrack =
|
||||||
|
[&]( TRACK* aTrack )
|
||||||
|
{
|
||||||
|
if( aTrack->IsNew() )
|
||||||
|
{
|
||||||
|
getView()->Remove( aTrack );
|
||||||
|
|
||||||
|
if( aTrack->GetStart() == aTrack->GetEnd() )
|
||||||
|
delete aTrack;
|
||||||
|
else
|
||||||
|
m_commit->Add( aTrack );
|
||||||
|
}
|
||||||
|
else if( aTrack->GetStart() == aTrack->GetEnd() )
|
||||||
|
{
|
||||||
|
m_commit->Remove( aTrack );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cleanupTrack( trackOnStart );
|
||||||
|
cleanupTrack( trackOnEnd );
|
||||||
|
|
||||||
|
// Should we commit?
|
||||||
|
if( restore_state )
|
||||||
|
{
|
||||||
|
m_commit->Revert();
|
||||||
|
|
||||||
|
if( trackOnStart->IsNew() )
|
||||||
|
delete trackOnStart;
|
||||||
|
|
||||||
|
if( trackOnEnd->IsNew() )
|
||||||
|
delete trackOnEnd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_commit->Push( _( "Drag Arc Track" ) );
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,10 +82,17 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Drag()
|
* Function Drag()
|
||||||
* Invoke the PNS router to drag tracks.
|
* Invoke the PNS router to drag tracks or do an offline resizing of an arc track
|
||||||
|
* if a single arc track is selected
|
||||||
*/
|
*/
|
||||||
int Drag( const TOOL_EVENT& aEvent );
|
int Drag( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function DragArcTrack()
|
||||||
|
* Drag-resize an arc (and change end points of connected straight segments)
|
||||||
|
*/
|
||||||
|
int DragArcTrack( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function Properties()
|
* Function Properties()
|
||||||
* Displays properties window for the selected object.
|
* Displays properties window for the selected object.
|
||||||
|
|
Loading…
Reference in New Issue