KiCad PCB EDA Suite
Loading...
Searching...
No Matches
construction_geom.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <layer_ids.h>
23#include <utility>
25#include <geometry/line.h>
28#include <view/view.h>
29
30using namespace KIGFX;
31
32
34 EDA_ITEM( nullptr, NOT_USED ), // Never added to a BOARD/SCHEMATIC so it needs no type
36{
37}
38
39
40std::array<SEG, 3> CONSTRUCTION_GEOM::DimensionBracketSegments( const SEG& aSpan, int aTickLength, int aOffset )
41{
42 const int halfTick = aTickLength / 2;
43 VECTOR2I tickOffset;
44 VECTOR2I bracketOffset;
45
46 if( std::abs( aSpan.B.x - aSpan.A.x ) >= std::abs( aSpan.B.y - aSpan.A.y ) )
47 {
48 tickOffset.y = halfTick;
49 bracketOffset.y = aOffset;
50 }
51 else
52 {
53 tickOffset.x = halfTick;
54 bracketOffset.x = aOffset;
55 }
56
57 SEG bracket( aSpan.A + bracketOffset, aSpan.B + bracketOffset );
58 return { bracket, SEG( bracket.A - tickOffset, bracket.A + tickOffset ),
59 SEG( bracket.B - tickOffset, bracket.B + tickOffset ) };
60}
61
62
63void CONSTRUCTION_GEOM::AddDrawable( const DRAWABLE& aItem, bool aPersistent, int aLineWidth )
64{
65 m_drawables.push_back( { aItem, aPersistent, aLineWidth } );
66}
67
68
69void CONSTRUCTION_GEOM::SetSnapGuides( std::vector<SNAP_GUIDE> aGuides )
70{
71 m_snapGuides = std::move( aGuides );
72}
73
74
75void CONSTRUCTION_GEOM::SetDimensionBrackets( std::vector<SEG> aBrackets )
76{
77 m_dimensionBrackets = std::move( aBrackets );
78}
79
80
85
86
88{
89 // We could be a bit more circumspect here, but much of the time the
90 // extended lines go across the whole screen anyway
91 BOX2I bbox;
92 bbox.SetMaximum();
93 return bbox;
94}
95
96
97void CONSTRUCTION_GEOM::ViewDraw( int aLayer, VIEW* aView ) const
98{
99 GAL& gal = *aView->GetGAL();
100
101 gal.SetIsFill( false );
102 gal.SetIsStroke( true );
103 gal.SetLineWidth( 1 );
104
105 const BOX2I viewport = BOX2ISafe( aView->GetViewport() );
106
107 // Prevents extremely short snap lines from inhibiting drawing
108 // These can happen due to rounding in intersections, etc.
109 // (usually it is length 1 IU)
110 const int minSnapLineLength = 10;
111 const bool haveSnapLine = m_snapLine && m_snapLine->Length() >= minSnapLineLength;
112
113 // Avoid fighting with the snap line
114 const auto drawLineIfNotAlsoSnapLine =
115 [&]( const SEG& aLine )
116 {
117 if( !haveSnapLine || !aLine.ApproxCollinear( *m_snapLine, 1 ) )
118 {
119 gal.DrawLine( aLine.A, aLine.B );
120 }
121 };
122
123 // Draw all the items
124 for( const DRAWABLE_INFO& drawable : m_drawables )
125 {
126 gal.SetStrokeColor( drawable.IsPersistent ? m_persistentColor : m_color );
127 gal.SetLineWidth( drawable.LineWidth / gal.GetWorldScale() );
128
129 std::visit(
130 [&]( const auto& visited )
131 {
132 using ItemType = std::decay_t<decltype( visited )>;
133
134 if constexpr( std::is_same_v<ItemType, LINE> )
135 {
136 // Extend the segment to the viewport boundary
137 std::optional<SEG> segToBoundary =
138 KIGEOM::ClipLineToBox( visited, viewport );
139
140 if( segToBoundary )
141 {
142 drawLineIfNotAlsoSnapLine( *segToBoundary );
143 }
144 }
145 else if constexpr( std::is_same_v<ItemType, HALF_LINE> )
146 {
147 // Extend the ray to the viewport boundary
148 std::optional<SEG> segToBoundary =
149 KIGEOM::ClipHalfLineToBox( visited, viewport );
150
151 if( segToBoundary )
152 {
153 drawLineIfNotAlsoSnapLine( *segToBoundary );
154 }
155 }
156 else if constexpr( std::is_same_v<ItemType, SEG> )
157 {
158 drawLineIfNotAlsoSnapLine( visited );
159 }
160 else if constexpr( std::is_same_v<ItemType, CIRCLE> )
161 {
162 gal.DrawCircle( visited.Center, visited.Radius );
163 }
164 else if constexpr( std::is_same_v<ItemType, SHAPE_ARC> )
165 {
166 gal.DrawArc( visited.GetCenter(), visited.GetRadius(),
167 visited.GetStartAngle(), visited.GetCentralAngle() );
168 }
169 else if constexpr( std::is_same_v<ItemType, VECTOR2I> )
170 {
171 KIGFX::DrawCross( gal, visited, aView->ToWorld( 16 ) );
172 }
173 },
174 drawable.Item );
175 }
176
177 for( const SNAP_GUIDE& guide : m_snapGuides )
178 {
179 const SEG& segment = guide.Segment;
180 const int dashSize = aView->ToWorld( 8 );
181
182 if( segment.A == segment.B )
183 continue;
184
185 std::optional<SEG> clipped = KIGEOM::ClipLineToBox( LINE( segment ), viewport );
186
187 if( !clipped )
188 continue;
189
190 gal.SetStrokeColor( guide.Color );
191 gal.SetLineWidth( guide.LineWidth );
192 KIGFX::DrawDashedLine( gal, *clipped, dashSize );
193 }
194
196 gal.SetLineWidth( 2.0 / gal.GetWorldScale() );
197
198 const int tickLength = aView->ToWorld( 10 );
199 const int edgeOffset = aView->ToWorld( 8 );
200
201 for( const SEG& bracket : m_dimensionBrackets )
202 {
203 if( bracket.A == bracket.B )
204 continue;
205
206 for( const SEG& segment : DimensionBracketSegments( bracket, tickLength, edgeOffset ) )
207 gal.DrawLine( segment.A, segment.B );
208 }
209
210 if( haveSnapLine )
211 {
213 gal.SetLineWidth( 2 );
214
215 const int dashSizeBasis = aView->ToWorld( 12 );
216 const int snapOriginMarkerSize = aView->ToWorld( 16 );
217
218 // Avoid clash with the snap marker if very close
219 const int omitStartMarkerIfWithinLength = aView->ToWorld( 8 );
220
221 // The line itself
222 KIGFX::DrawDashedLine( gal, *m_snapLine, dashSizeBasis );
223
224 // The line origin marker if the line is long enough
225 if( m_snapLine->A.Distance( m_snapLine->B ) > omitStartMarkerIfWithinLength )
226 {
227 KIGFX::DrawCross( gal, m_snapLine->A, snapOriginMarkerSize );
228 gal.DrawCircle( m_snapLine->A, snapOriginMarkerSize / 2 );
229 }
230 }
231}
232
233
234std::vector<int> CONSTRUCTION_GEOM::ViewGetLayers() const
235{
236 // Don't use LAYER_GP_OVERLAY, we need the construction geometry to be visible
237 // on top of the axis cross
238 std::vector<int> layers{ LAYER_UI_START };
239 return layers;
240}
constexpr BOX2I BOX2ISafe(const BOX2D &aInput)
Definition box2.h:925
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr void SetMaximum()
Definition box2.h:76
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:37
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
void AddDrawable(const DRAWABLE &aItem, bool aIsPersistent, int aLineWidth=1)
std::optional< SEG > m_snapLine
std::vector< SNAP_GUIDE > m_snapGuides
const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
std::vector< SEG > m_dimensionBrackets
void SetSnapGuides(std::vector< SNAP_GUIDE > aGuides)
std::vector< DRAWABLE_INFO > m_drawables
std::variant< SEG, LINE, HALF_LINE, CIRCLE, SHAPE_ARC, VECTOR2I > DRAWABLE
std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
void SetDimensionBrackets(std::vector< SEG > aBrackets)
void ViewDraw(int aLayer, VIEW *aView) const override
Draw the parts of the object belonging to layer aLayer.
static std::array< SEG, 3 > DimensionBracketSegments(const SEG &aSpan, int aTickLength, int aOffset=0)
Abstract interface for drawing on a 2D-surface.
virtual void SetIsFill(bool aIsFillEnabled)
Enable/disable fill.
virtual void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius)
Draw a circle using world coordinates.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
virtual void SetStrokeColor(const COLOR4D &aColor)
Set the stroke color.
virtual void SetIsStroke(bool aIsStrokeEnabled)
Enable/disable stroked outlines.
virtual void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a line.
virtual void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle)
Draw an arc.
double GetWorldScale() const
Get the world scale.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition view.cpp:597
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:207
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Converts a screen space point/vector to a point/vector in world space coordinates.
Definition view.cpp:534
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
@ WHITE
Definition color4d.h:44
Utility functions for drawing compound items (i.e.
@ LAYER_UI_START
Definition layer_ids.h:363
std::optional< SEG > ClipHalfLineToBox(const HALF_LINE &aRay, const BOX2I &aBox)
Get the segment of a half-line that is inside a box, if any.
std::optional< SEG > ClipLineToBox(const LINE &aLine, const BOX2I &aBox)
Get the segment of a line that is inside a box, if any.
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:29
void DrawCross(GAL &aGal, const VECTOR2I &aPosition, int aSize)
Draw a cross at a given position.
void DrawDashedLine(GAL &aGal, const SEG &aSeg, double aDashSize)
Draw a dashed line.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
Utility functions for working with shapes.
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:72
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683