You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

331 lines
8.7 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014-2019 CERN
  5. * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Maciej Suminski <maciej.suminski@cern.ch>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <gal/graphics_abstraction_layer.h>
  26. #include <gal/color4d.h>
  27. #include <gal/painter.h>
  28. #include <math/util.h> // for KiROUND
  29. #include "tool/edit_points.h"
  30. bool EDIT_POINT::WithinPoint( const VECTOR2I& aPoint, unsigned int aSize ) const
  31. {
  32. // Corners of the EDIT_POINT square
  33. VECTOR2I topLeft = GetPosition() - aSize;
  34. VECTOR2I bottomRight = GetPosition() + aSize;
  35. return ( aPoint.x > topLeft.x && aPoint.y > topLeft.y &&
  36. aPoint.x < bottomRight.x && aPoint.y < bottomRight.y );
  37. }
  38. EDIT_POINTS::EDIT_POINTS( EDA_ITEM* aParent ) :
  39. EDA_ITEM( NOT_USED ),
  40. m_parent( aParent ),
  41. m_swapX( false ),
  42. m_swapY( false ),
  43. m_allowPoints( true )
  44. {
  45. }
  46. EDIT_POINT* EDIT_POINTS::FindPoint( const VECTOR2I& aLocation, KIGFX::VIEW *aView ) // fixme: ugly
  47. {
  48. unsigned size = std::abs( KiROUND( aView->ToWorld( EDIT_POINT::POINT_SIZE ) ) );
  49. if( m_allowPoints )
  50. {
  51. for( EDIT_POINT& point : m_points )
  52. {
  53. if( point.WithinPoint( aLocation, size ) )
  54. return &point;
  55. }
  56. }
  57. for( EDIT_LINE& line : m_lines )
  58. {
  59. if( line.WithinPoint( aLocation, size ) )
  60. return &line;
  61. }
  62. return nullptr;
  63. }
  64. int EDIT_POINTS::GetContourStartIdx( int aPointIdx ) const
  65. {
  66. int lastIdx = 0;
  67. for( int idx : m_contours )
  68. {
  69. if( idx >= aPointIdx )
  70. return lastIdx;
  71. lastIdx = idx + 1;
  72. }
  73. return lastIdx;
  74. }
  75. int EDIT_POINTS::GetContourEndIdx( int aPointIdx ) const
  76. {
  77. for( int idx : m_contours )
  78. {
  79. if( idx >= aPointIdx )
  80. return idx;
  81. }
  82. return m_points.size() - 1;
  83. }
  84. bool EDIT_POINTS::IsContourStart( int aPointIdx ) const
  85. {
  86. for( int idx : m_contours )
  87. {
  88. if( idx + 1 == aPointIdx )
  89. return true;
  90. // the list is sorted, so we cannot expect it any further
  91. if( idx > aPointIdx )
  92. break;
  93. }
  94. return ( aPointIdx == 0 );
  95. }
  96. bool EDIT_POINTS::IsContourEnd( int aPointIdx ) const
  97. {
  98. for( int idx : m_contours )
  99. {
  100. if( idx == aPointIdx )
  101. return true;
  102. // the list is sorted, so we cannot expect it any further
  103. if( idx > aPointIdx )
  104. break;
  105. }
  106. // the end of the list surely is the end of a contour
  107. return ( aPointIdx == (int) m_points.size() - 1 );
  108. }
  109. EDIT_POINT* EDIT_POINTS::Previous( const EDIT_POINT& aPoint, bool aTraverseContours )
  110. {
  111. for( unsigned int i = 0; i < m_points.size(); ++i )
  112. {
  113. if( m_points[i] == aPoint )
  114. {
  115. if( !aTraverseContours && IsContourStart( i ) )
  116. return &m_points[GetContourEndIdx( i )];
  117. if( i == 0 )
  118. return &m_points[m_points.size() - 1];
  119. else
  120. return &m_points[i - 1];
  121. }
  122. }
  123. return nullptr;
  124. }
  125. EDIT_LINE* EDIT_POINTS::Previous( const EDIT_LINE& aLine )
  126. {
  127. for( unsigned int i = 0; i < m_lines.size(); ++i )
  128. {
  129. if( m_lines[i] == aLine )
  130. {
  131. if( i == 0 )
  132. return &m_lines[m_lines.size() - 1];
  133. else
  134. return &m_lines[i - 1];
  135. }
  136. }
  137. return nullptr;
  138. }
  139. EDIT_POINT* EDIT_POINTS::Next( const EDIT_POINT& aPoint, bool aTraverseContours )
  140. {
  141. for( unsigned int i = 0; i < m_points.size(); ++i )
  142. {
  143. if( m_points[i] == aPoint )
  144. {
  145. if( !aTraverseContours && IsContourEnd( i ) )
  146. return &m_points[GetContourStartIdx( i )];
  147. if( i == m_points.size() - 1 )
  148. return &m_points[0];
  149. else
  150. return &m_points[i + 1];
  151. }
  152. }
  153. return nullptr;
  154. }
  155. EDIT_LINE* EDIT_POINTS::Next( const EDIT_LINE& aLine )
  156. {
  157. for( unsigned int i = 0; i < m_lines.size(); ++i )
  158. {
  159. if( m_lines[i] == aLine )
  160. {
  161. if( i == m_lines.size() - 1 )
  162. return &m_lines[0];
  163. else
  164. return &m_lines[i + 1];
  165. }
  166. }
  167. return nullptr;
  168. }
  169. const BOX2I EDIT_POINTS::ViewBBox() const
  170. {
  171. BOX2I box;
  172. bool empty = true;
  173. for( const auto& point : m_points )
  174. {
  175. if( empty )
  176. {
  177. box.SetOrigin( point.GetPosition() );
  178. empty = false;
  179. }
  180. else
  181. {
  182. box.Merge( point.GetPosition() );
  183. }
  184. }
  185. for( const auto& line : m_lines )
  186. {
  187. if( empty )
  188. {
  189. box.SetOrigin( line.GetOrigin().GetPosition() );
  190. box.SetEnd( line.GetEnd().GetPosition() );
  191. empty = false;
  192. }
  193. else
  194. {
  195. box.Merge( line.GetOrigin().GetPosition() );
  196. box.Merge( line.GetEnd().GetPosition() );
  197. }
  198. }
  199. return box;
  200. }
  201. void EDIT_POINTS::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
  202. {
  203. KIGFX::GAL* gal = aView->GetGAL();
  204. KIGFX::RENDER_SETTINGS* settings = aView->GetPainter()->GetSettings();
  205. KIGFX::COLOR4D drawColor = settings->GetLayerColor( LAYER_AUX_ITEMS );
  206. // Don't assume LAYER_AUX_ITEMS is always a good choice. Compare with background.
  207. if( aView->GetGAL()->GetClearColor().Distance( drawColor ) < 0.5 )
  208. drawColor.Invert();
  209. // Linear darkening doesn't fit well with human color perception, and there's no guarantee
  210. // that there's enough room for contrast either.
  211. KIGFX::COLOR4D borderColor;
  212. KIGFX::COLOR4D highlightColor;
  213. double brightness = drawColor.GetBrightness();
  214. if( brightness > 0.5 )
  215. {
  216. borderColor = drawColor.Darkened( 0.7 ).WithAlpha( 0.8 );
  217. highlightColor = drawColor.Darkened( 0.5 ).WithAlpha( 0.8 );
  218. }
  219. else if( brightness > 0.2 )
  220. {
  221. borderColor = drawColor.Brightened( 0.4 ).WithAlpha( 0.8 );
  222. highlightColor = drawColor.Brightened( 0.3 ).WithAlpha( 0.8 );
  223. }
  224. else
  225. {
  226. borderColor = drawColor.Brightened( 0.7 ).WithAlpha( 0.8 );
  227. highlightColor = drawColor.Brightened( 0.5 ).WithAlpha( 0.8 );
  228. }
  229. KIGFX::GAL_SCOPED_ATTRS scopedAttrs( *gal, KIGFX::GAL_SCOPED_ATTRS::ALL_ATTRS );
  230. gal->SetFillColor( drawColor );
  231. gal->SetStrokeColor( borderColor );
  232. gal->SetIsFill( true );
  233. gal->SetIsStroke( true );
  234. gal->SetLayerDepth( gal->GetMinDepth() );
  235. double size = aView->ToWorld( EDIT_POINT::POINT_SIZE ) / 2.0;
  236. double borderSize = aView->ToWorld( EDIT_POINT::BORDER_SIZE );
  237. double hoverSize = aView->ToWorld( EDIT_POINT::HOVER_SIZE );
  238. auto drawPoint =
  239. [&]( const EDIT_POINT& aPoint, bool aDrawCircle = false )
  240. {
  241. if( aPoint.IsHover() || aPoint.IsActive() )
  242. {
  243. gal->SetStrokeColor( highlightColor );
  244. gal->SetLineWidth( hoverSize );
  245. }
  246. else
  247. {
  248. gal->SetStrokeColor( borderColor );
  249. gal->SetLineWidth( borderSize );
  250. }
  251. gal->SetFillColor( drawColor );
  252. if( aDrawCircle )
  253. gal->DrawCircle( aPoint.GetPosition(), size );
  254. else
  255. gal->DrawRectangle( aPoint.GetPosition() - size, aPoint.GetPosition() + size );
  256. };
  257. for( const EDIT_POINT& point : m_points )
  258. drawPoint( point );
  259. for( const EDIT_LINE& line : m_lines )
  260. {
  261. if( line.HasCenterPoint() )
  262. {
  263. drawPoint( line.GetPosition(), true );
  264. }
  265. if( line.DrawLine() )
  266. {
  267. gal->SetLineWidth( borderSize );
  268. gal->SetStrokeColor( borderColor );
  269. gal->DrawLine( line.GetOrigin().GetPosition(), line.GetEnd().GetPosition() );
  270. }
  271. }
  272. }