2017-09-17 22:44:30 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2017 Jon Evans <jon@craftyjon.com>
|
2023-02-10 09:04:52 +00:00
|
|
|
* Copyright (C) 2017-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
2017-09-17 22:44:30 +00:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
* option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <gerbview_painter.h>
|
|
|
|
#include <gal/graphics_abstraction_layer.h>
|
2021-09-07 20:34:10 +00:00
|
|
|
#include <pgm_base.h>
|
|
|
|
#include <settings/settings_manager.h>
|
2020-01-13 01:44:19 +00:00
|
|
|
#include <settings/color_settings.h>
|
2021-09-07 20:34:10 +00:00
|
|
|
#include <gerbview_settings.h>
|
2017-09-17 22:44:30 +00:00
|
|
|
#include <convert_basic_shapes_to_polygon.h>
|
|
|
|
#include <gerbview.h>
|
2020-10-14 03:37:48 +00:00
|
|
|
#include <trigo.h>
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2019-03-23 18:26:44 +00:00
|
|
|
#include <dcode.h>
|
2018-01-29 12:26:58 +00:00
|
|
|
#include <gerber_draw_item.h>
|
|
|
|
#include <gerber_file_image.h>
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
using namespace KIGFX;
|
|
|
|
|
2021-09-07 20:34:10 +00:00
|
|
|
|
|
|
|
GERBVIEW_SETTINGS* gvconfig()
|
|
|
|
{
|
|
|
|
return Pgm().GetSettingsManager().GetAppSettings<GERBVIEW_SETTINGS>();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-17 22:44:30 +00:00
|
|
|
GERBVIEW_RENDER_SETTINGS::GERBVIEW_RENDER_SETTINGS()
|
|
|
|
{
|
2018-07-07 08:50:13 +00:00
|
|
|
m_backgroundColor = COLOR4D::BLACK;
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
m_componentHighlightString = "";
|
|
|
|
m_netHighlightString = "";
|
|
|
|
m_attributeHighlightString = "";
|
2021-03-13 17:45:33 +00:00
|
|
|
m_dcodeHighlightValue = -1;
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-01-13 01:44:19 +00:00
|
|
|
void GERBVIEW_RENDER_SETTINGS::LoadColors( const COLOR_SETTINGS* aSettings )
|
2017-09-17 22:44:30 +00:00
|
|
|
{
|
2020-07-08 16:20:31 +00:00
|
|
|
// Layers to draw gerber data read from gerber files:
|
2017-09-17 22:44:30 +00:00
|
|
|
for( int i = GERBVIEW_LAYER_ID_START;
|
|
|
|
i < GERBVIEW_LAYER_ID_START + GERBER_DRAWLAYERS_COUNT; i++ )
|
|
|
|
{
|
2022-11-17 15:42:12 +00:00
|
|
|
COLOR4D baseColor = aSettings->GetColor( i );
|
|
|
|
|
|
|
|
if( gvconfig()->m_Display.m_DiffMode )
|
|
|
|
baseColor.a = 0.75;
|
|
|
|
|
|
|
|
m_layerColors[i] = baseColor;
|
|
|
|
m_layerColorsHi[i] = baseColor.Brightened( 0.5 );
|
|
|
|
m_layerColorsSel[i] = baseColor.Brightened( 0.8 );
|
|
|
|
m_layerColorsDark[i] = baseColor.Darkened( 0.25 );
|
2017-09-17 22:44:30 +00:00
|
|
|
}
|
|
|
|
|
2020-07-08 16:20:31 +00:00
|
|
|
// Draw layers specific to Gerbview:
|
2022-03-24 14:10:47 +00:00
|
|
|
// LAYER_DCODES, LAYER_NEGATIVE_OBJECTS, LAYER_GERBVIEW_GRID, LAYER_GERBVIEW_AXES,
|
|
|
|
// LAYER_GERBVIEW_BACKGROUND, LAYER_GERBVIEW_DRAWINGSHEET, LAYER_GERBVIEW_PAGE_LIMITS
|
2017-09-17 22:44:30 +00:00
|
|
|
for( int i = LAYER_DCODES; i < GERBVIEW_LAYER_ID_END; i++ )
|
2020-01-13 01:44:19 +00:00
|
|
|
m_layerColors[i] = aSettings->GetColor( i );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
for( int i = GAL_LAYER_ID_START; i < GAL_LAYER_ID_END; i++ )
|
2020-01-13 01:44:19 +00:00
|
|
|
m_layerColors[i] = aSettings->GetColor( i );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2022-09-08 18:34:32 +00:00
|
|
|
// Ensure the generic LAYER_DRAWINGSHEET has the same color as the specialized
|
|
|
|
// LAYER_GERBVIEW_DRAWINGSHEET
|
|
|
|
m_layerColors[LAYER_DRAWINGSHEET] = m_layerColors[ LAYER_GERBVIEW_DRAWINGSHEET ];
|
|
|
|
|
2017-09-17 22:44:30 +00:00
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-07-30 13:52:28 +00:00
|
|
|
void GERBVIEW_RENDER_SETTINGS::ClearHighlightSelections()
|
|
|
|
{
|
|
|
|
// Clear all highlight selections (dcode, net, component, attribute selection)
|
|
|
|
m_componentHighlightString.Empty();
|
|
|
|
m_netHighlightString.Empty();
|
|
|
|
m_attributeHighlightString.Empty();
|
|
|
|
m_dcodeHighlightValue = -1;
|
|
|
|
}
|
|
|
|
|
2020-07-11 17:42:00 +00:00
|
|
|
COLOR4D GERBVIEW_RENDER_SETTINGS::GetColor( const VIEW_ITEM* aItem, int aLayer ) const
|
2017-09-17 22:44:30 +00:00
|
|
|
{
|
2020-08-15 21:06:10 +00:00
|
|
|
const EDA_ITEM* item = dynamic_cast<const EDA_ITEM*>( aItem );
|
2018-02-04 20:38:53 +00:00
|
|
|
static const COLOR4D transparent = COLOR4D( 0, 0, 0, 0 );
|
2018-09-18 13:18:55 +00:00
|
|
|
const GERBER_DRAW_ITEM* gbrItem = nullptr;
|
|
|
|
|
|
|
|
if( item && item->Type() == GERBER_DRAW_ITEM_T )
|
|
|
|
gbrItem = static_cast<const GERBER_DRAW_ITEM*>( item );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
// All DCODE layers stored under a single color setting
|
|
|
|
if( IsDCodeLayer( aLayer ) )
|
|
|
|
return m_layerColors[ LAYER_DCODES ];
|
|
|
|
|
2018-09-18 13:18:55 +00:00
|
|
|
if( item && item->IsSelected() )
|
|
|
|
return m_layerColorsSel[aLayer];
|
|
|
|
|
|
|
|
if( gbrItem && gbrItem->GetLayerPolarity() )
|
2017-09-17 22:44:30 +00:00
|
|
|
{
|
2021-09-07 20:34:10 +00:00
|
|
|
if( gvconfig()->m_Appearance.show_negative_objects )
|
2018-09-18 13:18:55 +00:00
|
|
|
return m_layerColors[LAYER_NEGATIVE_OBJECTS];
|
|
|
|
else
|
|
|
|
return transparent;
|
2017-09-17 22:44:30 +00:00
|
|
|
}
|
|
|
|
|
2018-09-18 13:18:55 +00:00
|
|
|
if( !m_netHighlightString.IsEmpty() && gbrItem &&
|
|
|
|
m_netHighlightString == gbrItem->GetNetAttributes().m_Netname )
|
2017-09-17 22:44:30 +00:00
|
|
|
return m_layerColorsHi[aLayer];
|
|
|
|
|
2018-09-18 13:18:55 +00:00
|
|
|
if( !m_componentHighlightString.IsEmpty() && gbrItem &&
|
|
|
|
m_componentHighlightString == gbrItem->GetNetAttributes().m_Cmpref )
|
2017-09-17 22:44:30 +00:00
|
|
|
return m_layerColorsHi[aLayer];
|
|
|
|
|
2018-09-18 13:18:55 +00:00
|
|
|
if( !m_attributeHighlightString.IsEmpty() && gbrItem && gbrItem->GetDcodeDescr() &&
|
|
|
|
m_attributeHighlightString == gbrItem->GetDcodeDescr()->m_AperFunction )
|
2017-09-17 22:44:30 +00:00
|
|
|
return m_layerColorsHi[aLayer];
|
|
|
|
|
2021-03-13 17:45:33 +00:00
|
|
|
if( m_dcodeHighlightValue> 0 && gbrItem && gbrItem->GetDcodeDescr() &&
|
|
|
|
m_dcodeHighlightValue == gbrItem->GetDcodeDescr()->m_Num_Dcode )
|
|
|
|
return m_layerColorsHi[aLayer];
|
|
|
|
|
2017-09-17 22:44:30 +00:00
|
|
|
// Return grayish color for non-highlighted layers in the high contrast mode
|
2020-10-03 21:26:44 +00:00
|
|
|
if( m_hiContrastEnabled && m_highContrastLayers.count( aLayer ) == 0)
|
2019-06-09 15:13:50 +00:00
|
|
|
return m_hiContrastColor[aLayer];
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
// Catch the case when highlight and high-contraste modes are enabled
|
|
|
|
// and we are drawing a not highlighted track
|
|
|
|
if( m_highlightEnabled )
|
|
|
|
return m_layerColorsDark[aLayer];
|
|
|
|
|
|
|
|
// No special modificators enabled
|
|
|
|
return m_layerColors[aLayer];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-09-07 20:34:10 +00:00
|
|
|
bool GERBVIEW_RENDER_SETTINGS::GetShowPageLimits() const
|
|
|
|
{
|
|
|
|
return gvconfig()->m_Display.m_DisplayPageLimits;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-17 22:44:30 +00:00
|
|
|
GERBVIEW_PAINTER::GERBVIEW_PAINTER( GAL* aGal ) :
|
|
|
|
PAINTER( aGal )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO(JE): Pull up to PAINTER?
|
|
|
|
int GERBVIEW_PAINTER::getLineThickness( int aActualThickness ) const
|
|
|
|
{
|
2021-09-07 20:34:10 +00:00
|
|
|
// if items have 0 thickness, draw them with the outline width, otherwise respect the set
|
|
|
|
// value (which, no matter how small will produce something)
|
2017-09-17 22:44:30 +00:00
|
|
|
if( aActualThickness == 0 )
|
|
|
|
return m_gerbviewSettings.m_outlineWidth;
|
|
|
|
|
|
|
|
return aActualThickness;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool GERBVIEW_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
|
|
|
|
{
|
2021-09-07 20:34:10 +00:00
|
|
|
GERBER_DRAW_ITEM* gbrItem = dynamic_cast<GERBER_DRAW_ITEM*>( const_cast<VIEW_ITEM*>( aItem ) );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2021-09-07 20:34:10 +00:00
|
|
|
if( gbrItem )
|
2017-09-17 22:44:30 +00:00
|
|
|
{
|
2021-09-07 20:34:10 +00:00
|
|
|
draw( gbrItem, aLayer );
|
|
|
|
return true;
|
2017-09-17 22:44:30 +00:00
|
|
|
}
|
|
|
|
|
2021-09-07 20:34:10 +00:00
|
|
|
return false;
|
2017-09-17 22:44:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO(JE) aItem can't be const because of GetDcodeDescr()
|
|
|
|
// Probably that can be refactored in GERBER_DRAW_ITEM to allow const here.
|
|
|
|
void GERBVIEW_PAINTER::draw( /*const*/ GERBER_DRAW_ITEM* aItem, int aLayer )
|
|
|
|
{
|
|
|
|
VECTOR2D start( aItem->GetABPosition( aItem->m_Start ) ); // TODO(JE) Getter
|
|
|
|
VECTOR2D end( aItem->GetABPosition( aItem->m_End ) ); // TODO(JE) Getter
|
|
|
|
int width = aItem->m_Size.x; // TODO(JE) Getter
|
|
|
|
bool isFilled = true;
|
|
|
|
COLOR4D color;
|
|
|
|
// TODO(JE) This doesn't actually work properly for ImageNegative
|
2017-09-20 08:30:48 +00:00
|
|
|
bool isNegative = ( aItem->GetLayerPolarity() ^ aItem->m_GerberImageFile->m_ImageNegative );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2018-02-22 04:08:59 +00:00
|
|
|
// Draw DCODE overlay text
|
2017-09-17 22:44:30 +00:00
|
|
|
if( IsDCodeLayer( aLayer ) )
|
|
|
|
{
|
2022-01-11 13:47:21 +00:00
|
|
|
wxString codeText;
|
|
|
|
VECTOR2I textPosition;
|
|
|
|
int textSize;
|
|
|
|
EDA_ANGLE orient;
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2017-11-13 10:30:23 +00:00
|
|
|
if( !aItem->GetTextD_CodePrms( textSize, textPosition, orient ) )
|
|
|
|
return;
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
color = m_gerbviewSettings.GetColor( aItem, aLayer );
|
2022-02-05 19:31:22 +00:00
|
|
|
codeText.Printf( wxT( "D%d" ), aItem->m_DCode );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
m_gal->SetIsStroke( true );
|
|
|
|
m_gal->SetIsFill( false );
|
|
|
|
m_gal->SetStrokeColor( color );
|
|
|
|
m_gal->SetFillColor( COLOR4D( 0, 0, 0, 0 ) );
|
2018-11-20 09:22:45 +00:00
|
|
|
m_gal->SetLineWidth( textSize/10 );
|
2017-09-17 22:44:30 +00:00
|
|
|
m_gal->SetFontBold( false );
|
|
|
|
m_gal->SetFontItalic( false );
|
2020-10-02 20:18:07 +00:00
|
|
|
m_gal->SetFontUnderlined( false );
|
2017-09-17 22:44:30 +00:00
|
|
|
m_gal->SetTextMirrored( false );
|
|
|
|
m_gal->SetGlyphSize( VECTOR2D( textSize, textSize) );
|
2021-12-28 22:13:54 +00:00
|
|
|
m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER );
|
|
|
|
m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER );
|
2017-11-13 10:30:23 +00:00
|
|
|
m_gal->BitmapText( codeText, textPosition, orient );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
color = m_gerbviewSettings.GetColor( aItem, aLayer );
|
|
|
|
|
2017-09-18 01:54:02 +00:00
|
|
|
// TODO: Should brightened color be a preference?
|
|
|
|
if( aItem->IsBrightened() )
|
|
|
|
color = COLOR4D( 0.0, 1.0, 0.0, 0.75 );
|
|
|
|
|
2021-09-07 20:34:10 +00:00
|
|
|
m_gal->SetNegativeDrawMode( isNegative && !gvconfig()->m_Appearance.show_negative_objects );
|
2017-09-17 22:44:30 +00:00
|
|
|
m_gal->SetStrokeColor( color );
|
|
|
|
m_gal->SetFillColor( color );
|
|
|
|
m_gal->SetIsFill( isFilled );
|
|
|
|
m_gal->SetIsStroke( !isFilled );
|
|
|
|
|
2023-02-10 09:04:52 +00:00
|
|
|
switch( aItem->m_ShapeType )
|
2017-09-17 22:44:30 +00:00
|
|
|
{
|
|
|
|
case GBR_POLYGON:
|
|
|
|
{
|
2021-09-07 20:34:10 +00:00
|
|
|
isFilled = gvconfig()->m_Display.m_DisplayPolygonsFill;
|
2017-09-17 22:44:30 +00:00
|
|
|
m_gal->SetIsFill( isFilled );
|
|
|
|
m_gal->SetIsStroke( !isFilled );
|
|
|
|
|
|
|
|
if( isNegative && !isFilled )
|
|
|
|
{
|
|
|
|
m_gal->SetNegativeDrawMode( false );
|
|
|
|
m_gal->SetStrokeColor( GetSettings()->GetColor( aItem, aLayer ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !isFilled )
|
|
|
|
m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
|
|
|
|
|
2021-08-18 08:01:29 +00:00
|
|
|
if( aItem->m_AbsolutePolygon.OutlineCount() == 0 )
|
|
|
|
{
|
2023-02-10 10:50:20 +00:00
|
|
|
std::vector<VECTOR2I> pts = aItem->m_ShapeAsPolygon.COutline( 0 ).CPoints();
|
2019-03-23 18:26:44 +00:00
|
|
|
|
2021-08-18 08:01:29 +00:00
|
|
|
for( auto& pt : pts )
|
|
|
|
pt = aItem->GetABPosition( pt );
|
2019-03-23 18:26:44 +00:00
|
|
|
|
2021-08-18 08:01:29 +00:00
|
|
|
SHAPE_LINE_CHAIN chain( pts );
|
|
|
|
chain.SetClosed( true );
|
|
|
|
aItem->m_AbsolutePolygon.AddOutline( chain );
|
|
|
|
}
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2018-12-18 11:49:14 +00:00
|
|
|
// Degenerated polygons (having < 3 points) are drawn as lines
|
|
|
|
// to avoid issues in draw polygon functions
|
2021-08-18 08:01:29 +00:00
|
|
|
if( !isFilled || aItem->m_AbsolutePolygon.COutline( 0 ).PointCount() < 3 )
|
|
|
|
m_gal->DrawPolyline( aItem->m_AbsolutePolygon.COutline( 0 ) );
|
2017-09-17 22:44:30 +00:00
|
|
|
else
|
2018-12-18 11:49:14 +00:00
|
|
|
{
|
2021-09-07 20:34:10 +00:00
|
|
|
// On Opengl, a not convex filled polygon is usually drawn by using triangles as
|
|
|
|
// primitives. CacheTriangulation() can create basic triangle primitives to draw the
|
|
|
|
// polygon solid shape on Opengl
|
2022-05-13 16:09:22 +00:00
|
|
|
// We use the fastest CacheTriangulation calculation mode: no partition created because
|
|
|
|
// the partition is useless in Gerbview, and very time consumming (optimized only
|
|
|
|
// for pcbnew that has different internal unit)
|
2021-08-18 08:01:29 +00:00
|
|
|
if( m_gal->IsOpenGlEngine() && !aItem->m_AbsolutePolygon.IsTriangulationUpToDate() )
|
2022-05-13 16:09:22 +00:00
|
|
|
aItem->m_AbsolutePolygon.CacheTriangulation( false /* fastest triangulation calculation mode */ );
|
2018-12-18 11:49:14 +00:00
|
|
|
|
2021-08-18 08:01:29 +00:00
|
|
|
m_gal->DrawPolygon( aItem->m_AbsolutePolygon );
|
2018-12-18 11:49:14 +00:00
|
|
|
}
|
2018-12-03 12:10:55 +00:00
|
|
|
|
2017-09-17 22:44:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GBR_CIRCLE:
|
|
|
|
{
|
2021-09-07 20:34:10 +00:00
|
|
|
isFilled = gvconfig()->m_Display.m_DisplayLinesFill;
|
2017-09-17 22:44:30 +00:00
|
|
|
double radius = GetLineLength( aItem->m_Start, aItem->m_End );
|
|
|
|
m_gal->DrawCircle( start, radius );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GBR_ARC:
|
|
|
|
{
|
2021-09-07 20:34:10 +00:00
|
|
|
isFilled = gvconfig()->m_Display.m_DisplayLinesFill;
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2017-10-26 03:30:25 +00:00
|
|
|
// These are swapped because wxDC fills arcs counterclockwise and GAL
|
|
|
|
// fills them clockwise.
|
2022-01-01 06:04:08 +00:00
|
|
|
VECTOR2I arcStart = aItem->m_End;
|
|
|
|
VECTOR2I arcEnd = aItem->m_Start;
|
2017-10-26 03:30:25 +00:00
|
|
|
|
2017-09-17 22:44:30 +00:00
|
|
|
// Gerber arcs are 3-point (start, center, end)
|
|
|
|
// GAL needs center, radius, start angle, end angle
|
2017-10-26 03:30:25 +00:00
|
|
|
double radius = GetLineLength( arcStart, aItem->m_ArcCentre );
|
2017-09-17 22:44:30 +00:00
|
|
|
VECTOR2D center = aItem->GetABPosition( aItem->m_ArcCentre );
|
2017-10-26 03:30:25 +00:00
|
|
|
VECTOR2D startVec = VECTOR2D( aItem->GetABPosition( arcStart ) ) - center;
|
|
|
|
VECTOR2D endVec = VECTOR2D( aItem->GetABPosition( arcEnd ) ) - center;
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
m_gal->SetIsFill( isFilled );
|
|
|
|
m_gal->SetIsStroke( !isFilled );
|
|
|
|
m_gal->SetLineWidth( isFilled ? width : m_gerbviewSettings.m_outlineWidth );
|
|
|
|
|
2022-01-16 16:15:07 +00:00
|
|
|
EDA_ANGLE startAngle( startVec );
|
|
|
|
EDA_ANGLE endAngle( endVec );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2017-10-26 03:30:25 +00:00
|
|
|
// GAL fills in direction of increasing angle, so we have to convert
|
|
|
|
// the angle from the -PI to PI domain of atan2() to ensure that
|
|
|
|
// the arc goes in the right direction
|
2017-12-10 18:35:00 +00:00
|
|
|
if( startAngle > endAngle )
|
2022-01-16 16:15:07 +00:00
|
|
|
endAngle += ANGLE_360;
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2019-06-09 09:06:00 +00:00
|
|
|
// In Gerber, 360-degree arcs are stored in the file with start equal to end
|
2017-10-26 03:30:25 +00:00
|
|
|
if( arcStart == arcEnd )
|
2022-01-16 16:15:07 +00:00
|
|
|
endAngle = startAngle + ANGLE_360;
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2023-02-11 09:20:30 +00:00
|
|
|
// Adjust the allowed approx error to convert arcs to segments:
|
|
|
|
int arc_to_seg_error = gerbIUScale.mmToIU( 0.005 ); // Allow 5 microns
|
2023-08-22 13:06:53 +00:00
|
|
|
m_gal->DrawArcSegment( center, radius, startAngle, endAngle - startAngle, width,
|
|
|
|
arc_to_seg_error );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2019-06-09 09:06:00 +00:00
|
|
|
#if 0 // Arc Debugging only
|
2020-01-03 13:27:00 +00:00
|
|
|
m_gal->SetIsFill( false );
|
|
|
|
m_gal->SetIsStroke( true );
|
2019-06-09 09:06:00 +00:00
|
|
|
m_gal->SetLineWidth( 5 );
|
2020-01-03 13:27:00 +00:00
|
|
|
m_gal->SetStrokeColor( COLOR4D( 0.1, 0.5, 0.0, 0.5 ) );
|
2019-06-09 09:06:00 +00:00
|
|
|
m_gal->DrawLine( center, aItem->GetABPosition( arcStart ) );
|
2020-01-03 13:27:00 +00:00
|
|
|
m_gal->SetStrokeColor( COLOR4D( 0.6, 0.1, 0.0, 0.5 ) );
|
2019-06-09 09:06:00 +00:00
|
|
|
m_gal->DrawLine( center, aItem->GetABPosition( arcEnd ) );
|
|
|
|
#endif
|
2020-01-03 13:27:00 +00:00
|
|
|
|
|
|
|
#if 0 // Bbox arc Debugging only
|
|
|
|
m_gal->SetIsFill( false );
|
|
|
|
m_gal->SetIsStroke( true );
|
2022-08-31 12:56:58 +00:00
|
|
|
BOX2I box = aItem->GetBoundingBox();
|
2020-01-03 13:27:00 +00:00
|
|
|
m_gal->SetLineWidth( 5 );
|
|
|
|
m_gal->SetStrokeColor( COLOR4D(0.9, 0.9, 0, 0.4) );
|
|
|
|
// box coordinates are already in AB position.
|
|
|
|
m_gal->DrawRectangle( box.GetOrigin(), box.GetEnd() );
|
|
|
|
#endif
|
2017-09-17 22:44:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GBR_SPOT_CIRCLE:
|
|
|
|
case GBR_SPOT_RECT:
|
|
|
|
case GBR_SPOT_OVAL:
|
|
|
|
case GBR_SPOT_POLY:
|
|
|
|
case GBR_SPOT_MACRO:
|
2021-09-07 20:34:10 +00:00
|
|
|
isFilled = gvconfig()->m_Display.m_DisplayFlashedItemsFill;
|
2017-09-17 22:44:30 +00:00
|
|
|
drawFlashedShape( aItem, isFilled );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_SEGMENT:
|
|
|
|
{
|
|
|
|
/* Plot a line from m_Start to m_End.
|
|
|
|
* Usually, a round pen is used, but some gerber files use a rectangular pen
|
|
|
|
* In fact, any aperture can be used to plot a line.
|
|
|
|
* currently: only a square pen is handled (I believe using a polygon gives a strange plot).
|
|
|
|
*/
|
2021-09-07 20:34:10 +00:00
|
|
|
isFilled = gvconfig()->m_Display.m_DisplayLinesFill;
|
2017-09-17 22:44:30 +00:00
|
|
|
m_gal->SetIsFill( isFilled );
|
|
|
|
m_gal->SetIsStroke( !isFilled );
|
|
|
|
|
|
|
|
if( isNegative && !isFilled )
|
|
|
|
m_gal->SetStrokeColor( GetSettings()->GetColor( aItem, aLayer ) );
|
|
|
|
|
|
|
|
// TODO(JE) Refactor this to allow const aItem
|
|
|
|
D_CODE* code = aItem->GetDcodeDescr();
|
2023-02-10 10:18:09 +00:00
|
|
|
if( code && code->m_ApertType == APT_RECT )
|
2017-09-17 22:44:30 +00:00
|
|
|
{
|
2023-02-10 10:50:20 +00:00
|
|
|
if( aItem->m_ShapeAsPolygon.OutlineCount() == 0 )
|
2017-09-17 22:44:30 +00:00
|
|
|
aItem->ConvertSegmentToPolygon();
|
2019-03-23 18:26:44 +00:00
|
|
|
|
2023-02-10 10:50:20 +00:00
|
|
|
drawPolygon( aItem, aItem->m_ShapeAsPolygon, isFilled );
|
2017-09-17 22:44:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( !isFilled )
|
|
|
|
m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
|
|
|
|
|
|
|
|
m_gal->DrawSegment( start, end, width );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2022-02-05 19:31:22 +00:00
|
|
|
wxASSERT_MSG( false, wxT( "GERBER_DRAW_ITEM shape is unknown!" ) );
|
2017-09-17 22:44:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-08-19 08:01:31 +00:00
|
|
|
m_gal->SetNegativeDrawMode( false );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
// Enable for bounding box debugging
|
|
|
|
#if 0
|
|
|
|
const BOX2I& bb = aItem->ViewBBox();
|
|
|
|
m_gal->SetIsStroke( true );
|
|
|
|
m_gal->SetIsFill( true );
|
|
|
|
m_gal->SetLineWidth( 3 );
|
|
|
|
m_gal->SetStrokeColor( COLOR4D(0.9, 0.9, 0, 0.4) );
|
|
|
|
m_gal->SetFillColor( COLOR4D(0.9, 0.9, 0, 0.1) );
|
|
|
|
m_gal->DrawRectangle( bb.GetOrigin(), bb.GetEnd() );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-09-07 20:34:10 +00:00
|
|
|
void GERBVIEW_PAINTER::drawPolygon( GERBER_DRAW_ITEM* aParent, const SHAPE_POLY_SET& aPolygon,
|
|
|
|
bool aFilled, bool aShift )
|
2017-09-17 22:44:30 +00:00
|
|
|
{
|
2019-12-14 09:14:50 +00:00
|
|
|
wxASSERT( aPolygon.OutlineCount() == 1 );
|
|
|
|
|
|
|
|
if( aPolygon.OutlineCount() == 0 )
|
|
|
|
return;
|
|
|
|
|
2019-03-23 18:26:44 +00:00
|
|
|
SHAPE_POLY_SET poly;
|
2019-12-14 09:14:50 +00:00
|
|
|
poly.NewOutline();
|
|
|
|
const std::vector<VECTOR2I> pts = aPolygon.COutline( 0 ).CPoints();
|
2021-09-07 20:34:10 +00:00
|
|
|
VECTOR2I offset = aShift ? VECTOR2I( aParent->m_Start ) : VECTOR2I( 0, 0 );
|
2019-03-23 18:26:44 +00:00
|
|
|
|
2021-09-07 20:34:10 +00:00
|
|
|
for( const VECTOR2I& pt : pts )
|
2019-03-23 18:26:44 +00:00
|
|
|
poly.Append( aParent->GetABPosition( pt + offset ) );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2021-09-07 20:34:10 +00:00
|
|
|
if( !gvconfig()->m_Display.m_DisplayPolygonsFill )
|
2017-09-17 22:44:30 +00:00
|
|
|
m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
|
|
|
|
|
|
|
|
if( !aFilled )
|
2019-03-23 18:26:44 +00:00
|
|
|
m_gal->DrawPolyline( poly.COutline( 0 ) );
|
2017-09-17 22:44:30 +00:00
|
|
|
else
|
2019-03-23 18:26:44 +00:00
|
|
|
m_gal->DrawPolygon( poly );
|
2017-09-17 22:44:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GERBVIEW_PAINTER::drawFlashedShape( GERBER_DRAW_ITEM* aItem, bool aFilled )
|
|
|
|
{
|
|
|
|
D_CODE* code = aItem->GetDcodeDescr();
|
|
|
|
|
2022-02-05 19:31:22 +00:00
|
|
|
wxASSERT_MSG( code, wxT( "drawFlashedShape: Item has no D_CODE!" ) );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2018-03-19 01:55:07 +00:00
|
|
|
if( !code )
|
|
|
|
return;
|
|
|
|
|
2017-09-17 22:44:30 +00:00
|
|
|
m_gal->SetIsFill( aFilled );
|
|
|
|
m_gal->SetIsStroke( !aFilled );
|
|
|
|
m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
|
|
|
|
|
2023-02-10 09:04:52 +00:00
|
|
|
switch( aItem->m_ShapeType )
|
2017-09-17 22:44:30 +00:00
|
|
|
{
|
|
|
|
case GBR_SPOT_CIRCLE:
|
|
|
|
{
|
|
|
|
int radius = code->m_Size.x >> 1;
|
|
|
|
VECTOR2D start( aItem->GetABPosition( aItem->m_Start ) );
|
|
|
|
|
2018-11-15 12:10:42 +00:00
|
|
|
if( !aFilled || code->m_DrillShape == APT_DEF_NO_HOLE )
|
2017-09-17 22:44:30 +00:00
|
|
|
{
|
|
|
|
m_gal->DrawCircle( start, radius );
|
|
|
|
}
|
2018-11-15 12:10:42 +00:00
|
|
|
else // rectangular hole
|
2017-09-17 22:44:30 +00:00
|
|
|
{
|
2018-11-15 12:10:42 +00:00
|
|
|
if( code->m_Polygon.OutlineCount() == 0 )
|
2022-09-07 16:54:04 +00:00
|
|
|
code->ConvertShapeToPolygon( aItem );
|
2018-11-15 12:10:42 +00:00
|
|
|
|
2019-12-14 09:14:50 +00:00
|
|
|
drawPolygon( aItem, code->m_Polygon, aFilled, true );
|
2017-09-17 22:44:30 +00:00
|
|
|
}
|
2018-11-15 12:10:42 +00:00
|
|
|
|
2017-09-17 22:44:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GBR_SPOT_RECT:
|
|
|
|
{
|
2022-01-01 06:04:08 +00:00
|
|
|
VECTOR2I codeStart;
|
|
|
|
VECTOR2I aShapePos = aItem->m_Start;
|
2017-09-17 22:44:30 +00:00
|
|
|
codeStart.x = aShapePos.x - code->m_Size.x / 2;
|
|
|
|
codeStart.y = aShapePos.y - code->m_Size.y / 2;
|
2022-01-01 06:04:08 +00:00
|
|
|
VECTOR2I codeEnd = codeStart + code->m_Size;
|
2017-09-17 22:44:30 +00:00
|
|
|
codeStart = aItem->GetABPosition( codeStart );
|
|
|
|
codeEnd = aItem->GetABPosition( codeEnd );
|
|
|
|
|
|
|
|
if( !aFilled || code->m_DrillShape == APT_DEF_NO_HOLE )
|
|
|
|
{
|
|
|
|
m_gal->DrawRectangle( VECTOR2D( codeStart ), VECTOR2D( codeEnd ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( code->m_Polygon.OutlineCount() == 0 )
|
2022-09-07 16:54:04 +00:00
|
|
|
code->ConvertShapeToPolygon( aItem );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2019-12-14 09:14:50 +00:00
|
|
|
drawPolygon( aItem, code->m_Polygon, aFilled, true );
|
2017-09-17 22:44:30 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GBR_SPOT_OVAL:
|
|
|
|
{
|
|
|
|
int radius = 0;
|
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
VECTOR2I codeStart = aItem->m_Start;
|
|
|
|
VECTOR2I codeEnd = aItem->m_Start;
|
2017-09-17 22:44:30 +00:00
|
|
|
|
|
|
|
if( code->m_Size.x > code->m_Size.y ) // horizontal oval
|
|
|
|
{
|
|
|
|
int delta = (code->m_Size.x - code->m_Size.y) / 2;
|
|
|
|
codeStart.x -= delta;
|
|
|
|
codeEnd.x += delta;
|
|
|
|
radius = code->m_Size.y;
|
|
|
|
}
|
|
|
|
else // horizontal oval
|
|
|
|
{
|
|
|
|
int delta = (code->m_Size.y - code->m_Size.x) / 2;
|
|
|
|
codeStart.y -= delta;
|
|
|
|
codeEnd.y += delta;
|
|
|
|
radius = code->m_Size.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
codeStart = aItem->GetABPosition( codeStart );
|
|
|
|
codeEnd = aItem->GetABPosition( codeEnd );
|
|
|
|
|
|
|
|
if( !aFilled || code->m_DrillShape == APT_DEF_NO_HOLE )
|
|
|
|
{
|
|
|
|
m_gal->DrawSegment( codeStart, codeEnd, radius );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( code->m_Polygon.OutlineCount() == 0 )
|
2022-09-07 16:54:04 +00:00
|
|
|
code->ConvertShapeToPolygon( aItem );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2019-12-14 09:14:50 +00:00
|
|
|
drawPolygon( aItem, code->m_Polygon, aFilled, true );
|
2017-09-17 22:44:30 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GBR_SPOT_POLY:
|
|
|
|
if( code->m_Polygon.OutlineCount() == 0 )
|
2022-09-07 16:54:04 +00:00
|
|
|
code->ConvertShapeToPolygon( aItem );
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2019-12-14 09:14:50 +00:00
|
|
|
drawPolygon( aItem, code->m_Polygon, aFilled, true );
|
2017-09-17 22:44:30 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_SPOT_MACRO:
|
|
|
|
drawApertureMacro( aItem, aFilled );
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
wxASSERT_MSG( false, wxT( "Unknown Gerber flashed shape!" ) );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GERBVIEW_PAINTER::drawApertureMacro( GERBER_DRAW_ITEM* aParent, bool aFilled )
|
|
|
|
{
|
2023-02-11 11:00:02 +00:00
|
|
|
if( aParent->m_AbsolutePolygon.OutlineCount() == 0 )
|
|
|
|
{
|
|
|
|
D_CODE* code = aParent->GetDcodeDescr();
|
|
|
|
APERTURE_MACRO* macro = code->GetMacro();
|
|
|
|
aParent->m_AbsolutePolygon = *macro->GetApertureMacroShape( aParent, aParent->m_Start );
|
|
|
|
}
|
|
|
|
|
|
|
|
SHAPE_POLY_SET& polyset = aParent->m_AbsolutePolygon;
|
2017-09-17 22:44:30 +00:00
|
|
|
|
2021-09-07 20:34:10 +00:00
|
|
|
if( !gvconfig()->m_Display.m_DisplayPolygonsFill )
|
2017-09-17 22:44:30 +00:00
|
|
|
m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
|
|
|
|
|
|
|
|
if( !aFilled )
|
|
|
|
{
|
2023-02-11 11:00:02 +00:00
|
|
|
for( int i = 0; i < polyset.OutlineCount(); i++ )
|
|
|
|
m_gal->DrawPolyline( polyset.COutline( i ) );
|
2017-09-17 22:44:30 +00:00
|
|
|
}
|
|
|
|
else
|
2021-09-07 20:34:10 +00:00
|
|
|
{
|
2023-02-11 11:00:02 +00:00
|
|
|
m_gal->DrawPolygon( polyset );
|
2021-09-07 20:34:10 +00:00
|
|
|
}
|
2017-09-17 22:44:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-09-16 11:33:56 +00:00
|
|
|
const double GERBVIEW_RENDER_SETTINGS::MAX_FONT_SIZE = gerbIUScale.mmToIU( 10.0 );
|