From a41944020ddb2039f04ed8b2a640badc78b1c762 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Wed, 14 Jul 2021 21:03:32 +0100 Subject: [PATCH] Push most of PCB_SHAPE impl down in to EDA_SHAPE. --- .../3d_canvas/create_3Dgraphic_brd_items.cpp | 2 +- common/CMakeLists.txt | 3 +- common/eda_shape.cpp | 1209 ++++++++++++++++ common/eda_text.cpp | 19 + include/board_item.h | 39 +- include/eda_shape.h | 316 +++++ include/eda_text.h | 2 + include/preview_items/two_point_assistant.h | 2 +- pcbnew/CMakeLists.txt | 2 +- pcbnew/board.cpp | 53 +- pcbnew/board.h | 14 +- pcbnew/board_item.cpp | 15 - ...board_items_to_polygon_shape_transform.cpp | 28 +- ....cpp => convert_shape_list_to_polygon.cpp} | 11 +- ...ygon.h => convert_shape_list_to_polygon.h} | 6 - pcbnew/dialogs/dialog_footprint_checker.cpp | 2 +- .../dialog_graphic_item_properties.cpp | 6 +- pcbnew/footprint.cpp | 2 +- pcbnew/footprint.h | 2 +- pcbnew/fp_shape.cpp | 6 +- pcbnew/graphics_cleaner.cpp | 4 +- .../import_gfx/graphics_importer_pcbnew.cpp | 4 +- pcbnew/pad_custom_shape_functions.cpp | 2 +- pcbnew/pcb_shape.cpp | 1230 +---------------- pcbnew/pcb_shape.h | 247 +--- pcbnew/plugins/altium/altium_pcb.cpp | 10 +- .../cadstar/cadstar_pcb_archive_loader.cpp | 2 +- pcbnew/plugins/fabmaster/import_fabmaster.cpp | 8 +- pcbnew/plugins/kicad/kicad_plugin.cpp | 4 +- pcbnew/plugins/kicad/pcb_parser.cpp | 4 +- pcbnew/python/swig/board_item.i | 20 - pcbnew/tools/convert_tool.cpp | 2 +- pcbnew/tools/drawing_tool.cpp | 4 +- pcbnew/tools/pcb_point_editor.cpp | 4 +- pcbnew/tools/pcb_selection_conditions.h | 2 +- 35 files changed, 1751 insertions(+), 1535 deletions(-) create mode 100644 common/eda_shape.cpp create mode 100644 include/eda_shape.h rename pcbnew/{convert_drawsegment_list_to_polygon.cpp => convert_shape_list_to_polygon.cpp} (99%) rename pcbnew/{convert_drawsegment_list_to_polygon.h => convert_shape_list_to_polygon.h} (93%) diff --git a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp index f05a8b3de3..f59b3a2295 100644 --- a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp +++ b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp @@ -734,7 +734,7 @@ void BOARD_ADAPTER::addShapeWithClearance( const PCB_SHAPE* aShape, default: wxFAIL_MSG( "BOARD_ADAPTER::addShapeWithClearance no implementation for " - + SHAPE_T_asString( aShape->GetShape()) ); + + aShape->SHAPE_T_asString() ); break; } } diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index e29a623ece..654b842460 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -502,6 +502,7 @@ target_include_directories( common PUBLIC set( PCB_COMMON_SRCS base_screen.cpp eda_text.cpp + eda_shape.cpp fp_lib_table.cpp hash_eda.cpp page_info.cpp @@ -533,7 +534,7 @@ set( PCB_COMMON_SRCS ${CMAKE_SOURCE_DIR}/pcbnew/connectivity/connectivity_items.cpp ${CMAKE_SOURCE_DIR}/pcbnew/connectivity/connectivity_data.cpp ${CMAKE_SOURCE_DIR}/pcbnew/connectivity/from_to_cache.cpp - ${CMAKE_SOURCE_DIR}/pcbnew/convert_drawsegment_list_to_polygon.cpp + ${CMAKE_SOURCE_DIR}/pcbnew/convert_shape_list_to_polygon.cpp ${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_engine.cpp ${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_item.cpp ${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_rule.cpp diff --git a/common/eda_shape.cpp b/common/eda_shape.cpp new file mode 100644 index 0000000000..0114b28b1e --- /dev/null +++ b/common/eda_shape.cpp @@ -0,0 +1,1209 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck + * Copyright (C) 2011 Wayne Stambaugh + * Copyright (C) 1992-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 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 +#include +#include +#include +#include +#include +#include +#include // for KiROUND +#include + + +EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aDefaultLineWidth ) : + m_shape( aType ), + m_width( aDefaultLineWidth ) +{ + m_angle = 0; + m_filled = false; +} + + +EDA_SHAPE::~EDA_SHAPE() +{ +} + + +wxString EDA_SHAPE::ShowShape() const +{ + switch( m_shape ) + { + case SHAPE_T::SEGMENT: return _( "Line" ); + case SHAPE_T::RECT: return _( "Rect" ); + case SHAPE_T::ARC: return _( "Arc" ); + case SHAPE_T::CIRCLE: return _( "Circle" ); + case SHAPE_T::BEZIER: return _( "Bezier Curve" ); + case SHAPE_T::POLY: return _( "Polygon" ); + default: return wxT( "??" ); + } +} + + +wxString EDA_SHAPE::SHAPE_T_asString() const +{ + switch( m_shape ) + { + case SHAPE_T::SEGMENT: return "S_SEGMENT"; + case SHAPE_T::RECT: return "S_RECT"; + case SHAPE_T::ARC: return "S_ARC"; + case SHAPE_T::CIRCLE: return "S_CIRCLE"; + case SHAPE_T::POLY: return "S_POLYGON"; + case SHAPE_T::BEZIER: return "S_CURVE"; + case SHAPE_T::LAST: return "!S_LAST!"; // Synthetic value, but if we come across it then + // we're going to want to know. + } + + return wxEmptyString; // Just to quiet GCC. +} + + +void EDA_SHAPE::SetShapePos( const wxPoint& aPos ) +{ + m_start = aPos; +} + + +wxPoint EDA_SHAPE::GetShapePos() const +{ + if( m_shape == SHAPE_T::POLY ) + return (wxPoint) m_poly.CVertex( 0 ); + else + return m_start; +} + + +double EDA_SHAPE::GetLength() const +{ + double length = 0.0; + + switch( m_shape ) + { + case SHAPE_T::BEZIER: + for( size_t ii = 1; ii < m_bezierPoints.size(); ++ii ) + length += GetLineLength( m_bezierPoints[ ii - 1], m_bezierPoints[ii] ); + + return length; + + case SHAPE_T::SEGMENT: + return GetLineLength( GetStart(), GetEnd() ); + + case SHAPE_T::POLY: + for( int ii = 0; ii < m_poly.COutline( 0 ).SegmentCount(); ii++ ) + length += m_poly.COutline( 0 ).CSegment( ii ).Length(); + + return length; + + case SHAPE_T::ARC: + return 2 * M_PI * GetRadius() * ( GetAngle() / 3600.0 ); + + default: + wxFAIL_MSG( "EDA_SHAPE::GetLength not implemented for " + SHAPE_T_asString() ); + return 0.0; + } +} + + +void EDA_SHAPE::move( const wxPoint& aMoveVector ) +{ + // Move vector should not affect start/end for polygon since it will + // be applied directly to polygon outline. + if( m_shape != SHAPE_T::POLY ) + { + m_start += aMoveVector; + m_end += aMoveVector; + } + + switch ( m_shape ) + { + case SHAPE_T::POLY: + m_poly.Move( VECTOR2I( aMoveVector ) ); + break; + + case SHAPE_T::ARC: + m_thirdPoint += aMoveVector; + break; + + case SHAPE_T::BEZIER: + m_bezierC1 += aMoveVector; + m_bezierC2 += aMoveVector; + + for( wxPoint& pt : m_bezierPoints) + pt += aMoveVector; + + break; + + default: + wxFAIL_MSG( "EDA_SHAPE::ShapeMove not implemented for " + SHAPE_T_asString() ); + break; + } +} + + +void EDA_SHAPE::scale( double aScale ) +{ + auto scalePt = [&]( wxPoint& pt ) + { + pt.x = KiROUND( pt.x * aScale ); + pt.y = KiROUND( pt.y * aScale ); + }; + + int radius = GetRadius(); + + scalePt( m_start ); + scalePt( m_end ); + + // specific parameters: + switch( m_shape ) + { + case SHAPE_T::BEZIER: + scalePt( m_bezierC1 ); + scalePt( m_bezierC2 ); + break; + + case SHAPE_T::ARC: + scalePt( m_thirdPoint ); + break; + + case SHAPE_T::CIRCLE: // ring or circle + m_end.x = m_start.x + KiROUND( radius * aScale ); + m_end.y = m_start.y; + break; + + case SHAPE_T::POLY: // polygon + { + std::vector pts; + + for( const VECTOR2I& pt : m_poly.Outline( 0 ).CPoints() ) + { + pts.emplace_back( pt ); + scalePt( pts.back() ); + } + + SetPolyPoints( pts ); + } + break; + + default: + wxFAIL_MSG( "EDA_SHAPE::ShapeScale not implemented for " + SHAPE_T_asString() ); + break; + } +} + + +void EDA_SHAPE::rotate( const wxPoint& aRotCentre, double aAngle ) +{ + switch( m_shape ) + { + case SHAPE_T::ARC: + case SHAPE_T::SEGMENT: + case SHAPE_T::CIRCLE: + // these can all be done by just rotating the constituent points + RotatePoint( &m_start, aRotCentre, aAngle ); + RotatePoint( &m_end, aRotCentre, aAngle ); + RotatePoint( &m_thirdPoint, aRotCentre, aAngle ); + break; + + case SHAPE_T::RECT: + if( KiROUND( aAngle ) % 900 == 0 ) + { + RotatePoint( &m_start, aRotCentre, aAngle ); + RotatePoint( &m_end, aRotCentre, aAngle ); + break; + } + + // Convert non-cartesian-rotated rect to a diamond + m_shape = SHAPE_T::POLY; + m_poly.RemoveAllContours(); + m_poly.NewOutline(); + m_poly.Append( m_start ); + m_poly.Append( m_end.x, m_start.y ); + m_poly.Append( m_end ); + m_poly.Append( m_start.x, m_end.y ); + + KI_FALLTHROUGH; + + case SHAPE_T::POLY: + m_poly.Rotate( -DECIDEG2RAD( aAngle ), VECTOR2I( aRotCentre ) ); + break; + + case SHAPE_T::BEZIER: + RotatePoint( &m_start, aRotCentre, aAngle); + RotatePoint( &m_end, aRotCentre, aAngle); + RotatePoint( &m_bezierC1, aRotCentre, aAngle); + RotatePoint( &m_bezierC2, aRotCentre, aAngle); + + for( wxPoint& pt : m_bezierPoints ) + RotatePoint( &pt, aRotCentre, aAngle); + + break; + + default: + wxFAIL_MSG( "EDA_SHAPE::ShapeRotate not implemented for " + SHAPE_T_asString() ); + break; + } +} + + +void EDA_SHAPE::flip( const wxPoint& aCentre, bool aFlipLeftRight ) +{ + if( aFlipLeftRight ) + { + m_start.x = aCentre.x - ( m_start.x - aCentre.x ); + m_end.x = aCentre.x - ( m_end.x - aCentre.x ); + } + else + { + m_start.y = aCentre.y - ( m_start.y - aCentre.y ); + m_end.y = aCentre.y - ( m_end.y - aCentre.y ); + } + + switch ( m_shape ) + { + case SHAPE_T::ARC: + if( aFlipLeftRight ) + m_thirdPoint.x = aCentre.x - ( m_thirdPoint.x - aCentre.x ); + else + m_thirdPoint.y = aCentre.y - ( m_thirdPoint.y - aCentre.y ); + + m_angle = -m_angle; + break; + + case SHAPE_T::POLY: + m_poly.Mirror( aFlipLeftRight, !aFlipLeftRight, VECTOR2I( aCentre ) ); + break; + + case SHAPE_T::BEZIER: + { + if( aFlipLeftRight ) + { + m_bezierC1.x = aCentre.x - ( m_bezierC1.x - aCentre.x ); + m_bezierC2.x = aCentre.x - ( m_bezierC2.x - aCentre.x ); + } + else + { + m_bezierC1.y = aCentre.y - ( m_bezierC1.y - aCentre.y ); + m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y ); + } + + // Rebuild the poly points shape + std::vector ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end }; + BEZIER_POLY converter( ctrlPoints ); + converter.GetPoly( m_bezierPoints, m_width ); + } + break; + + case SHAPE_T::SEGMENT: + case SHAPE_T::RECT: + case SHAPE_T::CIRCLE: + break; + + default: + wxFAIL_MSG( "EDA_SHAPE::ShapeFlip not implemented for " + SHAPE_T_asString() ); + break; + } +} + + +void EDA_SHAPE::RebuildBezierToSegmentsPointsList( int aMinSegLen ) +{ + // Has meaning only for S_CURVE DRAW_SEGMENT shape + if( m_shape != SHAPE_T::BEZIER ) + { + m_bezierPoints.clear(); + return; + } + + // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve + m_bezierPoints = buildBezierToSegmentsPointsList( aMinSegLen ); +} + + +const std::vector EDA_SHAPE::buildBezierToSegmentsPointsList( int aMinSegLen ) const +{ + std::vector bezierPoints; + + // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve + std::vector ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end }; + BEZIER_POLY converter( ctrlPoints ); + converter.GetPoly( bezierPoints, aMinSegLen ); + + return bezierPoints; +} + + +wxPoint EDA_SHAPE::getCenter() const +{ + wxPoint c; + + switch( m_shape ) + { + case SHAPE_T::ARC: + case SHAPE_T::CIRCLE: + c = m_start; + break; + + case SHAPE_T::SEGMENT: + // Midpoint of the line + c = ( GetStart() + GetEnd() ) / 2; + break; + + case SHAPE_T::POLY: + case SHAPE_T::RECT: + case SHAPE_T::BEZIER: + c = getBoundingBox().Centre(); + break; + + default: + wxFAIL_MSG( "EDA_SHAPE::GetCentre not implemented for " + SHAPE_T_asString() ); + break; + } + + return c; +} + + +wxPoint EDA_SHAPE::GetArcEnd() const +{ + wxPoint endPoint( m_end ); // start of arc + + switch( m_shape ) + { + case SHAPE_T::ARC: + endPoint = m_thirdPoint; + break; + + default: + break; + } + + return endPoint; // after rotation, the end of the arc. +} + + +wxPoint EDA_SHAPE::GetArcMid() const +{ + wxPoint endPoint( m_end ); + + switch( m_shape ) + { + case SHAPE_T::ARC: + // rotate the starting point of the arc, given by m_End, through half + // the angle m_Angle to get the middle of the arc. + // m_Start is the arc centre + endPoint = m_end; // m_End = start point of arc + RotatePoint( &endPoint, m_start, -m_angle / 2.0 ); + break; + + default: + break; + } + + return endPoint; // after rotation, the end of the arc. +} + + +double EDA_SHAPE::GetArcAngleStart() const +{ + wxPoint arcStart = GetArcStart(); + wxPoint center = getCenter(); + double angleStart = ArcTangente( arcStart.y - center.y, arcStart.x - center.x ); + + // Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg + // because 180 deg and -180 are very near angles when ampping betewwen -180 ... 180 deg. + // and this is not easy to handle in calculations + NORMALIZE_ANGLE_POS( angleStart ); + + return angleStart; +} + +double EDA_SHAPE::GetArcAngleEnd() const +{ + wxPoint arcEnd = GetArcEnd(); + wxPoint center = getCenter(); + double angleStart = ArcTangente( arcEnd.y - center.y, arcEnd.x - center.x ); + + // Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg + // because 180 deg and -180 are very near angles when ampping betewwen -180 ... 180 deg. + // and this is not easy to handle in calculations + NORMALIZE_ANGLE_POS( angleStart ); + + return angleStart; +} + + +int EDA_SHAPE::GetRadius() const +{ + double radius = GetLineLength( m_start, m_end ); + + // don't allow degenerate arcs + return std::max( 1, KiROUND( radius ) ); +} + + +void EDA_SHAPE::SetArcGeometry( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd ) +{ + SetArcStart( aStart ); + SetArcEnd( aEnd ); + + // Sadly we currently store center and angle rather than mid. So we have to calculate + // those. + wxPoint center = GetArcCenter( aStart, aMid, aEnd ); + VECTOR2D startLine = aStart - center; + VECTOR2D endLine = aEnd - center; + bool clockwise = GetAngle() > 0; + double angle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() ); + + if( clockwise && angle < 0.0 ) + angle += 3600.0; + else if( !clockwise && angle > 0.0 ) + angle -= 3600.0; + + SetAngle( angle, false ); + SetArcCenter( center ); +} + + +void EDA_SHAPE::SetAngle( double aAngle, bool aUpdateEnd ) +{ + // m_Angle must be >= -360 and <= +360 degrees + m_angle = NormalizeAngle360Max( aAngle ); + + if( aUpdateEnd ) + { + m_thirdPoint = m_end; + RotatePoint( &m_thirdPoint, m_start, -m_angle ); + } +} + + +void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector& aList ) +{ + EDA_UNITS units = aFrame->GetUserUnits(); + ORIGIN_TRANSFORMS originTransforms = aFrame->GetOriginTransforms(); + wxString msg; + + wxString shape = _( "Shape" ); + + switch( m_shape ) + { + case SHAPE_T::CIRCLE: + aList.emplace_back( shape, _( "Circle" ) ); + + msg = MessageTextFromValue( units, GetLineLength( m_start, m_end ) ); + aList.emplace_back( _( "Radius" ), msg ); + break; + + case SHAPE_T::ARC: + aList.emplace_back( shape, _( "Arc" ) ); + + msg.Printf( wxT( "%.1f" ), m_angle / 10.0 ); + aList.emplace_back( _( "Angle" ), msg ); + + msg = MessageTextFromValue( units, GetLineLength( m_start, m_end ) ); + aList.emplace_back( _( "Radius" ), msg ); + break; + + case SHAPE_T::BEZIER: + aList.emplace_back( shape, _( "Curve" ) ); + + msg = MessageTextFromValue( units, GetLength() ); + aList.emplace_back( _( "Length" ), msg ); + break; + + case SHAPE_T::POLY: + aList.emplace_back( shape, _( "Polygon" ) ); + + msg.Printf( "%d", GetPolyShape().Outline(0).PointCount() ); + aList.emplace_back( _( "Points" ), msg ); + break; + + case SHAPE_T::RECT: + aList.emplace_back( shape, _( "Rectangle" ) ); + + msg = MessageTextFromValue( units, std::abs( m_end.x - m_start.x ) ); + aList.emplace_back( _( "Width" ), msg ); + + msg = MessageTextFromValue( units, std::abs( m_end.y - m_start.y ) ); + aList.emplace_back( _( "Height" ), msg ); + break; + + case SHAPE_T::SEGMENT: + { + aList.emplace_back( shape, _( "Segment" ) ); + + msg = MessageTextFromValue( units, GetLineLength( m_start, m_end ) ); + aList.emplace_back( _( "Length" ), msg ); + + // angle counter-clockwise from 3'o-clock + const double deg = RAD2DEG( atan2( (double)( m_start.y - m_end.y ), + (double)( m_end.x - m_start.x ) ) ); + aList.emplace_back( _( "Angle" ), wxString::Format( "%.1f", deg ) ); + } + break; + + default: + aList.emplace_back( shape, _( "Unrecognized" ) ); + break; + } + + aList.emplace_back( _( "Line width" ), MessageTextFromValue( units, m_width ) ); +} + + +const EDA_RECT EDA_SHAPE::getBoundingBox() const +{ + EDA_RECT bbox; + + bbox.SetOrigin( m_start ); + + switch( m_shape ) + { + case SHAPE_T::RECT: + bbox = EDA_RECT(); // re-init for merging + + for( wxPoint& pt : GetRectCorners() ) + bbox.Merge( pt ); + + break; + + case SHAPE_T::SEGMENT: + bbox.SetEnd( m_end ); + break; + + case SHAPE_T::CIRCLE: + bbox.Inflate( GetRadius() ); + break; + + case SHAPE_T::ARC: + computeArcBBox( bbox ); + break; + + case SHAPE_T::POLY: + if( m_poly.IsEmpty() ) + break; + + bbox = EDA_RECT(); // re-init for merging + + for( auto iter = m_poly.CIterate(); iter; iter++ ) + { + wxPoint pt( iter->x, iter->y ); + + RotatePoint( &pt, getParentOrientation() ); + pt += getParentPosition(); + + bbox.Merge( pt ); + } + + break; + + case SHAPE_T::BEZIER: + bbox.Merge( m_bezierC1 ); + bbox.Merge( m_bezierC2 ); + bbox.Merge( m_end ); + break; + + default: + wxFAIL_MSG( "EDA_SHAPE::getBoundingBox not implemented for " + SHAPE_T_asString() ); + break; + } + + bbox.Inflate( m_width / 2 ); + bbox.Normalize(); + + return bbox; +} + + +bool EDA_SHAPE::hitTest( const wxPoint& aPosition, int aAccuracy ) const +{ + int maxdist = aAccuracy + ( m_width / 2 ); + + switch( m_shape ) + { + case SHAPE_T::CIRCLE: + { + int radius = GetRadius(); + int dist = KiROUND( EuclideanNorm( aPosition - getCenter() ) ); + + if( IsFilled() ) + return dist <= radius + maxdist; // Filled circle hit-test + else + return abs( radius - dist ) <= maxdist; // Ring hit-test + } + + case SHAPE_T::ARC: + { + wxPoint relPos = aPosition - getCenter(); + int radius = GetRadius(); + int dist = KiROUND( EuclideanNorm( relPos ) ); + + if( abs( radius - dist ) <= maxdist ) + { + // For arcs, the test point angle must be >= arc angle start + // and <= arc angle end + // However angle values > 360 deg are not easy to handle + // so we calculate the relative angle between arc start point and teast point + // this relative arc should be < arc angle if arc angle > 0 (CW arc) + // and > arc angle if arc angle < 0 (CCW arc) + double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg + + double arc_hittest = ArcTangente( relPos.y, relPos.x ); + + // Calculate relative angle between the starting point of the arc, and the test point + arc_hittest -= arc_angle_start; + + // Normalise arc_hittest between 0 ... 360 deg + NORMALIZE_ANGLE_POS( arc_hittest ); + + // Check angle: inside the arc angle when it is > 0 and outside the not drawn arc when + // it is < 0 + if( GetAngle() >= 0.0 ) + return arc_hittest <= GetAngle(); + else + return arc_hittest >= ( 3600.0 + GetAngle() ); + } + else + { + return false; + } + } + + case SHAPE_T::BEZIER: + const_cast( this )->RebuildBezierToSegmentsPointsList( m_width ); + + for( unsigned int i= 1; i < m_bezierPoints.size(); i++) + { + if( TestSegmentHit( aPosition, m_bezierPoints[ i - 1], m_bezierPoints[i], maxdist ) ) + return true; + } + + return false; + + case SHAPE_T::SEGMENT: + return TestSegmentHit( aPosition, m_start, m_end, maxdist ); + + case SHAPE_T::RECT: + if( IsFilled() ) // Filled rect hit-test + { + SHAPE_POLY_SET poly; + poly.NewOutline(); + + for( const wxPoint& pt : GetRectCorners() ) + poly.Append( pt ); + + return poly.Collide( VECTOR2I( aPosition ), maxdist ); + } + else // Open rect hit-test + { + std::vector pts = GetRectCorners(); + + return TestSegmentHit( aPosition, pts[0], pts[1], maxdist ) + || TestSegmentHit( aPosition, pts[1], pts[2], maxdist ) + || TestSegmentHit( aPosition, pts[2], pts[3], maxdist ) + || TestSegmentHit( aPosition, pts[3], pts[0], maxdist ); + } + + case SHAPE_T::POLY: + if( IsFilled() ) + { + return m_poly.Collide( VECTOR2I( aPosition ), maxdist ); + } + else + { + SHAPE_POLY_SET::VERTEX_INDEX dummy; + return m_poly.CollideEdge( VECTOR2I( aPosition ), dummy, maxdist ); + } + + default: + wxFAIL_MSG( "EDA_SHAPE::hitTest(point) not implemented for " + SHAPE_T_asString() ); + return false; + } +} + + +bool EDA_SHAPE::hitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const +{ + EDA_RECT arect = aRect; + arect.Normalize(); + arect.Inflate( aAccuracy ); + + EDA_RECT arcRect; + EDA_RECT bb = getBoundingBox(); + + switch( m_shape ) + { + case SHAPE_T::CIRCLE: + // Test if area intersects or contains the circle: + if( aContained ) + return arect.Contains( bb ); + else + { + // If the rectangle does not intersect the bounding box, this is a much quicker test + if( !aRect.Intersects( bb ) ) + { + return false; + } + else + { + return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ); + } + } + + case SHAPE_T::ARC: + // Test for full containment of this arc in the rect + if( aContained ) + { + return arect.Contains( bb ); + } + // Test if the rect crosses the arc + else + { + arcRect = bb.Common( arect ); + + /* All following tests must pass: + * 1. Rectangle must intersect arc BoundingBox + * 2. Rectangle must cross the outside of the arc + */ + return arcRect.Intersects( arect ) && + arcRect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ); + } + + case SHAPE_T::RECT: + if( aContained ) + { + return arect.Contains( bb ); + } + else + { + std::vector pts = GetRectCorners(); + + // Account for the width of the lines + arect.Inflate( GetWidth() / 2 ); + return ( arect.Intersects( pts[0], pts[1] ) + || arect.Intersects( pts[1], pts[2] ) + || arect.Intersects( pts[2], pts[3] ) + || arect.Intersects( pts[3], pts[0] ) ); + } + + case SHAPE_T::SEGMENT: + if( aContained ) + { + return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() ); + } + else + { + // Account for the width of the line + arect.Inflate( GetWidth() / 2 ); + return arect.Intersects( GetStart(), GetEnd() ); + } + + case SHAPE_T::POLY: + if( aContained ) + { + return arect.Contains( bb ); + } + else + { + // Fast test: if aRect is outside the polygon bounding box, + // rectangles cannot intersect + if( !arect.Intersects( bb ) ) + return false; + + // Account for the width of the line + arect.Inflate( GetWidth() / 2 ); + + // Polygons in footprints use coordinates relative to the footprint. + // Therefore, instead of using m_poly, we make a copy which is translated + // to the actual location in the board. + double orientation = 0.0; + wxPoint offset = getParentPosition(); + + if( getParentOrientation() ) + orientation = -DECIDEG2RAD( getParentOrientation() ); + + SHAPE_POLY_SET poly{ m_poly }; + poly.Rotate( orientation ); + poly.Move( offset ); + + int count = poly.TotalVertices(); + + for( int ii = 0; ii < count; ii++ ) + { + VECTOR2I vertex = poly.CVertex( ii ); + VECTOR2I vertexNext = poly.CVertex( ( ii + 1 ) % count ); + + // Test if the point is within aRect + if( arect.Contains( ( wxPoint ) vertex ) ) + return true; + + // Test if this edge intersects aRect + if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) ) + return true; + } + + return false; + } + + case SHAPE_T::BEZIER: + if( aContained ) + { + return arect.Contains( bb ); + } + else + { + // Fast test: if aRect is outside the polygon bounding box, + // rectangles cannot intersect + if( !arect.Intersects( bb ) ) + return false; + + // Account for the width of the line + arect.Inflate( GetWidth() / 2 ); + unsigned count = m_bezierPoints.size(); + + for( unsigned ii = 1; ii < count; ii++ ) + { + wxPoint vertex = m_bezierPoints[ ii - 1]; + wxPoint vertexNext = m_bezierPoints[ii]; + + // Test if the point is within aRect + if( arect.Contains( ( wxPoint ) vertex ) ) + return true; + + // Test if this edge intersects aRect + if( arect.Intersects( vertex, vertexNext ) ) + return true; + } + + return false; + } + + default: + wxFAIL_MSG( "EDA_SHAPE::hitTest(rect) not implemented for " + SHAPE_T_asString() ); + return false; + } +} + + +std::vector EDA_SHAPE::GetRectCorners() const +{ + std::vector pts; + wxPoint topLeft = GetStart(); + wxPoint botRight = GetEnd(); + + // Un-rotate rect topLeft and botRight + if( KiROUND( getParentOrientation() ) % 900 != 0 ) + { + topLeft -= getParentPosition(); + RotatePoint( &topLeft, -getParentOrientation() ); + + botRight -= getParentPosition(); + RotatePoint( &botRight, -getParentOrientation() ); + } + + // Set up the un-rotated 4 corners + pts.emplace_back( topLeft ); + pts.emplace_back( botRight.x, topLeft.y ); + pts.emplace_back( botRight ); + pts.emplace_back( topLeft.x, botRight.y ); + + // Now re-rotate the 4 corners to get a diamond + if( KiROUND( getParentOrientation() ) % 900 != 0 ) + { + for( wxPoint& pt : pts ) + { + RotatePoint( &pt, getParentOrientation() ); + pt += getParentPosition(); + } + } + + return pts; +} + + +void EDA_SHAPE::computeArcBBox( EDA_RECT& aBBox ) const +{ + // Do not include the center, which is not necessarily + // inside the BB of a arc with a small angle + aBBox.SetOrigin( m_end ); + + wxPoint end = m_end; + RotatePoint( &end, m_start, -m_angle ); + aBBox.Merge( end ); + + // Determine the starting quarter + // 0 right-bottom + // 1 left-bottom + // 2 left-top + // 3 right-top + unsigned int quarter = 0; // assume right-bottom + + if( m_end.x < m_start.x ) + { + if( m_end.y <= m_start.y ) + quarter = 2; + else // ( m_End.y > m_Start.y ) + quarter = 1; + } + else if( m_end.x >= m_start.x ) + { + if( m_end.y < m_start.y ) + quarter = 3; + else if( m_end.x == m_start.x ) + quarter = 1; + } + + int radius = GetRadius(); + int angle = (int) GetArcAngleStart() % 900 + m_angle; + bool directionCW = ( m_angle > 0 ); // Is the direction of arc clockwise? + + // Make the angle positive, so we go clockwise and merge points belonging to the arc + if( !directionCW ) + { + angle = 900 - angle; + quarter = ( quarter + 3 ) % 4; // -1 modulo arithmetic + } + + while( angle > 900 ) + { + switch( quarter ) + { + case 0: aBBox.Merge( wxPoint( m_start.x, m_start.y + radius ) ); break; // down + case 1: aBBox.Merge( wxPoint( m_start.x - radius, m_start.y ) ); break; // left + case 2: aBBox.Merge( wxPoint( m_start.x, m_start.y - radius ) ); break; // up + case 3: aBBox.Merge( wxPoint( m_start.x + radius, m_start.y ) ); break; // right + } + + if( directionCW ) + ++quarter; + else + quarter += 3; // -1 modulo arithmetic + + quarter %= 4; + angle -= 900; + } + + aBBox.Inflate( m_width ); // Technically m_width / 2, but it doesn't hurt to have the + // bounding box a bit large to account for drawing clearances, + // etc. +} + + +void EDA_SHAPE::SetPolyPoints( const std::vector& aPoints ) +{ + m_poly.RemoveAllContours(); + m_poly.NewOutline(); + + for ( const wxPoint& p : aPoints ) + m_poly.Append( p.x, p.y ); +} + + +std::vector EDA_SHAPE::MakeEffectiveShapes() const +{ + std::vector effectiveShapes; + + switch( m_shape ) + { + case SHAPE_T::ARC: + effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetArcStart(), + GetAngle() / 10.0, m_width ) ); + break; + + case SHAPE_T::SEGMENT: + effectiveShapes.emplace_back( new SHAPE_SEGMENT( GetStart(), GetEnd(), m_width ) ); + break; + + case SHAPE_T::RECT: + { + std::vector pts = GetRectCorners(); + + if( IsFilled() ) + effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) ); + + if( m_width > 0 || !IsFilled() ) + { + effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], m_width ) ); + effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], m_width ) ); + effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], m_width ) ); + effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], m_width ) ); + } + } + break; + + case SHAPE_T::CIRCLE: + { + if( IsFilled() ) + effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) ); + + if( m_width > 0 || !IsFilled() ) + { + // SHAPE_CIRCLE has no ConvertToPolyline() method, so use a 360.0 SHAPE_ARC + SHAPE_ARC circle( getCenter(), GetEnd(), 360.0 ); + SHAPE_LINE_CHAIN l = circle.ConvertToPolyline(); + + for( int i = 0; i < l.SegmentCount(); i++ ) + { + effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A, + l.Segment( i ).B, m_width ) ); + } + } + + break; + } + + case SHAPE_T::BEZIER: + { + auto bezierPoints = buildBezierToSegmentsPointsList( GetWidth() ); + wxPoint start_pt = bezierPoints[0]; + + for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ ) + { + wxPoint end_pt = bezierPoints[jj]; + effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, m_width ) ); + start_pt = end_pt; + } + + break; + } + + case SHAPE_T::POLY: + { + SHAPE_LINE_CHAIN l = GetPolyShape().COutline( 0 ); + + l.Rotate( -DECIDEG2RAD( getParentOrientation() ) ); + l.Move( getParentPosition() ); + + if( IsFilled() ) + effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) ); + + if( m_width > 0 || !IsFilled() ) + { + for( int i = 0; i < l.SegmentCount(); i++ ) + effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ), m_width ) ); + } + } + break; + + default: + wxFAIL_MSG( "EDA_SHAPE::MakeEffectiveShapes not implemented for " + SHAPE_T_asString() ); + break; + } + + return effectiveShapes; +} + + +void EDA_SHAPE::DupPolyPointsList( std::vector& aBuffer ) const +{ + if( m_poly.OutlineCount() ) + { + int pointCount = m_poly.COutline( 0 ).PointCount(); + + if( pointCount ) + { + aBuffer.reserve( pointCount ); + + for ( auto iter = m_poly.CIterate(); iter; iter++ ) + aBuffer.emplace_back( iter->x, iter->y ); + } + } +} + + +bool EDA_SHAPE::IsPolyShapeValid() const +{ + // return true if the polygonal shape is valid (has more than 2 points) + if( GetPolyShape().OutlineCount() == 0 ) + return false; + + const SHAPE_LINE_CHAIN& outline = ( (SHAPE_POLY_SET&)GetPolyShape() ).Outline( 0 ); + + return outline.PointCount() > 2; +} + + +int EDA_SHAPE::GetPointCount() const +{ + // return the number of corners of the polygonal shape + // this shape is expected to be only one polygon without hole + if( GetPolyShape().OutlineCount() ) + return GetPolyShape().VertexCount( 0 ); + + return 0; +} + + +void EDA_SHAPE::SwapShape( EDA_SHAPE* aImage ) +{ + EDA_SHAPE* image = dynamic_cast( aImage ); + assert( image ); + + std::swap( m_width, image->m_width ); + std::swap( m_start, image->m_start ); + std::swap( m_end, image->m_end ); + std::swap( m_thirdPoint, image->m_thirdPoint ); + std::swap( m_shape, image->m_shape ); + std::swap( m_angle, image->m_angle ); + std::swap( m_bezierC1, image->m_bezierC1 ); + std::swap( m_bezierC2, image->m_bezierC2 ); + std::swap( m_bezierPoints, image->m_bezierPoints ); + std::swap( m_poly, image->m_poly ); +} + + +int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const +{ +#define TEST( a, b ) { if( a != b ) return a < b; } +#define TEST_PT( a, b ) { TEST( a.x, b.x ); TEST( a.y, b.y ); } + + TEST_PT( m_start, aOther->m_start ); + TEST_PT( m_end, aOther->m_end ); + + TEST( (int) m_shape, (int) aOther->m_shape ); + + if( m_shape == SHAPE_T::ARC ) + { + TEST_PT( m_thirdPoint, aOther->m_thirdPoint ); + } + else if( m_shape == SHAPE_T::BEZIER ) + { + TEST_PT( m_bezierC1, aOther->m_bezierC1 ); + TEST_PT( m_bezierC2, aOther->m_bezierC2 ); + } + else if( m_shape == SHAPE_T::POLY ) + { + TEST( m_poly.TotalVertices(), aOther->m_poly.TotalVertices() ); + + for( int ii = 0; ii < m_poly.TotalVertices(); ++ii ) + TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) ); + } + + TEST( m_width, aOther->m_width ); + + return 0; +} \ No newline at end of file diff --git a/common/eda_text.cpp b/common/eda_text.cpp index 2c630e2c7b..cc3a368078 100644 --- a/common/eda_text.cpp +++ b/common/eda_text.cpp @@ -649,6 +649,25 @@ double EDA_TEXT::GetDrawRotation() const } +int EDA_TEXT::Compare( const EDA_TEXT* aOther ) const +{ +#define TEST( a, b ) { if( a != b ) return a < b; } +#define TEST_PT( a, b ) { TEST( a.x, b.x ); TEST( a.y, b.y ); } + + TEST_PT( m_e.pos, aOther->m_e.pos ); + + TEST_PT( m_e.size, aOther->m_e.size ); + TEST( m_e.penwidth, aOther->m_e.penwidth ); + TEST( m_e.angle, aOther->m_e.angle ); + + TEST( m_e.hjustify, aOther->m_e.hjustify ); + TEST( m_e.vjustify, aOther->m_e.vjustify ); + TEST( m_e.bits, aOther->m_e.bits ); + + return m_text.Cmp( aOther->m_text ); +} + + static struct EDA_TEXT_DESC { EDA_TEXT_DESC() diff --git a/include/board_item.h b/include/board_item.h index 8065788c98..fc0ff6d474 100644 --- a/include/board_item.h +++ b/include/board_item.h @@ -40,37 +40,6 @@ class PCB_BASE_FRAME; class SHAPE; class PCB_GROUP; -/** - * The set of shapes for PCB graphics and tracks and footprint graphics in the .m_Shape member - */ -enum class SHAPE_T : int -{ - SEGMENT = 0, ///< usual segment : line with rounded ends - RECT, ///< segment with non rounded ends - ARC, ///< Arcs (with rounded ends) - CIRCLE, ///< ring - POLY, ///< polygon (not yet used for tracks, but could be in microwave apps) - BEZIER, ///< Bezier Curve - LAST ///< last value for this list -}; - -static inline wxString SHAPE_T_asString( SHAPE_T a ) -{ - switch( a ) - { - case SHAPE_T::SEGMENT: return "S_SEGMENT"; - case SHAPE_T::RECT: return "S_RECT"; - case SHAPE_T::ARC: return "S_ARC"; - case SHAPE_T::CIRCLE: return "S_CIRCLE"; - case SHAPE_T::POLY: return "S_POLYGON"; - case SHAPE_T::BEZIER: return "S_CURVE"; - case SHAPE_T::LAST: return "S_LAST"; // Synthetic value, but if we come across it we're - // going to want to know. - } - - return wxEmptyString; // Just to quiet GCC. -}; - /** * A base class for any item which can be embedded within the #BOARD container class, and @@ -259,11 +228,6 @@ public: */ void DeleteStructure(); - /** - * Convert the enum #PCB_SHAPE_TYPE_T integer value to a wxString. - */ - static wxString ShowShape( SHAPE_T aShape ); - /** * Move this object. * @@ -271,8 +235,7 @@ public: */ virtual void Move( const wxPoint& aMoveVector ) { - wxFAIL_MSG( wxString::Format( wxT( "virtual BOARD_ITEM::Move called for %s" ), - GetClass() ) ); + wxFAIL_MSG( "virtual BOARD_ITEM::Move called for " + GetClass() ); } void Move( const VECTOR2I& aMoveVector ) diff --git a/include/eda_shape.h b/include/eda_shape.h new file mode 100644 index 0000000000..d95cdd356f --- /dev/null +++ b/include/eda_shape.h @@ -0,0 +1,316 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2018 Jean-Pierre Charras jp.charras at wanadoo.fr + * Copyright (C) 1992-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 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 + */ + +#ifndef EDA_SHAPE_H +#define EDA_SHAPE_H + +#include +#include +#include +#include +#include +#include + +class LINE_READER; +class EDA_DRAW_FRAME; +class FOOTPRINT; +class MSG_PANEL_ITEM; + + +enum class SHAPE_T : int +{ + SEGMENT = 0, + RECT, + ARC, + CIRCLE, + POLY, + BEZIER, + LAST ///< marker for list end +}; + + +class EDA_SHAPE +{ +public: + EDA_SHAPE( SHAPE_T aType, int aDefaultLineWidth ); + + // Do not create a copy constructor & operator=. + // The ones generated by the compiler are adequate. + + ~EDA_SHAPE(); + + void SwapShape( EDA_SHAPE* aImage ); + + wxString ShowShape() const; + + wxString SHAPE_T_asString() const; + + void SetFilled( bool aFlag ) { m_filled = aFlag; } + + bool IsFilled() const + { + switch( m_shape ) + { + case SHAPE_T::RECT: + case SHAPE_T::CIRCLE: + case SHAPE_T::POLY: + return m_filled; + + case SHAPE_T::SEGMENT: + case SHAPE_T::ARC: + case SHAPE_T::BEZIER: + return false; + + case SHAPE_T::LAST: // Make CLang compiler happy + return false; + } + + return false; // Make GCC compiler happy + } + + void SetWidth( int aWidth ) { m_width = aWidth; } + int GetWidth() const { return m_width; } + + /** + * Set the angle for arcs, and normalizes it within the range 0 - 360 degrees. + * + * @param aAngle is tenths of degrees, but will soon be degrees. + * @param aUpdateEnd set to true to update also arc end coordinates m_thirdPoint, so must + * be called after setting m_Start and m_End. + */ + virtual void SetAngle( double aAngle, bool aUpdateEnd = true ); + double GetAngle() const { return m_angle; } + + void SetShape( SHAPE_T aShape ) { m_shape = aShape; } + SHAPE_T GetShape() const { return m_shape; } + + void SetBezierC1( const wxPoint& aPoint ) { m_bezierC1 = aPoint; } + const wxPoint& GetBezierC1() const { return m_bezierC1; } + + void SetBezierC2( const wxPoint& aPoint ) { m_bezierC2 = aPoint; } + const wxPoint& GetBezierC2() const { return m_bezierC2; } + + void SetShapePos( const wxPoint& aPos ); + wxPoint GetShapePos() const; + + /** + * Return the starting point of the graphic. + */ + const wxPoint& GetStart() const { return m_start; } + int GetStartY() { return m_start.y; } + int GetStartX() { return m_start.x; } + void SetStart( const wxPoint& aStart ) { m_start = aStart; } + void SetStartY( int y ) { m_start.y = y; } + void SetStartX( int x ) { m_start.x = x; } + + /** + * Return the ending point of the graphic. + */ + const wxPoint& GetEnd() const { return m_end; } + int GetEndY() { return m_end.y; } + int GetEndX() { return m_end.x; } + void SetEnd( const wxPoint& aEnd ) { m_end = aEnd; } + void SetEndY( int y ) { m_end.y = y; } + void SetEndX( int x ) { m_end.x = x; } + + /** + * Function GetThirdPoint + * returns the third point point of the graphic + */ + const wxPoint& GetThirdPoint() const { return m_thirdPoint; } + int GetThirdPointY() { return m_thirdPoint.y; } + int GetThirdPointX() { return m_thirdPoint.x; } + void SetThirdPoint( const wxPoint& aPoint ) { m_thirdPoint = aPoint; } + void SetThirdPointY( int y ) { m_thirdPoint.y = y; } + void SetThirdPointX( int x ) { m_thirdPoint.x = x; } + + // Some attributes are read only, since they are derived from m_Start, m_End, and m_Angle. + // No Set...() function for these attributes. + + wxPoint getCenter() const; + wxPoint GetArcStart() const { return m_end; } + wxPoint GetArcEnd() const; + wxPoint GetArcMid() const; + std::vector GetRectCorners() const; + + /** + * @return the angle of the starting point of this arc, between 0 and 3600 in 0.1 deg. + */ + double GetArcAngleStart() const; + + /** + * @return the angle of the ending point of this arc, between 0 and 3600 in 0.1 deg. + */ + double GetArcAngleEnd() const; + + /** + * Return the radius of this item. + * + * Has meaning only for arcs and circles. + */ + int GetRadius() const; + + /** + * Initialize the start arc point. + * + * Can be used for circles to initialize one point of the cicumference. + */ + void SetArcStart( const wxPoint& aArcStartPoint ) + { + m_end = aArcStartPoint; + } + + /** + * Initialize the end arc point. + * + * Can be used for circles to initialize one point of the cicumference. + */ + void SetArcEnd( const wxPoint& aArcEndPoint ) + { + m_thirdPoint = aArcEndPoint; + } + + void SetArcCenter( const wxPoint& aCenterPoint ) { m_start = aCenterPoint; } + + /** + * Set the three controlling points for an arc. + * + * NB: these are NOT what's currently stored, so we have to do some calculations behind + * the scenes. However, they are what SHOULD be stored. + */ + void SetArcGeometry( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd ); + + const std::vector& GetBezierPoints() const { return m_bezierPoints; } + + /** + * Duplicate the list of corners in a std::vector + * + * It must be used only to convert the SHAPE_POLY_SET internal corner buffer + * to a list of wxPoints, and nothing else, because it duplicates the buffer, + * that is inefficient to know for instance the corner count + */ + void DupPolyPointsList( std::vector& aBuffer ) const; + + /** + * @return the number of corners of the polygonal shape + */ + int GetPointCount() const; + + // Accessors to the polygonal shape + SHAPE_POLY_SET& GetPolyShape() { return m_poly; } + const SHAPE_POLY_SET& GetPolyShape() const { return m_poly; } + + /** + * @return true if the polygonal shape is valid (has more than 2 points) + */ + bool IsPolyShapeValid() const; + + void SetPolyShape( const SHAPE_POLY_SET& aShape ) { m_poly = aShape; } + + void SetBezierPoints( const std::vector& aPoints ) { m_bezierPoints = aPoints; } + + /** + * Rebuild the m_BezierPoints vertex list that approximate the Bezier curve + * by a list of segments. + * + * Has meaning only for BEZIER shape. + * + * @param aMinSegLen is the min length of segments approximating the bezier. The shape's last + * segment can be shorter. This parameter avoids having too many very short + * segment in list. Good values are between m_width/2 and m_width. + */ + void RebuildBezierToSegmentsPointsList( int aMinSegLen ); + + void SetPolyPoints( const std::vector& aPoints ); + + /** + * Make a set of SHAPE objects representing the EDA_SHAPE. Caller owns the objects. + */ + // fixme: move to shape_compound + std::vector MakeEffectiveShapes() const; + + void ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector& aList ); + + /** + * Return the length of the track using the hypotenuse calculation. + * + * @return the length of the track + */ + double GetLength() const; + + /** + * Convert the shape to a closed polygon. + * + * Used in filling zones calculations. Circles and arcs are approximated by segments. + * + * @param aCornerBuffer is a buffer to store the polygon. + * @param aClearanceValue is the clearance around the pad. + * @param aError is the maximum deviation from a true arc. + * @param aErrorLoc whether any approximation error shoule be placed inside or outside + * @param ignoreLineWidth is used for edge cut items where the line width is only + * for visualization + */ + void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearanceValue, + int aError, ERROR_LOC aErrorLoc, + bool ignoreLineWidth ) const; + + int Compare( const EDA_SHAPE* aOther ) const; + +protected: + void move( const wxPoint& aMoveVector ); + void rotate( const wxPoint& aRotCentre, double aAngle ); + void flip( const wxPoint& aCentre, bool aFlipLeftRight ); + void scale( double aScale ); + + // To be implemented by concrete classes + virtual double getParentOrientation() const = 0; + virtual wxPoint getParentPosition() const = 0; + + const EDA_RECT getBoundingBox() const; + + void computeArcBBox( EDA_RECT& aBBox ) const; + + bool hitTest( const wxPoint& aPosition, int aAccuracy = 0 ) const; + bool hitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const; + + const std::vector buildBezierToSegmentsPointsList( int aMinSegLen ) const; + +protected: + SHAPE_T m_shape; // Shape: line, Circle, Arc + int m_width; // thickness of lines ... + bool m_filled; // Pretty much what it says on the tin... + wxPoint m_start; // Line start point or Circle and Arc center + wxPoint m_end; // Line end point or circle and arc start point + + wxPoint m_thirdPoint; // Used only for Arcs: arc end point + double m_angle; // Used only for Arcs: Arc angle in 1/10 deg + + wxPoint m_bezierC1; // Bezier Control Point 1 + wxPoint m_bezierC2; // Bezier Control Point 2 + + std::vector m_bezierPoints; + SHAPE_POLY_SET m_poly; // Stores the S_POLYGON shape +}; + +#endif // EDA_SHAPE_H diff --git a/include/eda_text.h b/include/eda_text.h index c31929f266..45f79434a5 100644 --- a/include/eda_text.h +++ b/include/eda_text.h @@ -366,6 +366,8 @@ public: virtual double GetDrawRotation() const; + int Compare( const EDA_TEXT* aOther ) const; + private: void cacheShownText(); diff --git a/include/preview_items/two_point_assistant.h b/include/preview_items/two_point_assistant.h index b4c0275e63..e14e0d427c 100644 --- a/include/preview_items/two_point_assistant.h +++ b/include/preview_items/two_point_assistant.h @@ -33,7 +33,7 @@ namespace KIGFX namespace PREVIEW { -// TODO: required until PCB_SHAPE_TYPE_T is moved into commons or a better approach is found +// TODO: required until EDA_SHAPE_TYPE_T is moved into commons or a better approach is found enum class GEOM_SHAPE { SEGMENT = 0, diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 87f2bca386..b524f0f258 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -272,7 +272,7 @@ set( PCBNEW_CLASS_SRCS array_pad_number_provider.cpp build_BOM_from_board.cpp cleanup_item.cpp - convert_drawsegment_list_to_polygon.cpp + convert_shape_list_to_polygon.cpp cross-probing.cpp edit.cpp edit_track_width.cpp diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp index dbacbaf359..7ad302e63b 100644 --- a/pcbnew/board.cpp +++ b/pcbnew/board.cpp @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include #include @@ -50,7 +52,7 @@ #include #include #include -#include +#include #include // This is an odd place for this, but CvPcb won't link if it's in board_item.cpp like I first @@ -2120,3 +2122,52 @@ BOARD::GroupLegalOpsField BOARD::GroupLegalOps( const PCB_SELECTION& selection ) return legalOps; } + + +bool BOARD::cmp_items::operator() ( const BOARD_ITEM* a, const BOARD_ITEM* b ) const +{ + if( a->Type() != b->Type() ) + return a->Type() < b->Type(); + + if( a->GetLayer() != b->GetLayer() ) + return a->GetLayer() < b->GetLayer(); + + if( a->GetPosition().x != b->GetPosition().x ) + return a->GetPosition().x < b->GetPosition().x; + + if( a->GetPosition().y != b->GetPosition().y ) + return a->GetPosition().y < b->GetPosition().y; + + if( a->m_Uuid != b->m_Uuid ) // shopuld be always the case foer valid boards + return a->m_Uuid < b->m_Uuid; + + return a < b; +} + + +bool BOARD::cmp_drawings::operator()( const BOARD_ITEM* aFirst, + const BOARD_ITEM* aSecond ) const +{ + if( aFirst->Type() != aSecond->Type() ) + return aFirst->Type() < aSecond->Type(); + + if( aFirst->GetLayer() != aSecond->GetLayer() ) + return aFirst->GetLayer() < aSecond->GetLayer(); + + if( aFirst->Type() == PCB_SHAPE_T ) + { + const PCB_SHAPE* shape = static_cast( aFirst ); + const PCB_SHAPE* other = static_cast( aSecond ); + return shape->Compare( other ); + } + else if( aFirst->Type() == PCB_TEXT_T ) + { + const PCB_TEXT* text = static_cast( aFirst ); + const PCB_TEXT* other = static_cast( aSecond ); + return text->Compare( other ); + } + + return aFirst->m_Uuid < aSecond->m_Uuid; +} + + diff --git a/pcbnew/board.h b/pcbnew/board.h index bdfb28f44f..c60e41a6d9 100644 --- a/pcbnew/board.h +++ b/pcbnew/board.h @@ -27,7 +27,7 @@ #include #include // Needed for stl hash extensions -#include // for OUTLINE_ERROR_HANDLER +#include // for OUTLINE_ERROR_HANDLER #include #include #include @@ -1073,6 +1073,18 @@ public: */ GroupLegalOpsField GroupLegalOps( const PCB_SELECTION& selection ) const; + // --------- Item order comparators --------- + + struct cmp_items + { + bool operator() ( const BOARD_ITEM* aFirst, const BOARD_ITEM* aSecond ) const; + }; + + struct cmp_drawings + { + bool operator()( const BOARD_ITEM* aFirst, const BOARD_ITEM* aSecond ) const; + }; + // ------------ Run-time caches ------------- std::mutex m_CachesMutex; std::map< std::pair, bool > m_InsideCourtyardCache; diff --git a/pcbnew/board_item.cpp b/pcbnew/board_item.cpp index 878d61e1ec..8625b27a42 100644 --- a/pcbnew/board_item.cpp +++ b/pcbnew/board_item.cpp @@ -32,21 +32,6 @@ #include -wxString BOARD_ITEM::ShowShape( SHAPE_T aShape ) -{ - switch( aShape ) - { - case SHAPE_T::SEGMENT: return _( "Line" ); - case SHAPE_T::RECT: return _( "Rect" ); - case SHAPE_T::ARC: return _( "Arc" ); - case SHAPE_T::CIRCLE: return _( "Circle" ); - case SHAPE_T::BEZIER: return _( "Bezier Curve" ); - case SHAPE_T::POLY: return _( "Polygon" ); - default: return wxT( "??" ); - } -} - - const BOARD* BOARD_ITEM::GetBoard() const { if( Type() == PCB_T ) diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index af13f39964..8c56e77d79 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -415,8 +415,8 @@ void PCB_TEXT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuff } -void PCB_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, - PCB_LAYER_ID aLayer, int aClearanceValue, +void EDA_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, + int aClearanceValue, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const { @@ -429,12 +429,12 @@ void PCB_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuf case SHAPE_T::CIRCLE: if( IsFilled() ) { - TransformCircleToPolygon( aCornerBuffer, GetCenter(), GetRadius() + width / 2, aError, + TransformCircleToPolygon( aCornerBuffer, getCenter(), GetRadius() + width / 2, aError, aErrorLoc ); } else { - TransformRingToPolygon( aCornerBuffer, GetCenter(), GetRadius(), width, aError, + TransformRingToPolygon( aCornerBuffer, getCenter(), GetRadius(), width, aError, aErrorLoc ); } @@ -479,12 +479,8 @@ void PCB_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuf break; // The polygon is expected to be a simple polygon; not self intersecting, no hole. - FOOTPRINT* footprint = GetParentFootprint(); - double orientation = footprint ? footprint->GetOrientation() : 0.0; - wxPoint offset; - - if( footprint ) - offset = footprint->GetPosition(); + double orientation = getParentOrientation(); + wxPoint offset = getParentPosition(); // Build the polygon with the actual position and orientation: std::vector poly; @@ -538,12 +534,22 @@ void PCB_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuf default: wxFAIL_MSG( "PCB_SHAPE::TransformShapeWithClearanceToPolygon no implementation for " - + SHAPE_T_asString( m_shape ) ); + + SHAPE_T_asString() ); break; } } +void PCB_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, + PCB_LAYER_ID aLayer, int aClearanceValue, + int aError, ERROR_LOC aErrorLoc, + bool ignoreLineWidth ) const +{ + EDA_SHAPE::TransformShapeWithClearanceToPolygon( aCornerBuffer, aClearanceValue, aError, + aErrorLoc, ignoreLineWidth ); +} + + void PCB_TRACK::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, PCB_LAYER_ID aLayer, int aClearanceValue, int aError, ERROR_LOC aErrorLoc, diff --git a/pcbnew/convert_drawsegment_list_to_polygon.cpp b/pcbnew/convert_shape_list_to_polygon.cpp similarity index 99% rename from pcbnew/convert_drawsegment_list_to_polygon.cpp rename to pcbnew/convert_shape_list_to_polygon.cpp index 54428259dd..d622963b98 100644 --- a/pcbnew/convert_drawsegment_list_to_polygon.cpp +++ b/pcbnew/convert_shape_list_to_polygon.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include @@ -535,8 +535,8 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SET& break; default: - wxFAIL_MSG( "Unsupported PCB_SHAPE type " - + BOARD_ITEM::ShowShape( graphic->GetShape() ) ); + wxFAIL_MSG( "ConvertOutlineToPolygon not implemented for " + + graphic->SHAPE_T_asString() ); return false; } @@ -781,9 +781,8 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SET& break; default: - wxFAIL_MSG( "Unsupported PCB_SHAPE type " - + BOARD_ITEM::ShowShape( graphic->GetShape() ) ); - + wxFAIL_MSG( "ConvertOutlineToPolygon not implemented for " + + graphic->SHAPE_T_asString() ); return false; } diff --git a/pcbnew/convert_drawsegment_list_to_polygon.h b/pcbnew/convert_shape_list_to_polygon.h similarity index 93% rename from pcbnew/convert_drawsegment_list_to_polygon.h rename to pcbnew/convert_shape_list_to_polygon.h index 84fc7037ce..2da04fe178 100644 --- a/pcbnew/convert_drawsegment_list_to_polygon.h +++ b/pcbnew/convert_shape_list_to_polygon.h @@ -21,12 +21,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/** - * @file convert_drawsegment_list_to_polygon.h - * @brief functions to convert a shape built with DRAWSEGMENTS to a polygon. - * expecting the shape describes shape similar to a polygon - */ - #pragma once class PCB_SHAPE; diff --git a/pcbnew/dialogs/dialog_footprint_checker.cpp b/pcbnew/dialogs/dialog_footprint_checker.cpp index 18e82ff614..1c7ac4b230 100644 --- a/pcbnew/dialogs/dialog_footprint_checker.cpp +++ b/pcbnew/dialogs/dialog_footprint_checker.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include diff --git a/pcbnew/dialogs/dialog_graphic_item_properties.cpp b/pcbnew/dialogs/dialog_graphic_item_properties.cpp index f523b33d83..85d0ab1a60 100644 --- a/pcbnew/dialogs/dialog_graphic_item_properties.cpp +++ b/pcbnew/dialogs/dialog_graphic_item_properties.cpp @@ -335,7 +335,7 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::TransferDataFromWindow() if( m_item->GetShape() == SHAPE_T::ARC ) { - m_item->SetCenter( GetArcCenter( m_item->GetArcStart(), m_item->GetArcEnd(), m_AngleValue ) ); + m_item->SetArcCenter( GetArcCenter( m_item->GetArcStart(), m_item->GetArcEnd(), m_AngleValue )); m_item->SetAngle( m_AngleValue * 10.0, false ); } @@ -408,8 +408,8 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::Validate() break; default: - wxASSERT_MSG( false, "DIALOG_GRAPHIC_ITEM_PROPERTIES::Validate not implemented for shape" - + PCB_SHAPE::ShowShape( m_item->GetShape() ) ); + wxFAIL_MSG( "DIALOG_GRAPHIC_ITEM_PROPERTIES::Validate not implemented for shape " + + m_item->SHAPE_T_asString() ); break; } diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp index 74208999aa..504b662cac 100644 --- a/pcbnew/footprint.cpp +++ b/pcbnew/footprint.cpp @@ -45,7 +45,7 @@ #include #include #include -#include +#include #include FOOTPRINT::FOOTPRINT( BOARD* parent ) : diff --git a/pcbnew/footprint.h b/pcbnew/footprint.h index 144608f6d5..6fa61b707d 100644 --- a/pcbnew/footprint.h +++ b/pcbnew/footprint.h @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include #include diff --git a/pcbnew/fp_shape.cpp b/pcbnew/fp_shape.cpp index 5869282452..194e5c83e9 100644 --- a/pcbnew/fp_shape.cpp +++ b/pcbnew/fp_shape.cpp @@ -38,10 +38,8 @@ FP_SHAPE::FP_SHAPE( FOOTPRINT* parent, SHAPE_T aShape ) : - PCB_SHAPE( parent, PCB_FP_SHAPE_T ) + PCB_SHAPE( parent, PCB_FP_SHAPE_T, aShape ) { - m_shape = aShape; - m_angle = 0; m_layer = F_SilkS; } @@ -122,7 +120,7 @@ void FP_SHAPE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vectorGetShape()) ); + + aShape->SHAPE_T_asString() ); return false; } } @@ -127,7 +127,7 @@ bool GRAPHICS_CLEANER::areEquivalent( PCB_SHAPE* aShape1, PCB_SHAPE* aShape2 ) default: wxFAIL_MSG( "GRAPHICS_CLEANER::areEquivalent unsupported PCB_SHAPE shape: " - + SHAPE_T_asString( aShape1->GetShape()) ); + + aShape1->SHAPE_T_asString() ); return false; } } diff --git a/pcbnew/import_gfx/graphics_importer_pcbnew.cpp b/pcbnew/import_gfx/graphics_importer_pcbnew.cpp index af9733e80e..b0d9f206cf 100644 --- a/pcbnew/import_gfx/graphics_importer_pcbnew.cpp +++ b/pcbnew/import_gfx/graphics_importer_pcbnew.cpp @@ -82,7 +82,7 @@ void GRAPHICS_IMPORTER_PCBNEW::AddCircle( const VECTOR2D& aCenter, double aRadiu circle->SetFilled( aFilled ); circle->SetLayer( GetLayer() ); circle->SetWidth( MapLineWidth( aWidth ) ); - circle->SetCenter( MapCoordinate( aCenter ) ); + circle->SetArcCenter( MapCoordinate( aCenter )); circle->SetArcStart( MapCoordinate( VECTOR2D( aCenter.x + aRadius, aCenter.y ) ) ); if( circle->Type() == PCB_FP_SHAPE_T ) @@ -99,7 +99,7 @@ void GRAPHICS_IMPORTER_PCBNEW::AddArc( const VECTOR2D& aCenter, const VECTOR2D& arc->SetShape( SHAPE_T::ARC ); arc->SetLayer( GetLayer() ); arc->SetWidth( MapLineWidth( aWidth ) ); - arc->SetCenter( MapCoordinate( aCenter) ); + arc->SetArcCenter( MapCoordinate( aCenter )); arc->SetArcStart( MapCoordinate( aStart ) ); arc->SetAngle( aAngle * 10.0 ); // Pcbnew uses the decidegree diff --git a/pcbnew/pad_custom_shape_functions.cpp b/pcbnew/pad_custom_shape_functions.cpp index c83c9d30ee..c93cce6bca 100644 --- a/pcbnew/pad_custom_shape_functions.cpp +++ b/pcbnew/pad_custom_shape_functions.cpp @@ -88,7 +88,7 @@ void PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aA PCB_SHAPE* item = new PCB_SHAPE(); item->SetShape( SHAPE_T::ARC ); item->SetFilled( false ); - item->SetCenter( aCenter ); + item->SetArcCenter( aCenter ); item->SetArcStart( aStart ); item->SetAngle( aArcAngle ); item->SetWidth( aThickness ); diff --git a/pcbnew/pcb_shape.cpp b/pcbnew/pcb_shape.cpp index bad23b12cb..b60ffa6f40 100644 --- a/pcbnew/pcb_shape.cpp +++ b/pcbnew/pcb_shape.cpp @@ -24,35 +24,20 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include -#include #include #include -#include #include #include -#include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include // for KiROUND +#include -PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T idtype ) : - BOARD_ITEM( aParent, idtype ) +PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T idtype, SHAPE_T shapetype ) : + BOARD_ITEM( aParent, idtype ), + EDA_SHAPE( shapetype, Millimeter2iu( DEFAULT_LINE_WIDTH ) ) { - m_angle = 0; - m_filled = false; m_flags = 0; - m_shape = SHAPE_T::SEGMENT; - m_width = Millimeter2iu( DEFAULT_LINE_WIDTH ); } @@ -61,21 +46,6 @@ PCB_SHAPE::~PCB_SHAPE() } -void PCB_SHAPE::SetPosition( const wxPoint& aPos ) -{ - m_start = aPos; -} - - -wxPoint PCB_SHAPE::GetPosition() const -{ - if( m_shape == SHAPE_T::POLY ) - return (wxPoint) m_poly.CVertex( 0 ); - else - return m_start; -} - - const wxPoint PCB_SHAPE::GetFocusPosition() const { // For some shapes return the visual center, but for not filled polygonal shapes, @@ -86,12 +56,14 @@ const wxPoint PCB_SHAPE::GetFocusPosition() const case SHAPE_T::CIRCLE: if( !IsFilled() ) return wxPoint( GetCenter().x + GetRadius(), GetCenter().y ); - break; + else + return GetCenter(); case SHAPE_T::RECT: if( !IsFilled() ) return GetStart(); - break; + else + return GetCenter(); case SHAPE_T::POLY: if( !IsFilled() ) @@ -99,436 +71,49 @@ const wxPoint PCB_SHAPE::GetFocusPosition() const VECTOR2I pos = GetPolyShape().Outline(0).CPoint(0); return wxPoint( pos.x, pos.y ); } - break; + else + { + return GetCenter(); + } case SHAPE_T::ARC: return GetArcMid(); - break; case SHAPE_T::BEZIER: return GetStart(); - break; default: - break; + return GetCenter(); } - - return GetCenter(); -} - - -double PCB_SHAPE::GetLength() const -{ - double length = 0.0; - - switch( m_shape ) - { - case SHAPE_T::BEZIER: - for( size_t ii = 1; ii < m_bezierPoints.size(); ++ii ) - length += GetLineLength( m_bezierPoints[ ii - 1], m_bezierPoints[ii] ); - - break; - - case SHAPE_T::SEGMENT: - length = GetLineLength( GetStart(), GetEnd() ); - break; - - case SHAPE_T::POLY: - for( int ii = 0; ii < m_poly.COutline( 0 ).SegmentCount(); ii++ ) - length += m_poly.COutline( 0 ).CSegment( ii ).Length(); - - break; - - case SHAPE_T::ARC: - length = 2 * M_PI * GetRadius() * ( GetAngle() / 3600.0 ); - break; - - default: - wxASSERT_MSG( false, "PCB_SHAPE::GetLength not implemented for shape" - + ShowShape( GetShape() ) ); - break; - } - - return length; } void PCB_SHAPE::Move( const wxPoint& aMoveVector ) { - // Move vector should not affect start/end for polygon since it will - // be applied directly to polygon outline. - if( m_shape != SHAPE_T::POLY ) - { - m_start += aMoveVector; - m_end += aMoveVector; - } - - switch ( m_shape ) - { - case SHAPE_T::POLY: - m_poly.Move( VECTOR2I( aMoveVector ) ); - break; - - case SHAPE_T::ARC: - m_thirdPoint += aMoveVector; - break; - - case SHAPE_T::BEZIER: - m_bezierC1 += aMoveVector; - m_bezierC2 += aMoveVector; - - for( wxPoint& pt : m_bezierPoints) - pt += aMoveVector; - - break; - - default: - break; - } + move( aMoveVector ); } void PCB_SHAPE::Scale( double aScale ) { - auto scalePt = [&]( wxPoint& pt ) - { - pt.x = KiROUND( pt.x * aScale ); - pt.y = KiROUND( pt.y * aScale ); - }; - - int radius = GetRadius(); - - scalePt( m_start ); - scalePt( m_end ); - - // specific parameters: - switch( m_shape ) - { - case SHAPE_T::BEZIER: - scalePt( m_bezierC1 ); - scalePt( m_bezierC2 ); - break; - - case SHAPE_T::ARC: - scalePt( m_thirdPoint ); - break; - - case SHAPE_T::CIRCLE: // ring or circle - m_end.x = m_start.x + KiROUND( radius * aScale ); - m_end.y = m_start.y; - break; - - case SHAPE_T::POLY: // polygon - { - std::vector pts; - - for( const VECTOR2I& pt : m_poly.Outline( 0 ).CPoints() ) - { - pts.emplace_back( pt ); - scalePt( pts.back() ); - } - - SetPolyPoints( pts ); - break; - } - - default: - break; - } + scale( aScale ); } void PCB_SHAPE::Rotate( const wxPoint& aRotCentre, double aAngle ) { - switch( m_shape ) - { - case SHAPE_T::ARC: - case SHAPE_T::SEGMENT: - case SHAPE_T::CIRCLE: - // these can all be done by just rotating the constituent points - RotatePoint( &m_start, aRotCentre, aAngle ); - RotatePoint( &m_end, aRotCentre, aAngle ); - RotatePoint( &m_thirdPoint, aRotCentre, aAngle ); - break; - - case SHAPE_T::RECT: - if( KiROUND( aAngle ) % 900 == 0 ) - { - RotatePoint( &m_start, aRotCentre, aAngle ); - RotatePoint( &m_end, aRotCentre, aAngle ); - break; - } - - // Convert non-cartesian-rotated rect to a diamond - m_shape = SHAPE_T::POLY; - m_poly.RemoveAllContours(); - m_poly.NewOutline(); - m_poly.Append( m_start ); - m_poly.Append( m_end.x, m_start.y ); - m_poly.Append( m_end ); - m_poly.Append( m_start.x, m_end.y ); - - KI_FALLTHROUGH; - - case SHAPE_T::POLY: - m_poly.Rotate( -DECIDEG2RAD( aAngle ), VECTOR2I( aRotCentre ) ); - break; - - case SHAPE_T::BEZIER: - RotatePoint( &m_start, aRotCentre, aAngle); - RotatePoint( &m_end, aRotCentre, aAngle); - RotatePoint( &m_bezierC1, aRotCentre, aAngle); - RotatePoint( &m_bezierC2, aRotCentre, aAngle); - - for( wxPoint& pt : m_bezierPoints ) - RotatePoint( &pt, aRotCentre, aAngle); - - break; - - default: - wxFAIL_MSG( "PCB_SHAPE::Rotate not implemented for " - + SHAPE_T_asString( m_shape ) ); - break; - } + rotate( aRotCentre, aAngle ); } void PCB_SHAPE::Flip( const wxPoint& aCentre, bool aFlipLeftRight ) { - if( aFlipLeftRight ) - { - m_start.x = aCentre.x - ( m_start.x - aCentre.x ); - m_end.x = aCentre.x - ( m_end.x - aCentre.x ); - } - else - { - m_start.y = aCentre.y - ( m_start.y - aCentre.y ); - m_end.y = aCentre.y - ( m_end.y - aCentre.y ); - } - - switch ( m_shape ) - { - case SHAPE_T::ARC: - if( aFlipLeftRight ) - m_thirdPoint.x = aCentre.x - ( m_thirdPoint.x - aCentre.x ); - else - m_thirdPoint.y = aCentre.y - ( m_thirdPoint.y - aCentre.y ); - - m_angle = -m_angle; - break; - - case SHAPE_T::POLY: - m_poly.Mirror( aFlipLeftRight, !aFlipLeftRight, VECTOR2I( aCentre ) ); - break; - - case SHAPE_T::BEZIER: - { - if( aFlipLeftRight ) - { - m_bezierC1.x = aCentre.x - ( m_bezierC1.x - aCentre.x ); - m_bezierC2.x = aCentre.x - ( m_bezierC2.x - aCentre.x ); - } - else - { - m_bezierC1.y = aCentre.y - ( m_bezierC1.y - aCentre.y ); - m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y ); - } - - // Rebuild the poly points shape - std::vector ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end }; - BEZIER_POLY converter( ctrlPoints ); - converter.GetPoly( m_bezierPoints, m_width ); - } - break; - - case SHAPE_T::SEGMENT: - case SHAPE_T::RECT: - case SHAPE_T::CIRCLE: - break; - - default: - wxFAIL_MSG( "PCB_SHAPE::Flip not implemented for " - + SHAPE_T_asString( m_shape ) ); - break; - } + flip( aCentre, aFlipLeftRight ); SetLayer( FlipLayer( GetLayer(), GetBoard()->GetCopperLayerCount() ) ); } -void PCB_SHAPE::RebuildBezierToSegmentsPointsList( int aMinSegLen ) -{ - // Has meaning only for S_CURVE DRAW_SEGMENT shape - if( m_shape != SHAPE_T::BEZIER ) - { - m_bezierPoints.clear(); - return; - } - // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve - m_bezierPoints = buildBezierToSegmentsPointsList( aMinSegLen ); -} - - -const std::vector PCB_SHAPE::buildBezierToSegmentsPointsList( int aMinSegLen ) const -{ - std::vector bezierPoints; - - // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve - std::vector ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end }; - BEZIER_POLY converter( ctrlPoints ); - converter.GetPoly( bezierPoints, aMinSegLen ); - - return bezierPoints; -} - - -wxPoint PCB_SHAPE::GetCenter() const -{ - wxPoint c; - - switch( m_shape ) - { - case SHAPE_T::ARC: - case SHAPE_T::CIRCLE: - c = m_start; - break; - - case SHAPE_T::SEGMENT: - // Midpoint of the line - c = ( GetStart() + GetEnd() ) / 2; - break; - - case SHAPE_T::POLY: - case SHAPE_T::RECT: - case SHAPE_T::BEZIER: - c = GetBoundingBox().Centre(); - break; - - default: - wxFAIL_MSG( "PCB_SHAPE::GetCentre not implemented for " - + SHAPE_T_asString( m_shape ) ); - break; - } - - return c; -} - - -wxPoint PCB_SHAPE::GetArcEnd() const -{ - wxPoint endPoint( m_end ); // start of arc - - switch( m_shape ) - { - case SHAPE_T::ARC: - endPoint = m_thirdPoint; - break; - - default: - break; - } - - return endPoint; // after rotation, the end of the arc. -} - - -wxPoint PCB_SHAPE::GetArcMid() const -{ - wxPoint endPoint( m_end ); - - switch( m_shape ) - { - case SHAPE_T::ARC: - // rotate the starting point of the arc, given by m_End, through half - // the angle m_Angle to get the middle of the arc. - // m_Start is the arc center - endPoint = m_end; // m_End = start point of arc - RotatePoint( &endPoint, m_start, -m_angle / 2.0 ); - break; - - default: - break; - } - - return endPoint; // after rotation, the end of the arc. -} - - -double PCB_SHAPE::GetArcAngleStart() const -{ - // due to the Y axis orient atan2 needs - y value - double angleStart = ArcTangente( GetArcStart().y - GetCenter().y, - GetArcStart().x - GetCenter().x ); - - // Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg - // because 180 deg and -180 are very near angles when mapping between -180 ... 180 deg. - // and this is not easy to handle in calculations - NORMALIZE_ANGLE_POS( angleStart ); - - return angleStart; -} - - -double PCB_SHAPE::GetArcAngleEnd() const -{ - // due to the Y axis orient atan2 needs - y value - double angleStart = ArcTangente( GetArcEnd().y - GetCenter().y, - GetArcEnd().x - GetCenter().x ); - - // Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg - // because 180 deg and -180 are very near angles when mapping between -180 ... 180 deg. - // and this is not easy to handle in calculations - NORMALIZE_ANGLE_POS( angleStart ); - - return angleStart; -} - - -int PCB_SHAPE::GetRadius() const -{ - double radius = GetLineLength( m_start, m_end ); - - // don't allow degenerate arcs - return std::max( 1, KiROUND( radius ) ); -} - - -void PCB_SHAPE::SetArcGeometry( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd ) -{ - SetArcStart( aStart ); - SetArcEnd( aEnd ); - - // Sadly we currently store center and angle rather than mid. So we have to calculate - // those. - wxPoint center = GetArcCenter( aStart, aMid, aEnd ); - VECTOR2D startLine = aStart - center; - VECTOR2D endLine = aEnd - center; - bool clockwise = GetAngle() > 0; - double angle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() ); - - if( clockwise && angle < 0.0 ) - angle += 3600.0; - else if( !clockwise && angle > 0.0 ) - angle -= 3600.0; - - SetAngle( angle, false ); - SetCenter( center ); -} - - -void PCB_SHAPE::SetAngle( double aAngle, bool aUpdateEnd ) -{ - // m_Angle must be >= -360 and <= +360 degrees - m_angle = NormalizeAngle360Max( aAngle ); - - if( aUpdateEnd ) - { - m_thirdPoint = m_end; - RotatePoint( &m_thirdPoint, m_start, -m_angle ); - } -} - - FOOTPRINT* PCB_SHAPE::GetParentFootprint() const { if( !m_parent || m_parent->Type() != PCB_FOOTPRINT_T ) @@ -538,478 +123,40 @@ FOOTPRINT* PCB_SHAPE::GetParentFootprint() const } +double PCB_SHAPE::getParentOrientation() const +{ + if( GetParentFootprint() ) + return GetParentFootprint()->GetOrientation(); + else + return 0.0; +} + + +wxPoint PCB_SHAPE::getParentPosition() const +{ + if( GetParentFootprint() ) + return GetParentFootprint()->GetPosition(); + else + return wxPoint( 0, 0 ); +} + + void PCB_SHAPE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector& aList ) { - EDA_UNITS units = aFrame->GetUserUnits(); - ORIGIN_TRANSFORMS originTransforms = aFrame->GetOriginTransforms(); - wxString msg; - aList.emplace_back( _( "Type" ), _( "Drawing" ) ); if( IsLocked() ) aList.emplace_back( _( "Status" ), _( "Locked" ) ); - wxString shape = _( "Shape" ); - - switch( m_shape ) - { - case SHAPE_T::CIRCLE: - aList.emplace_back( shape, _( "Circle" ) ); - - msg = MessageTextFromValue( units, GetLineLength( m_start, m_end ) ); - aList.emplace_back( _( "Radius" ), msg ); - break; - - case SHAPE_T::ARC: - aList.emplace_back( shape, _( "Arc" ) ); - - msg.Printf( wxT( "%.1f" ), m_angle / 10.0 ); - aList.emplace_back( _( "Angle" ), msg ); - - msg = MessageTextFromValue( units, GetLineLength( m_start, m_end ) ); - aList.emplace_back( _( "Radius" ), msg ); - break; - - case SHAPE_T::BEZIER: - aList.emplace_back( shape, _( "Curve" ) ); - - msg = MessageTextFromValue( units, GetLength() ); - aList.emplace_back( _( "Length" ), msg ); - break; - - case SHAPE_T::POLY: - aList.emplace_back( shape, _( "Polygon" ) ); - - msg.Printf( "%d", GetPolyShape().Outline(0).PointCount() ); - aList.emplace_back( _( "Points" ), msg ); - break; - - case SHAPE_T::RECT: - aList.emplace_back( shape, _( "Rectangle" ) ); - - msg = MessageTextFromValue( units, std::abs( m_end.x - m_start.x ) ); - aList.emplace_back( _( "Width" ), msg ); - - msg = MessageTextFromValue( units, std::abs( m_end.y - m_start.y ) ); - aList.emplace_back( _( "Height" ), msg ); - break; - - case SHAPE_T::SEGMENT: - { - aList.emplace_back( shape, _( "Segment" ) ); - - msg = MessageTextFromValue( units, GetLineLength( m_start, m_end ) ); - aList.emplace_back( _( "Length" ), msg ); - - // angle counter-clockwise from 3'o-clock - const double deg = RAD2DEG( atan2( (double)( m_start.y - m_end.y ), - (double)( m_end.x - m_start.x ) ) ); - aList.emplace_back( _( "Angle" ), wxString::Format( "%.1f", deg ) ); - } - break; - - default: - aList.emplace_back( shape, _( "Unrecognized" ) ); - break; - } + ShapeGetMsgPanelInfo( aFrame, aList ); aList.emplace_back( _( "Layer" ), GetLayerName() ); - - aList.emplace_back( _( "Line width" ), MessageTextFromValue( units, m_width ) ); -} - - -const EDA_RECT PCB_SHAPE::GetBoundingBox() const -{ - EDA_RECT bbox; - - bbox.SetOrigin( m_start ); - - switch( m_shape ) - { - case SHAPE_T::RECT: - { - std::vector pts = GetRectCorners(); - - bbox = EDA_RECT(); // re-init for merging - - for( wxPoint& pt : pts ) - bbox.Merge( pt ); - - break; - } - - case SHAPE_T::SEGMENT: - bbox.SetEnd( m_end ); - break; - - case SHAPE_T::CIRCLE: - bbox.Inflate( GetRadius() ); - break; - - case SHAPE_T::ARC: - computeArcBBox( bbox ); - break; - - case SHAPE_T::POLY: - { - if( m_poly.IsEmpty() ) - break; - - FOOTPRINT* parentFootprint = GetParentFootprint(); - bbox = EDA_RECT(); // re-init for merging - - for( auto iter = m_poly.CIterate(); iter; iter++ ) - { - wxPoint pt( iter->x, iter->y ); - - if( parentFootprint ) // Transform, if we belong to a footprint - { - RotatePoint( &pt, parentFootprint->GetOrientation() ); - pt += parentFootprint->GetPosition(); - } - - bbox.Merge( pt ); - } - - break; - } - - case SHAPE_T::BEZIER: - bbox.Merge( m_bezierC1 ); - bbox.Merge( m_bezierC2 ); - bbox.Merge( m_end ); - break; - - default: - wxFAIL_MSG( "PCB_SHAPE::GetBoundingBox not implemented for " - + SHAPE_T_asString( m_shape ) ); - break; - } - - bbox.Inflate( m_width / 2 ); - bbox.Normalize(); - - return bbox; -} - - -bool PCB_SHAPE::HitTest( const wxPoint& aPosition, int aAccuracy ) const -{ - int maxdist = aAccuracy + ( m_width / 2 ); - - switch( m_shape ) - { - case SHAPE_T::CIRCLE: - { - int radius = GetRadius(); - int dist = KiROUND( EuclideanNorm( aPosition - GetCenter() ) ); - - if( IsFilled() ) // Filled circle hit-test - { - if( dist <= radius + maxdist ) - return true; - } - else // Ring hit-test - { - if( abs( radius - dist ) <= maxdist ) - return true; - } - - break; - } - - case SHAPE_T::ARC: - { - wxPoint relPos = aPosition - GetCenter(); - int radius = GetRadius(); - int dist = KiROUND( EuclideanNorm( relPos ) ); - - if( abs( radius - dist ) <= maxdist ) - { - // For arcs, the test point angle must be >= arc angle start - // and <= arc angle end - // However angle values > 360 deg are not easy to handle - // so we calculate the relative angle between arc start point and test point - // this relative arc should be < arc angle if arc angle > 0 (CW arc) - // and > arc angle if arc angle < 0 (CCW arc) - double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg - - double arc_hittest = ArcTangente( relPos.y, relPos.x ); - - // Calculate relative angle between the starting point of the arc, and the test point - arc_hittest -= arc_angle_start; - - // Normalize arc_hittest between 0 ... 360 deg - NORMALIZE_ANGLE_POS( arc_hittest ); - - // Check angle: inside the arc angle when it is > 0 - // and outside the not drawn arc when it is < 0 - if( GetAngle() >= 0.0 ) - { - if( arc_hittest <= GetAngle() ) - return true; - } - else - { - if( arc_hittest >= ( 3600.0 + GetAngle() ) ) - return true; - } - } - - break; - } - - case SHAPE_T::BEZIER: - const_cast( this )->RebuildBezierToSegmentsPointsList( m_width ); - - for( unsigned int i= 1; i < m_bezierPoints.size(); i++) - { - if( TestSegmentHit( aPosition, m_bezierPoints[ i - 1], m_bezierPoints[i], maxdist ) ) - return true; - } - - break; - - case SHAPE_T::SEGMENT: - if( TestSegmentHit( aPosition, m_start, m_end, maxdist ) ) - return true; - - break; - - case SHAPE_T::RECT: - { - std::vector pts = GetRectCorners(); - - if( IsFilled() ) // Filled rect hit-test - { - SHAPE_POLY_SET poly; - poly.NewOutline(); - - for( const wxPoint& pt : pts ) - poly.Append( pt ); - - if( poly.Collide( VECTOR2I( aPosition ), maxdist ) ) - return true; - } - else // Open rect hit-test - { - if( TestSegmentHit( aPosition, pts[0], pts[1], maxdist ) - || TestSegmentHit( aPosition, pts[1], pts[2], maxdist ) - || TestSegmentHit( aPosition, pts[2], pts[3], maxdist ) - || TestSegmentHit( aPosition, pts[3], pts[0], maxdist ) ) - { - return true; - } - } - - break; - } - - case SHAPE_T::POLY: - if( IsFilled() ) - { - return m_poly.Collide( VECTOR2I( aPosition ), maxdist ); - } - else - { - SHAPE_POLY_SET::VERTEX_INDEX dummy; - return m_poly.CollideEdge( VECTOR2I( aPosition ), dummy, maxdist ); - } - - break; - - default: - wxFAIL_MSG( "PCB_SHAPE::HitTest (point) not implemented for " - + SHAPE_T_asString( m_shape ) ); - break; - } - - return false; -} - - -bool PCB_SHAPE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const -{ - EDA_RECT arect = aRect; - arect.Normalize(); - arect.Inflate( aAccuracy ); - - EDA_RECT arcRect; - EDA_RECT bb = GetBoundingBox(); - - switch( m_shape ) - { - case SHAPE_T::CIRCLE: - // Test if area intersects or contains the circle: - if( aContained ) - { - return arect.Contains( bb ); - } - else - { - // If the rectangle does not intersect the bounding box, this is a much quicker test - if( !aRect.Intersects( bb ) ) - { - return false; - } - else - { - return arect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() ); - } - } - - break; - - case SHAPE_T::ARC: - // Test for full containment of this arc in the rect - if( aContained ) - { - return arect.Contains( bb ); - } - else - { - // Test if the rect crosses the arc - arcRect = bb.Common( arect ); - - /* All following tests must pass: - * 1. Rectangle must intersect arc BoundingBox - * 2. Rectangle must cross the outside of the arc - */ - return arcRect.Intersects( arect ) && - arcRect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() ); - } - - break; - - case SHAPE_T::RECT: - if( aContained ) - { - return arect.Contains( bb ); - } - else - { - std::vector pts = GetRectCorners(); - - // Account for the width of the lines - arect.Inflate( GetWidth() / 2 ); - return ( arect.Intersects( pts[0], pts[1] ) - || arect.Intersects( pts[1], pts[2] ) - || arect.Intersects( pts[2], pts[3] ) - || arect.Intersects( pts[3], pts[0] ) ); - } - - break; - - case SHAPE_T::SEGMENT: - if( aContained ) - { - return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() ); - } - else - { - // Account for the width of the line - arect.Inflate( GetWidth() / 2 ); - return arect.Intersects( GetStart(), GetEnd() ); - } - - break; - - case SHAPE_T::POLY: - if( aContained ) - { - return arect.Contains( bb ); - } - else - { - // Fast test: if aRect is outside the polygon bounding box, - // rectangles cannot intersect - if( !arect.Intersects( bb ) ) - return false; - - // Account for the width of the line - arect.Inflate( GetWidth() / 2 ); - - // Polygons in footprints use coordinates relative to the footprint. - // Therefore, instead of using m_poly, we make a copy which is translated - // to the actual location in the board. - - FOOTPRINT* fp{ GetParentFootprint() }; - double orientation{ fp ? -DECIDEG2RAD( fp->GetOrientation() ) : 0.0 }; - wxPoint offset; - - if( fp ) - offset = fp->GetPosition(); - - SHAPE_POLY_SET poly{ m_poly }; - poly.Rotate( orientation ); - poly.Move( offset ); - - int count = poly.TotalVertices(); - - for( int ii = 0; ii < count; ii++ ) - { - auto vertex = poly.CVertex( ii ); - auto vertexNext = poly.CVertex(( ii + 1 ) % count ); - - // Test if the point is within aRect - if( arect.Contains( ( wxPoint ) vertex ) ) - return true; - - // Test if this edge intersects aRect - if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) ) - return true; - } - } - - break; - - case SHAPE_T::BEZIER: - if( aContained ) - { - return arect.Contains( bb ); - } - else - { - // Fast test: if aRect is outside the polygon bounding box, - // rectangles cannot intersect - if( !arect.Intersects( bb ) ) - return false; - - // Account for the width of the line - arect.Inflate( GetWidth() / 2 ); - unsigned count = m_bezierPoints.size(); - - for( unsigned ii = 1; ii < count; ii++ ) - { - wxPoint vertex = m_bezierPoints[ ii - 1]; - wxPoint vertexNext = m_bezierPoints[ii]; - - // Test if the point is within aRect - if( arect.Contains( ( wxPoint ) vertex ) ) - return true; - - // Test if this edge intersects aRect - if( arect.Intersects( vertex, vertexNext ) ) - return true; - } - } - - break; - - default: - wxFAIL_MSG( "PCB_SHAPE::HitTest (rect) not implemented for " - + SHAPE_T_asString( m_shape ) ); - break; - } - - return false; } wxString PCB_SHAPE::GetSelectMenuText( EDA_UNITS aUnits ) const { - return wxString::Format( _( "%s on %s" ), ShowShape( m_shape ), GetLayerName() ); + return wxString::Format( _( "%s on %s" ), ShowShape(), GetLayerName() ); } @@ -1029,306 +176,34 @@ const BOX2I PCB_SHAPE::ViewBBox() const { // For arcs - do not include the center point in the bounding box, // it is redundant for displaying an arc - if( m_shape == SHAPE_T::ARC ) + if( GetShape() == SHAPE_T::ARC ) { EDA_RECT bbox; - bbox.SetOrigin( m_end ); computeArcBBox( bbox ); return BOX2I( bbox.GetOrigin(), bbox.GetSize() ); } BOX2I return_box = EDA_ITEM::ViewBBox(); - return_box.Inflate( m_width ); // Technically m_width / 2, but it never hurts to be a - // bit large to account for selection shadows, etc. + return_box.Inflate( GetWidth() ); // Technically GetWidth() / 2, but it never hurts to be + // a bit large to account for selection shadows, etc. return return_box; } -std::vector PCB_SHAPE::GetRectCorners() const -{ - std::vector pts; - FOOTPRINT* parentFootprint = GetParentFootprint(); - wxPoint topLeft = GetStart(); - wxPoint botRight = GetEnd(); - - // Un-rotate rect topLeft and botRight - if( parentFootprint && KiROUND( parentFootprint->GetOrientation() ) % 900 != 0 ) - { - topLeft -= parentFootprint->GetPosition(); - RotatePoint( &topLeft, -parentFootprint->GetOrientation() ); - - botRight -= parentFootprint->GetPosition(); - RotatePoint( &botRight, -parentFootprint->GetOrientation() ); - } - - // Set up the un-rotated 4 corners - pts.emplace_back( topLeft ); - pts.emplace_back( botRight.x, topLeft.y ); - pts.emplace_back( botRight ); - pts.emplace_back( topLeft.x, botRight.y ); - - // Now re-rotate the 4 corners to get a diamond - if( parentFootprint && KiROUND( parentFootprint->GetOrientation() ) % 900 != 0 ) - { - for( wxPoint& pt : pts ) - { - RotatePoint( &pt, parentFootprint->GetOrientation() ); - pt += parentFootprint->GetPosition(); - } - } - - return pts; -} - - -void PCB_SHAPE::computeArcBBox( EDA_RECT& aBBox ) const -{ - // Do not include the center, which is not necessarily - // inside the BB of a arc with a small angle - aBBox.SetOrigin( m_end ); - - wxPoint end = m_end; - RotatePoint( &end, m_start, -m_angle ); - aBBox.Merge( end ); - - // Determine the starting quarter - // 0 right-bottom - // 1 left-bottom - // 2 left-top - // 3 right-top - unsigned int quarter = 0; // assume right-bottom - - if( m_end.x < m_start.x ) - { - if( m_end.y <= m_start.y ) - quarter = 2; - else // ( m_End.y > m_Start.y ) - quarter = 1; - } - else if( m_end.x >= m_start.x ) - { - if( m_end.y < m_start.y ) - quarter = 3; - else if( m_end.x == m_start.x ) - quarter = 1; - } - - int radius = GetRadius(); - int angle = (int) GetArcAngleStart() % 900 + m_angle; - bool directionCW = ( m_angle > 0 ); // Is the direction of arc clockwise? - - // Make the angle positive, so we go clockwise and merge points belonging to the arc - if( !directionCW ) - { - angle = 900 - angle; - quarter = ( quarter + 3 ) % 4; // -1 modulo arithmetic - } - - while( angle > 900 ) - { - switch( quarter ) - { - case 0: aBBox.Merge( wxPoint( m_start.x, m_start.y + radius ) ); break; // down - case 1: aBBox.Merge( wxPoint( m_start.x - radius, m_start.y ) ); break; // left - case 2: aBBox.Merge( wxPoint( m_start.x, m_start.y - radius ) ); break; // up - case 3: aBBox.Merge( wxPoint( m_start.x + radius, m_start.y ) ); break; // right - } - - if( directionCW ) - ++quarter; - else - quarter += 3; // -1 modulo arithmetic - - quarter %= 4; - angle -= 900; - } - - aBBox.Inflate( m_width ); // Technically m_width / 2, but it doesn't hurt to have the - // bounding box a bit large to account for drawing clearances, - // etc. -} - - -void PCB_SHAPE::SetPolyPoints( const std::vector& aPoints ) -{ - m_poly.RemoveAllContours(); - m_poly.NewOutline(); - - for ( const wxPoint& p : aPoints ) - m_poly.Append( p.x, p.y ); -} - - -std::vector PCB_SHAPE::MakeEffectiveShapes() const -{ - std::vector effectiveShapes; - - switch( m_shape ) - { - case SHAPE_T::ARC: - effectiveShapes.emplace_back( new SHAPE_ARC( GetCenter(), GetArcStart(), - GetAngle() / 10.0, m_width ) ); - break; - - case SHAPE_T::SEGMENT: - effectiveShapes.emplace_back( new SHAPE_SEGMENT( GetStart(), GetEnd(), m_width ) ); - break; - - case SHAPE_T::RECT: - { - std::vector pts = GetRectCorners(); - - if( IsFilled() ) - { - effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) ); - } - - if( m_width > 0 || !IsFilled() ) - { - effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], m_width ) ); - effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], m_width ) ); - effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], m_width ) ); - effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], m_width ) ); - } - - break; - } - - case SHAPE_T::CIRCLE: - { - if( IsFilled() ) - { - effectiveShapes.emplace_back( new SHAPE_CIRCLE( GetCenter(), GetRadius() ) ); - } - - if( m_width > 0 || !IsFilled() ) - { - // SHAPE_CIRCLE has no ConvertToPolyline() method, so use a 360.0 SHAPE_ARC - SHAPE_ARC circle( GetCenter(), GetEnd(), 360.0 ); - SHAPE_LINE_CHAIN l = circle.ConvertToPolyline(); - - for( int i = 0; i < l.SegmentCount(); i++ ) - { - effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A, - l.Segment( i ).B, m_width ) ); - } - } - - break; - } - - case SHAPE_T::BEZIER: - { - auto bezierPoints = buildBezierToSegmentsPointsList( GetWidth() ); - wxPoint start_pt = bezierPoints[0]; - - for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ ) - { - wxPoint end_pt = bezierPoints[jj]; - effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, m_width ) ); - start_pt = end_pt; - } - - break; - } - - case SHAPE_T::POLY: - { - SHAPE_LINE_CHAIN l = GetPolyShape().COutline( 0 ); - FOOTPRINT* parentFootprint = dynamic_cast( m_parent ); - - if( parentFootprint ) - { - l.Rotate( -parentFootprint->GetOrientationRadians() ); - l.Move( parentFootprint->GetPosition() ); - } - - if( IsFilled() ) - { - effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) ); - } - - if( m_width > 0 || !IsFilled() ) - { - for( int i = 0; i < l.SegmentCount(); i++ ) - effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ), m_width ) ); - } - - break; - } - - default: - wxFAIL_MSG( "PCB_SHAPE::MakeEffectiveShapes unsupported PCB_SHAPE shape: " - + SHAPE_T_asString( m_shape ) ); - break; - } - - return effectiveShapes; -} - - std::shared_ptr PCB_SHAPE::GetEffectiveShape( PCB_LAYER_ID aLayer ) const { return std::make_shared( MakeEffectiveShapes() ); } -void PCB_SHAPE::DupPolyPointsList( std::vector& aBuffer ) const -{ - if( m_poly.OutlineCount() ) - { - int pointCount = m_poly.COutline( 0 ).PointCount(); - - if( pointCount ) - { - aBuffer.reserve( pointCount ); - - for ( auto iter = m_poly.CIterate(); iter; iter++ ) - aBuffer.emplace_back( iter->x, iter->y ); - } - } -} - - -bool PCB_SHAPE::IsPolyShapeValid() const -{ - // return true if the polygonal shape is valid (has more than 2 points) - if( GetPolyShape().OutlineCount() == 0 ) - return false; - - const SHAPE_LINE_CHAIN& outline = ( (SHAPE_POLY_SET&)GetPolyShape() ).Outline( 0 ); - - return outline.PointCount() > 2; -} - - -int PCB_SHAPE::GetPointCount() const -{ - // return the number of corners of the polygonal shape - // this shape is expected to be only one polygon without hole - if( GetPolyShape().OutlineCount() ) - return GetPolyShape().VertexCount( 0 ); - - return 0; -} - - void PCB_SHAPE::SwapData( BOARD_ITEM* aImage ) { PCB_SHAPE* image = dynamic_cast( aImage ); assert( image ); - std::swap( m_width, image->m_width ); - std::swap( m_start, image->m_start ); - std::swap( m_end, image->m_end ); - std::swap( m_thirdPoint, image->m_thirdPoint ); - std::swap( m_shape, image->m_shape ); - std::swap( m_angle, image->m_angle ); - std::swap( m_bezierC1, image->m_bezierC1 ); - std::swap( m_bezierC2, image->m_bezierC2 ); - std::swap( m_bezierPoints, image->m_bezierPoints ); - std::swap( m_poly, image->m_poly ); + SwapShape( image ); + std::swap( m_layer, image->m_layer ); std::swap( m_flags, image->m_flags ); std::swap( m_status, image->m_status ); @@ -1359,24 +234,27 @@ bool PCB_SHAPE::cmp_drawings::operator()( const BOARD_ITEM* aFirst, } -static struct DRAWSEGMENT_DESC +static struct PCB_SHAPE_DESC { - DRAWSEGMENT_DESC() + PCB_SHAPE_DESC() { PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance(); REGISTER_TYPE( PCB_SHAPE ); + propMgr.AddTypeCast( new TYPE_CAST ); + propMgr.AddTypeCast( new TYPE_CAST ); propMgr.InheritsAfter( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_ITEM ) ); + propMgr.InheritsAfter( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ) ); - propMgr.AddProperty( new PROPERTY( _HKI( "Thickness" ), - &PCB_SHAPE::SetWidth, &PCB_SHAPE::GetWidth, PROPERTY_DISPLAY::DISTANCE ) ); + propMgr.AddProperty( new PROPERTY( _HKI( "Thickness" ), + &EDA_SHAPE::SetWidth, &EDA_SHAPE::GetWidth, PROPERTY_DISPLAY::DISTANCE ) ); // TODO show certain properties depending on the shape //propMgr.AddProperty( new PROPERTY( _HKI( "Angle" ), // &PCB_SHAPE::SetAngle, &PCB_SHAPE::GetAngle, PROPERTY_DISPLAY::DECIDEGREE ) ); // TODO or may have different names (arcs) // TODO type? - propMgr.AddProperty( new PROPERTY( _HKI( "End X" ), - &PCB_SHAPE::SetEndX, &PCB_SHAPE::GetEndX, PROPERTY_DISPLAY::DISTANCE ) ); - propMgr.AddProperty( new PROPERTY( _HKI( "End Y" ), - &PCB_SHAPE::SetEndY, &PCB_SHAPE::GetEndY, PROPERTY_DISPLAY::DISTANCE ) ); + propMgr.AddProperty( new PROPERTY( _HKI( "End X" ), + &EDA_SHAPE::SetEndX, &EDA_SHAPE::GetEndX, PROPERTY_DISPLAY::DISTANCE ) ); + propMgr.AddProperty( new PROPERTY( _HKI( "End Y" ), + &EDA_SHAPE::SetEndY, &EDA_SHAPE::GetEndY, PROPERTY_DISPLAY::DISTANCE ) ); } -} _DRAWSEGMENT_DESC; +} _PCB_SHAPE_DESC; diff --git a/pcbnew/pcb_shape.h b/pcbnew/pcb_shape.h index c1139f0947..1134189e89 100644 --- a/pcbnew/pcb_shape.h +++ b/pcbnew/pcb_shape.h @@ -26,11 +26,7 @@ #define PCB_SHAPE_H #include -#include -#include -#include -#include -#include +#include class LINE_READER; @@ -39,10 +35,11 @@ class FOOTPRINT; class MSG_PANEL_ITEM; -class PCB_SHAPE : public BOARD_ITEM +class PCB_SHAPE : public BOARD_ITEM, public EDA_SHAPE { public: - PCB_SHAPE( BOARD_ITEM* aParent = nullptr, KICAD_T idtype = PCB_SHAPE_T ); + PCB_SHAPE( BOARD_ITEM* aParent = NULL, KICAD_T idtype = PCB_SHAPE_T, + SHAPE_T shapetype = SHAPE_T::SEGMENT ); // Do not create a copy constructor & operator=. // The ones generated by the compiler are adequate. @@ -54,6 +51,11 @@ public: return aItem && PCB_SHAPE_T == aItem->Type(); } + wxString GetClass() const override + { + return wxT( "PCB_SHAPE" ); + } + bool IsType( const KICAD_T aScanTypes[] ) const override { if( BOARD_ITEM::IsType( aScanTypes ) ) @@ -70,145 +72,14 @@ public: return false; } - void SetFilled( bool aFlag ) { m_filled = aFlag; } + void SetPosition( const wxPoint& aPos ) override { SetShapePos( aPos ); } + wxPoint GetPosition() const override { return GetShapePos(); } - bool IsFilled() const - { - switch( m_shape ) - { - case SHAPE_T::RECT: - case SHAPE_T::CIRCLE: - case SHAPE_T::POLY: - return m_filled; - - case SHAPE_T::SEGMENT: - case SHAPE_T::ARC: - case SHAPE_T::BEZIER: - return false; - - case SHAPE_T::LAST: // Make CLang compiler happy - return false; - } - - return false; // Make GCC compiler happy - } - - void SetWidth( int aWidth ) { m_width = aWidth; } - int GetWidth() const { return m_width; } + wxPoint GetCenter() const override { return getCenter(); } /** - * Set the angle for arcs, and normalizes it within the range 0 - 360 degrees. - * - * @param aAngle is tenths of degrees, but will soon be degrees. - * @param aUpdateEnd set to true to update also arc end coordinates m_thirdPoint, - * so must be called after setting m_Start and m_End. - */ - virtual void SetAngle( double aAngle, bool aUpdateEnd = true ); - double GetAngle() const { return m_angle; } - - void SetShape( SHAPE_T aShape ) { m_shape = aShape; } - SHAPE_T GetShape() const { return m_shape; } - - void SetBezierC1( const wxPoint& aPoint ) { m_bezierC1 = aPoint; } - const wxPoint& GetBezierC1() const { return m_bezierC1; } - - void SetBezierC2( const wxPoint& aPoint ) { m_bezierC2 = aPoint; } - const wxPoint& GetBezierC2() const { return m_bezierC2; } - - void SetPosition( const wxPoint& aPos ) override; - wxPoint GetPosition() const override; - - /** - * Return the starting point of the graphic. - */ - const wxPoint& GetStart() const { return m_start; } - int GetStartY() { return m_start.y; } - int GetStartX() { return m_start.x; } - void SetStart( const wxPoint& aStart ) { m_start = aStart; } - void SetStartY( int y ) { m_start.y = y; } - void SetStartX( int x ) { m_start.x = x; } - - /** - * Return the ending point of the graphic. - */ - const wxPoint& GetEnd() const { return m_end; } - int GetEndY() { return m_end.y; } - int GetEndX() { return m_end.x; } - void SetEnd( const wxPoint& aEnd ) { m_end = aEnd; } - void SetEndY( int y ) { m_end.y = y; } - void SetEndX( int x ) { m_end.x = x; } - - /** - * Return the third point of the graphic. - */ - const wxPoint& GetThirdPoint() const { return m_thirdPoint; } - int GetThirdPointY() { return m_thirdPoint.y; } - int GetThirdPointX() { return m_thirdPoint.x; } - void SetThirdPoint( const wxPoint& aPoint ) { m_thirdPoint = aPoint; } - void SetThirdPointY( int y ) { m_thirdPoint.y = y; } - void SetThirdPointX( int x ) { m_thirdPoint.x = x; } - - // Some attributes are read only, since they are "calculated" from - // m_Start, m_End, and m_Angle. - // No Set...() function for these attributes. - - wxPoint GetCenter() const override; - wxPoint GetArcStart() const { return m_end; } - wxPoint GetArcEnd() const; - wxPoint GetArcMid() const; - std::vector GetRectCorners() const; - - /** - * @return the angle of the starting point of this arc, between 0 and 3600 in 0.1 deg. - */ - double GetArcAngleStart() const; - - /** - * @return the angle of the ending point of this arc, between 0 and 3600 in 0.1 deg. - */ - double GetArcAngleEnd() const; - - /** - * Return the radius of this item. - * - * Has meaning only for arcs and circles. - */ - int GetRadius() const; - - /** - * Initialize the start arc point. - * - * Can be used for circles to initialize one point of the cicumference. - */ - void SetArcStart( const wxPoint& aArcStartPoint ) - { - m_end = aArcStartPoint; - } - - /** - * Initialize the end arc point. - * - * Can be used for circles to initialize one point of the cicumference. - */ - void SetArcEnd( const wxPoint& aArcEndPoint ) - { - m_thirdPoint = aArcEndPoint; - } - - void SetCenter( const wxPoint& aCenterPoint ) { m_start = aCenterPoint; } - - /** - * Set the three controlling points for an arc. - * - * NB: these are NOT what's currently stored, so we have to do some calculations behind - * the scenes. However, they are what SHOULD be stored. - */ - void SetArcGeometry( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd ); - - /** - * Allows items to return their visual center rather than their anchor. - * For some shapes this is similar to GetPosition, but for polygonal shapes, - * the anchor is not suitable (shows nothing): a point on the outline is better + * Allows items to return their visual center rather than their anchor. For some shapes this + * is similar to GetCenter(), but for unfilled shapes a point on the outline is better. */ const wxPoint GetFocusPosition() const override; @@ -219,77 +90,24 @@ public: */ FOOTPRINT* GetParentFootprint() const; - // Accessors: - const std::vector& GetBezierPoints() const { return m_bezierPoints; } - - /** - * Duplicate the list of corners in a std::vector - * - * It must be used only to convert the SHAPE_POLY_SET internal corner buffer - * to a list of wxPoints, and nothing else, because it duplicates the buffer, - * that is inefficient to know for instance the corner count - */ - void DupPolyPointsList( std::vector& aBuffer ) const; - - /** - * @return the number of corners of the polygonal shape - */ - int GetPointCount() const; - - // Accessors to the polygonal shape - SHAPE_POLY_SET& GetPolyShape() { return m_poly; } - const SHAPE_POLY_SET& GetPolyShape() const { return m_poly; } - - /** - * @return true if the polygonal shape is valid (has more than 2 points) - */ - bool IsPolyShapeValid() const; - - void SetPolyShape( const SHAPE_POLY_SET& aShape ) { m_poly = aShape; } - - void SetBezierPoints( const std::vector& aPoints ) - { - m_bezierPoints = aPoints; - } - - /** - * Rebuild the m_BezierPoints vertex list that approximate the Bezier curve - * by a list of segments. - * - * Has meaning only for S_CURVE DRAW_SEGMENT shape. - * - * @param aMinSegLen is the min length of segments approximating the he. shape last segment - * can be shorter. This parameter avoids having too many very short segment in list. - * A good value is m_Width/2 to m_Width. - */ - void RebuildBezierToSegmentsPointsList( int aMinSegLen ); - - void SetPolyPoints( const std::vector& aPoints ); - /** * Make a set of SHAPE objects representing the PCB_SHAPE. Caller owns the objects. */ - std::vector MakeEffectiveShapes() const; // fixme: move to shape_compound std::shared_ptr GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER ) const override; void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector& aList ) override; - const EDA_RECT GetBoundingBox() const override; + const EDA_RECT GetBoundingBox() const override { return getBoundingBox(); } - bool HitTest( const wxPoint& aPosition, int aAccuracy = 0 ) const override; - bool HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const override; - - wxString GetClass() const override + bool HitTest( const wxPoint& aPosition, int aAccuracy = 0 ) const override { - return wxT( "PCB_SHAPE" ); + return hitTest( aPosition, aAccuracy ); } - /** - * Return the length of the track using the hypotenuse calculation. - * - * @return the length of the track - */ - double GetLength() const; + bool HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const override + { + return hitTest( aRect, aContained, aAccuracy ); + } virtual void Move( const wxPoint& aMoveVector ) override; @@ -300,13 +118,14 @@ public: void Scale( double aScale ); /** - * Convert the draw segment to a closed polygon. + * Convert the shape to a closed polygon. * * Used in filling zones calculations. Circles and arcs are approximated by segments. * * @param aCornerBuffer is a buffer to store the polygon. * @param aClearanceValue is the clearance around the pad. * @param aError is the maximum deviation from a true arc. + * @param aErrorLoc whether any approximation error shoule be placed inside or outside * @param ignoreLineWidth is used for edge cut items where the line width is only * for visualization */ @@ -335,24 +154,8 @@ public: #endif protected: - // Compute the bounding box for an arc - void computeArcBBox( EDA_RECT& aBBox ) const; - - const std::vector buildBezierToSegmentsPointsList( int aMinSegLen ) const; - - int m_width; // thickness of lines ... - bool m_filled; // Pretty much what it says on the tin... - wxPoint m_start; // Line start point or Circle and Arc center - wxPoint m_end; // Line end point or circle and arc start point - wxPoint m_thirdPoint; // Used only for Arcs: arc end point - - SHAPE_T m_shape; // Shape: line, Circle, Arc - double m_angle; // Used only for Arcs: Arc angle in 1/10 deg - wxPoint m_bezierC1; // Bezier Control Point 1 - wxPoint m_bezierC2; // Bezier Control Point 2 - - std::vector m_bezierPoints; - SHAPE_POLY_SET m_poly; // Stores the S_POLYGON shape + double getParentOrientation() const override; + wxPoint getParentPosition() const override; }; #endif // PCB_SHAPE_H diff --git a/pcbnew/plugins/altium/altium_pcb.cpp b/pcbnew/plugins/altium/altium_pcb.cpp index b047e81e2d..b97f0b0ca5 100644 --- a/pcbnew/plugins/altium/altium_pcb.cpp +++ b/pcbnew/plugins/altium/altium_pcb.cpp @@ -868,7 +868,7 @@ void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector& aV wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * cur->radius ), -KiROUND( std::sin( startradiant ) * cur->radius ) ); wxPoint arcStart = cur->center + arcStartOffset; - shape->SetCenter( cur->center ); + shape->SetArcCenter( cur->center ); shape->SetArcStart( arcStart ); if( !last->isRound ) @@ -1927,7 +1927,7 @@ void ALTIUM_PCB::ParseArcs6Data( const CFB::CompoundFileReader& aReader, { PCB_SHAPE shape( nullptr ); // just a helper to get the graphic shape.SetWidth( elem.width ); - shape.SetCenter( elem.center ); + shape.SetArcCenter( elem.center ); if( elem.startangle == 0. && elem.endangle == 360. ) { // TODO: other variants to define circle? @@ -2027,7 +2027,7 @@ void ALTIUM_PCB::ParseArcs6Data( const CFB::CompoundFileReader& aReader, else { PCB_SHAPE* shape = HelperCreateAndAddDrawsegment( elem.component ); - shape->SetCenter( elem.center ); + shape->SetArcCenter( elem.center ); shape->SetWidth( elem.width ); shape->SetLayer( klayer ); @@ -2367,7 +2367,7 @@ void ALTIUM_PCB::HelperParsePad6NonCopper( const APAD6& aElem ) // circle shape->SetShape( SHAPE_T::CIRCLE ); shape->SetFilled( true ); - shape->SetCenter( aElem.position ); + shape->SetArcCenter( aElem.position ); shape->SetWidth( aElem.topsize.x / 2 ); shape->SetArcStart( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) ); } @@ -2400,7 +2400,7 @@ void ALTIUM_PCB::HelperParsePad6NonCopper( const APAD6& aElem ) shape->SetShape( SHAPE_T::CIRCLE ); shape->SetFilled( true ); shape->SetLayer( klayer ); - shape->SetCenter( aElem.position ); + shape->SetArcCenter( aElem.position ); shape->SetWidth( aElem.topsize.x / 2 ); shape->SetArcStart( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) ); HelperDrawsegmentSetLocalCoord( shape, aElem.component ); diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp index c49e767881..7fe93e29f9 100644 --- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp +++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp @@ -2834,7 +2834,7 @@ PCB_SHAPE* CADSTAR_PCB_ARCHIVE_LOADER::getShapeFromVertex( const POINT& aCadstar } shape->SetArcStart( startPoint ); - shape->SetCenter( centerPoint ); + shape->SetArcCenter( centerPoint ); arcStartAngle = getPolarAngle( startPoint - centerPoint ); arcEndAngle = getPolarAngle( endPoint - centerPoint ); diff --git a/pcbnew/plugins/fabmaster/import_fabmaster.cpp b/pcbnew/plugins/fabmaster/import_fabmaster.cpp index d45f6ccac2..9d8534105a 100644 --- a/pcbnew/plugins/fabmaster/import_fabmaster.cpp +++ b/pcbnew/plugins/fabmaster/import_fabmaster.cpp @@ -2120,14 +2120,14 @@ bool FABMASTER::loadFootprints( BOARD* aBoard ) if( src->mirror ) { arc->SetLayer( FlipLayer( layer ) ); - arc->SetCenter( wxPoint( lsrc->center_x, 2 * src->y - lsrc->center_y ) ); + arc->SetArcCenter( wxPoint( lsrc->center_x, 2 * src->y - lsrc->center_y )); arc->SetArcStart( wxPoint( lsrc->end_x, 2 * src->y - lsrc->end_y ) ); arc->SetAngle( lsrc->result.GetCentralAngle() * 10.0 ); } else { arc->SetLayer( layer ); - arc->SetCenter( wxPoint( lsrc->center_x, lsrc->center_y ) ); + arc->SetArcCenter( wxPoint( lsrc->center_x, lsrc->center_y )); arc->SetArcStart( wxPoint( lsrc->end_x, lsrc->end_y ) ); arc->SetAngle( -lsrc->result.GetCentralAngle() * 10.0 ); } @@ -2775,7 +2775,7 @@ bool FABMASTER::loadOutline( BOARD* aBoard, const std::unique_ptrSetShape( SHAPE_T::ARC ); arc->SetLayer( layer ); - arc->SetCenter( wxPoint( src->center_x, src->center_y ) ); + arc->SetArcCenter( wxPoint( src->center_x, src->center_y )); arc->SetArcStart( wxPoint( src->start_x, src->start_y ) ); arc->SetAngle( src->result.GetCentralAngle() * 10.0 ); arc->SetWidth( src->width ); @@ -2896,7 +2896,7 @@ bool FABMASTER::loadGraphics( BOARD* aBoard ) PCB_SHAPE* arc = new PCB_SHAPE( aBoard ); arc->SetShape( SHAPE_T::ARC ); arc->SetLayer( layer ); - arc->SetCenter( wxPoint( src->center_x, src->center_y ) ); + arc->SetArcCenter( wxPoint( src->center_x, src->center_y )); arc->SetArcStart( wxPoint( src->start_x, src->start_y ) ); arc->SetAngle( src->result.GetCentralAngle() * 10.0 ); arc->SetWidth( src->width ); diff --git a/pcbnew/plugins/kicad/kicad_plugin.cpp b/pcbnew/plugins/kicad/kicad_plugin.cpp index 9fc805c468..ad12b36529 100644 --- a/pcbnew/plugins/kicad/kicad_plugin.cpp +++ b/pcbnew/plugins/kicad/kicad_plugin.cpp @@ -906,7 +906,7 @@ void PCB_IO::format( const PCB_SHAPE* aShape, int aNestLevel ) const default: wxFAIL_MSG( "PCB_IO::format cannot format unknown PCB_SHAPE shape:" - + SHAPE_T_asString( aShape->GetShape()) ); + + aShape->SHAPE_T_asString() ); return; }; @@ -1040,7 +1040,7 @@ void PCB_IO::format( const FP_SHAPE* aFPShape, int aNestLevel ) const default: wxFAIL_MSG( "PCB_IO::format cannot format unknown FP_SHAPE shape:" - + SHAPE_T_asString( aFPShape->GetShape()) ); + + aFPShape->SHAPE_T_asString() ); return; }; diff --git a/pcbnew/plugins/kicad/pcb_parser.cpp b/pcbnew/plugins/kicad/pcb_parser.cpp index 0d03b77863..117d0380e5 100644 --- a/pcbnew/plugins/kicad/pcb_parser.cpp +++ b/pcbnew/plugins/kicad/pcb_parser.cpp @@ -2392,7 +2392,7 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE() pt.x = parseBoardUnits( "X coordinate" ); pt.y = parseBoardUnits( "Y coordinate" ); - shape->SetCenter( pt ); + shape->SetArcCenter( pt ); NeedRIGHT(); NeedLEFT(); token = NextTok(); @@ -2426,7 +2426,7 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE() pt.x = parseBoardUnits( "X coordinate" ); pt.y = parseBoardUnits( "Y coordinate" ); - shape->SetCenter( pt ); + shape->SetArcCenter( pt ); NeedRIGHT(); NeedLEFT(); diff --git a/pcbnew/python/swig/board_item.i b/pcbnew/python/swig/board_item.i index c8b23a594c..07f3d91ed6 100644 --- a/pcbnew/python/swig/board_item.i +++ b/pcbnew/python/swig/board_item.i @@ -32,28 +32,8 @@ %include board_item.h // generate code for this interface %include eda_item_flags.h // generate code for this interface -/* Only for compatibility with old python scripts: */ -const int S_SEGMENT = (const int)SHAPE_T::SEGMENT; -const int S_RECT = (const int)SHAPE_T::RECT; -const int S_ARC = (const int)SHAPE_T::ARC; -const int S_CIRCLE = (const int)SHAPE_T::CIRCLE; -const int S_POLYGON = (const int)SHAPE_T::POLY; -const int S_CURVE = (const int)SHAPE_T::BEZIER; - %rename(Get) operator BOARD_ITEM*; -%{ -#include -/* for compatibility with old python scripts: */ -const int S_SEGMENT = (const int)SHAPE_T::SEGMENT; -const int S_RECT = (const int)SHAPE_T::RECT; -const int S_ARC = (const int)SHAPE_T::ARC; -const int S_CIRCLE = (const int)SHAPE_T::CIRCLE; -const int S_POLYGON = (const int)SHAPE_T::POLY; -const int S_CURVE = (const int)SHAPE_T::BEZIER; -%} - - %inline { /// Cast down from EDA_ITEM/BOARD_ITEM to child diff --git a/pcbnew/tools/convert_tool.cpp b/pcbnew/tools/convert_tool.cpp index 31949c7de0..6be3bd0dda 100644 --- a/pcbnew/tools/convert_tool.cpp +++ b/pcbnew/tools/convert_tool.cpp @@ -814,7 +814,7 @@ int CONVERT_TOOL::SegmentToArc( const TOOL_EVENT& aEvent ) arc->SetLayer( layer ); arc->SetWidth( line->GetWidth() ); - arc->SetCenter( wxPoint( center ) ); + arc->SetArcCenter( wxPoint( center )); arc->SetArcStart( wxPoint( start ) ); arc->SetAngle( GetArcAngle( start, mid, end ) ); diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index 71e8c83b94..7f870ca02d 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -1418,7 +1418,7 @@ bool DRAWING_TOOL::drawSegment( const std::string& aTool, PCB_SHAPE** aGraphic, KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER twoPointManager; // drawing assistant overlay - // TODO: workaround because PCB_SHAPE_TYPE_T is not visible from commons. + // TODO: workaround because EDA_SHAPE_TYPE_T is not visible from commons. KIGFX::PREVIEW::GEOM_SHAPE geomShape( static_cast( shape ) ); KIGFX::PREVIEW::TWO_POINT_ASSISTANT twoPointAsst( twoPointManager, userUnits, geomShape ); @@ -1716,7 +1716,7 @@ static void updateArcFromConstructionMgr( const KIGFX::PREVIEW::ARC_GEOM_MANAGER { auto vec = aMgr.GetOrigin(); - aArc.SetCenter( { vec.x, vec.y } ); + aArc.SetArcCenter( { vec.x, vec.y } ); vec = aMgr.GetStartRadiusEnd(); aArc.SetArcStart( { vec.x, vec.y } ); diff --git a/pcbnew/tools/pcb_point_editor.cpp b/pcbnew/tools/pcb_point_editor.cpp index fd0cc59e7e..9e39bb5bbe 100644 --- a/pcbnew/tools/pcb_point_editor.cpp +++ b/pcbnew/tools/pcb_point_editor.cpp @@ -760,7 +760,7 @@ void PCB_POINT_EDITOR::editArcEndpointKeepTangent( PCB_SHAPE* aArc, const VECTOR if( arcValid ) { aArc->SetAngle( newAngle, false ); - aArc->SetCenter( ( wxPoint ) center ); + aArc->SetArcCenter( ( wxPoint ) center ); if( movingStart ) aArc->SetArcStart( ( wxPoint ) start ); @@ -944,7 +944,7 @@ void PCB_POINT_EDITOR::editArcEndpointKeepCenter( PCB_SHAPE* aArc, const VECTOR2 newAngle -= 3600.0; aArc->SetAngle( newAngle, false ); - aArc->SetCenter( (wxPoint) aCenter ); + aArc->SetArcCenter((wxPoint) aCenter ); if( movingStart ) aArc->SetArcStart( (wxPoint) aStart ); diff --git a/pcbnew/tools/pcb_selection_conditions.h b/pcbnew/tools/pcb_selection_conditions.h index 53c5f2f12e..9dd5e8f493 100644 --- a/pcbnew/tools/pcb_selection_conditions.h +++ b/pcbnew/tools/pcb_selection_conditions.h @@ -29,7 +29,7 @@ #include #include -#include +#include class PCB_SELECTION_CONDITIONS : public SELECTION_CONDITIONS