kicad-source/include/eda_shape.h
Jeff Young 0659290417 Simplify hatch damage/rebuild and move it out of view.
There should be far fewer hatched objects than other
objects, so we're spending too much effort finding
all the possible damage when we probably should
just be auto-regenerating all the hatching.

This also moves it out of being done during redraw,
which was proving problematic.  Plus the refill
zones architecture does it during commit, and has
a lot more miles behind it.
2025-04-07 11:46:02 +01:00

522 lines
16 KiB
C++

/*
* 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 The 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
*/
#pragma once
#include <core/mirror.h>
#include <geometry/shape_poly_set.h>
#include <geometry/approximation.h>
#include <properties/property.h>
#include <stroke_params.h>
#include <trigo.h>
#include <api/serializable.h>
class LINE_READER;
class EDA_DRAW_FRAME;
class FOOTPRINT;
class MSG_PANEL_ITEM;
using KIGFX::COLOR4D;
enum class SHAPE_T : int
{
UNDEFINED = -1,
SEGMENT = 0,
RECTANGLE, ///< Use RECTANGLE instead of RECT to avoid collision in a Windows header.
ARC,
CIRCLE,
POLY,
BEZIER
};
// WARNING: Do not change these values without updating dialogs that depend on their position values
enum class FILL_T : int
{
NO_FILL = 1,
FILLED_SHAPE, ///< Fill with object color.
FILLED_WITH_BG_BODYCOLOR, //< Fill with background body color.
FILLED_WITH_COLOR, //< Fill with a separate color.
HATCH,
REVERSE_HATCH,
CROSS_HATCH
};
enum UI_FILL_MODE
{
NONE = 0,
SOLID,
HATCH,
REVERSE_HATCH,
CROSS_HATCH
};
/// Holding struct to keep originating midpoint
struct ARC_MID
{
VECTOR2I mid;
VECTOR2I start;
VECTOR2I end;
VECTOR2I center;
};
class EDA_SHAPE : public SERIALIZABLE
{
public:
EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill );
/// Construct an EDA_SHAPE from an abstract SHAPE geometry.
EDA_SHAPE( const SHAPE& aShape );
// Do not create a copy constructor & operator=.
// The ones generated by the compiler are adequate.
virtual ~EDA_SHAPE();
void SwapShape( EDA_SHAPE* aImage );
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
wxString ShowShape() const;
wxString SHAPE_T_asString() const;
virtual bool IsProxyItem() const { return m_proxyItem; }
virtual void SetIsProxyItem( bool aIsProxy = true ) { m_proxyItem = aIsProxy; }
bool IsAnyFill() const
{
return GetFillMode() != FILL_T::NO_FILL;
}
bool IsSolidFill() const
{
return GetFillMode() == FILL_T::FILLED_SHAPE
|| GetFillMode() == FILL_T::FILLED_WITH_COLOR
|| GetFillMode() == FILL_T::FILLED_WITH_BG_BODYCOLOR;
}
bool IsHatchedFill() const
{
return GetFillMode() == FILL_T::HATCH
|| GetFillMode() == FILL_T::REVERSE_HATCH
|| GetFillMode() == FILL_T::CROSS_HATCH;
}
virtual bool IsFilledForHitTesting() const
{
return IsSolidFill();
}
virtual void SetFilled( bool aFlag )
{
setFilled( aFlag );
}
void SetFillMode( FILL_T aFill );
FILL_T GetFillMode() const { return m_fill; }
void SetFillModeProp( UI_FILL_MODE );
UI_FILL_MODE GetFillModeProp() const;
void SetHatchingDirty() { m_hatchingDirty = true; }
const SHAPE_POLY_SET& GetHatching() const { return m_hatching; }
bool IsClosed() const;
COLOR4D GetFillColor() const { return m_fillColor; }
void SetFillColor( const COLOR4D& aColor ) { m_fillColor = aColor; }
void SetWidth( int aWidth );
virtual int GetWidth() const { return m_stroke.GetWidth(); }
virtual int GetEffectiveWidth() const { return GetWidth(); }
virtual int GetHatchLineWidth() const { return GetEffectiveWidth(); }
virtual int GetHatchLineSpacing() const { return GetHatchLineWidth() * 10; }
void SetLineStyle( const LINE_STYLE aStyle );
LINE_STYLE GetLineStyle() const;
void SetLineColor( const COLOR4D& aColor ) { m_stroke.SetColor( aColor ); }
COLOR4D GetLineColor() const { return m_stroke.GetColor(); }
void SetShape( SHAPE_T aShape ) { m_shape = aShape; }
SHAPE_T GetShape() const { return m_shape; }
/**
* Return the starting point of the graphic.
*/
const VECTOR2I& GetStart() const { return m_start; }
int GetStartY() const { return m_start.y; }
int GetStartX() const { return m_start.x; }
void SetStart( const VECTOR2I& aStart )
{
m_start = aStart;
m_endsSwapped = false;
m_hatchingDirty = true;
}
void SetStartY( int y )
{
m_start.y = y;
m_endsSwapped = false;
m_hatchingDirty = true;
}
void SetStartX( int x )
{
m_start.x = x;
m_endsSwapped = false;
m_hatchingDirty = true;
}
void SetCenterY( int y )
{
m_end.y += y - m_start.y;
m_start.y = y;
m_hatchingDirty = true;
}
void SetCenterX( int x )
{
m_end.x += x - m_start.x;
m_start.x = x;
m_hatchingDirty = true;
}
/**
* Return the ending point of the graphic.
*/
const VECTOR2I& GetEnd() const { return m_end; }
int GetEndY() const { return m_end.y; }
int GetEndX() const { return m_end.x; }
void SetEnd( const VECTOR2I& aEnd )
{
m_end = aEnd;
m_endsSwapped = false;
m_hatchingDirty = true;
}
void SetEndY( int aY )
{
m_end.y = aY;
m_endsSwapped = false;
m_hatchingDirty = true;
}
void SetEndX( int aX )
{
m_end.x = aX;
m_endsSwapped = false;
m_hatchingDirty = true;
}
void SetRadius( int aX )
{
m_end = m_start + VECTOR2I( aX, 0 );
m_hatchingDirty = true;
}
virtual VECTOR2I GetTopLeft() const { return GetStart(); }
virtual VECTOR2I GetBotRight() const { return GetEnd(); }
virtual void SetTop( int val ) { SetStartY( val ); }
virtual void SetLeft( int val ) { SetStartX( val ); }
virtual void SetRight( int val ) { SetEndX( val ); }
virtual void SetBottom( int val ) { SetEndY( val ); }
void SetBezierC1( const VECTOR2I& aPt ) { m_bezierC1 = aPt; }
const VECTOR2I& GetBezierC1() const { return m_bezierC1; }
void SetBezierC2( const VECTOR2I& aPt ) { m_bezierC2 = aPt; }
const VECTOR2I& GetBezierC2() const { return m_bezierC2; }
VECTOR2I getCenter() const;
void SetCenter( const VECTOR2I& aCenter );
/**
* Set the end point from the angle center and start.
*
* aAngle is:
* - clockwise in right-down coordinate system
* - counter-clockwise in right-up (libedit) coordinate system.
*/
void SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeAngle = false );
EDA_ANGLE GetArcAngle() const;
EDA_ANGLE GetSegmentAngle() const;
/**
* Have the start and end points been swapped since they were set?
*
* @return true if they have.
*/
bool EndsSwapped() const { return m_endsSwapped; }
// Some attributes are read only, since they are derived from m_Start, m_End, and m_Angle.
// No Set...() function for these attributes.
VECTOR2I GetArcMid() const;
std::vector<VECTOR2I> GetRectCorners() const;
std::vector<VECTOR2I> GetCornersInSequence() const;
/**
* Calc arc start and end angles such that aStartAngle < aEndAngle. Each may be between
* -360.0 and 360.0.
*/
void CalcArcAngles( EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle ) const;
int GetRadius() const;
/**
* 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 VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd );
/**
* Set the data used for mid point caching.
*
* If the controlling points remain constant, then we keep the midpoint the same as it was
* when read in. This minimizes VCS churn.
*
* @param aStart Cached start point.
* @param aMid Cached mid point.
* @param aEnd Cached end point.
* @param aCenter Calculated center point using the preceeding three.
*/
void SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd,
const VECTOR2I& aCenter );
const std::vector<VECTOR2I>& GetBezierPoints() const { return m_bezierPoints; }
/**
* Duplicate the list of corners in a std::vector<VECTOR2I>.
*
* It must be used only to convert the SHAPE_POLY_SET internal corner buffer
* to a list of VECTOR2Is, and nothing else, because it duplicates the buffer,
* that is inefficient to know for instance the corner count.
*/
void DupPolyPointsList( std::vector<VECTOR2I>& 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;
for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
{
if( m_poly.HoleCount( ii ) )
{
m_poly.Fracture();
break;
}
}
}
void SetPolyPoints( const std::vector<VECTOR2I>& 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 max deviation between the polyline and the curve.
*/
void RebuildBezierToSegmentsPointsList( int aMaxError );
/**
* Make a set of SHAPE objects representing the #EDA_SHAPE.
*
* Caller owns the objects.
*
* @param aEdgeOnly indicates only edges should be generated (even if 0 width), and no fill
* shapes.
*/
virtual std::vector<SHAPE*> MakeEffectiveShapes( bool aEdgeOnly = false ) const
{
return makeEffectiveShapes( aEdgeOnly );
}
void ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList );
void SetLength( const double& aLength );
void SetRectangleHeight( const int& aHeight );
void SetRectangleWidth( const int& aWidth );
void SetRectangle( const long long int& aHeight, const long long int& aWidth );
void SetSegmentAngle( const EDA_ANGLE& aAngle );
bool IsClockwiseArc() const;
/**
* @return the length of the segment using the hypotenuse calculation.
*/
double GetLength() const;
int GetRectangleHeight() const;
int GetRectangleWidth() const;
virtual void UpdateHatching() const;
/**
* Convert the shape to a closed polygon.
*
* Circles and arcs are approximated by segments.
*
* @param aBuffer is a buffer to store the polygon.
* @param aClearance is the clearance around the pad.
* @param aError is the maximum deviation from a true arc.
* @param aErrorLoc whether any approximation error should be placed inside or outside
* @param ignoreLineWidth is used for edge cut items where the line width is only for
* visualization
*/
void TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
ERROR_LOC aErrorLoc, bool ignoreLineWidth = false,
bool includeFill = false ) const;
int Compare( const EDA_SHAPE* aOther ) const;
double Similarity( const EDA_SHAPE& aOther ) const;
bool operator==( const EDA_SHAPE& aOther ) const;
protected:
wxString getFriendlyName() const;
void setPosition( const VECTOR2I& aPos );
VECTOR2I getPosition() const;
virtual void setFilled( bool aFlag )
{
m_fill = aFlag ? FILL_T::FILLED_SHAPE : FILL_T::NO_FILL;
}
void move( const VECTOR2I& aMoveVector );
void rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle );
void flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection );
void scale( double aScale );
virtual EDA_ANGLE getDrawRotation() const { return ANGLE_0; }
const BOX2I getBoundingBox() const;
void computeArcBBox( BOX2I& aBBox ) const;
bool hitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const;
bool hitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const;
const std::vector<VECTOR2I> buildBezierToSegmentsPointsList( int aMaxError ) const;
void beginEdit( const VECTOR2I& aStartPoint );
bool continueEdit( const VECTOR2I& aPosition );
void calcEdit( const VECTOR2I& aPosition );
/**
* Finish editing the shape.
*
* @param aClosed Should polygon shapes be closed (yes for pcbnew/fpeditor, no for libedit).
*/
void endEdit( bool aClosed = true );
void setEditState( int aState ) { m_editState = aState; }
virtual bool isMoving() const { return false; }
/**
* Make a set of #SHAPE objects representing the #EDA_SHAPE.
*
* Caller owns the objects.
*
* @param aEdgeOnly indicates only edges should be generated (even if 0 width), and no fill
* shapes.
* @param aLineChainOnly indicates #SHAPE_POLY_SET is being abused slightly to represent a
* lineChain rather than a closed polygon.
*/
// fixme: move to shape_compound
std::vector<SHAPE*> makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly = false ) const;
protected:
bool m_endsSwapped; // true if start/end were swapped e.g. SetArcAngleAndEnd
SHAPE_T m_shape; // Shape: line, Circle, Arc
STROKE_PARAMS m_stroke; // Line style, width, etc.
FILL_T m_fill;
COLOR4D m_fillColor;
mutable SHAPE_POLY_SET m_hatching;
mutable bool m_hatchingDirty;
long long int m_rectangleHeight;
long long int m_rectangleWidth;
double m_segmentLength;
EDA_ANGLE m_segmentAngle;
VECTOR2I m_start; // Line start point or Circle center
VECTOR2I m_end; // Line end point or Circle 3 o'clock point
VECTOR2I m_arcCenter; // Used only for Arcs: arc end point
ARC_MID m_arcMidData; // Used to store originating data
VECTOR2I m_bezierC1; // Bezier Control Point 1
VECTOR2I m_bezierC2; // Bezier Control Point 2
std::vector<VECTOR2I> m_bezierPoints;
SHAPE_POLY_SET m_poly; // Stores the S_POLYGON shape
int m_editState;
bool m_proxyItem; // A shape storing proxy information (ie: a pad
// number box, thermal spoke template, etc.)
};
#ifndef SWIG
DECLARE_ENUM_TO_WXANY( SHAPE_T );
DECLARE_ENUM_TO_WXANY( LINE_STYLE );
DECLARE_ENUM_TO_WXANY( UI_FILL_MODE );
#endif