diff --git a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp new file mode 100644 index 0000000000..5852bdedbc --- /dev/null +++ b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp @@ -0,0 +1,1005 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2016 Mario Luzeiro + * Copyright (C) 1992-2018 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 + */ + +/** + * @file create_graphic_brd_items.cpp + * @brief This file implements the creation of 2D graphic primitives of pcb items: + * pads, tracks, drawsegments, texts.... + * It is based on the function found in the files: + * board_items_to_polygon_shape_transform.cpp + */ + +#include "cinfo3d_visu.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/cring2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h" +#include "../3d_rendering/3d_render_raytracing/accelerators/ccontainer2d.h" +#include "../3d_rendering/3d_render_raytracing/shapes3D/ccylinder.h" +#include "../3d_rendering/3d_render_raytracing/shapes3D/clayeritem.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +// These variables are parameters used in addTextSegmToContainer. +// But addTextSegmToContainer is a call-back function, +// so we cannot send them as arguments. +static int s_textWidth; +static CGENERICCONTAINER2D *s_dstcontainer = NULL; +static float s_biuTo3Dunits; +static const CBBOX2D *s_boardBBox3DU = NULL; +static const BOARD_ITEM *s_boardItem = NULL; + +// This is a call back function, used by DrawGraphicText to draw the 3D text shape: +void addTextSegmToContainer( int x0, int y0, int xf, int yf ) +{ + wxASSERT( s_boardBBox3DU != NULL ); + wxASSERT( s_dstcontainer != NULL ); + + const SFVEC2F start3DU( x0 * s_biuTo3Dunits, -y0 * s_biuTo3Dunits ); + const SFVEC2F end3DU ( xf * s_biuTo3Dunits, -yf * s_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + s_dstcontainer->Add( new CFILLEDCIRCLE2D( start3DU, + s_textWidth * s_biuTo3Dunits, + *s_boardItem) ); + else + s_dstcontainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + s_textWidth * s_biuTo3Dunits, + *s_boardItem ) ); +} + + +// Based on +// void TEXTE_PCB::TransformShapeWithClearanceToPolygonSet +// board_items_to_polygon_shape_transform.cpp +void CINFO3D_VISU::AddShapeWithClearanceToContainer( const TEXTE_PCB* aTextPCB, + CGENERICCONTAINER2D *aDstContainer, + PCB_LAYER_ID aLayerId, + int aClearanceValue ) +{ + wxSize size = aTextPCB->GetTextSize(); + + if( aTextPCB->IsMirrored() ) + size.x = -size.x; + + s_boardItem = (const BOARD_ITEM *)&aTextPCB; + s_dstcontainer = aDstContainer; + s_textWidth = aTextPCB->GetThickness() + ( 2 * aClearanceValue ); + s_biuTo3Dunits = m_biuTo3Dunits; + s_boardBBox3DU = &m_board2dBBox3DU; + + // not actually used, but needed by DrawGraphicText + const COLOR4D dummy_color = COLOR4D::BLACK; + + if( aTextPCB->IsMultilineAllowed() ) + { + wxArrayString strings_list; + wxStringSplit( aTextPCB->GetShownText(), strings_list, '\n' ); + std::vector positions; + positions.reserve( strings_list.Count() ); + aTextPCB->GetPositionsOfLinesOfMultilineText( positions, + strings_list.Count() ); + + for( unsigned ii = 0; ii < strings_list.Count(); ++ii ) + { + wxString txt = strings_list.Item( ii ); + + DrawGraphicText( NULL, NULL, positions[ii], dummy_color, + txt, aTextPCB->GetTextAngle(), size, + aTextPCB->GetHorizJustify(), aTextPCB->GetVertJustify(), + aTextPCB->GetThickness(), aTextPCB->IsItalic(), + true, addTextSegmToContainer ); + } + } + else + { + DrawGraphicText( NULL, NULL, aTextPCB->GetTextPos(), dummy_color, + aTextPCB->GetShownText(), aTextPCB->GetTextAngle(), size, + aTextPCB->GetHorizJustify(), aTextPCB->GetVertJustify(), + aTextPCB->GetThickness(), aTextPCB->IsItalic(), + true, addTextSegmToContainer ); + } +} + + +void CINFO3D_VISU::AddShapeWithClearanceToContainer( const DIMENSION* aDimension, + CGENERICCONTAINER2D *aDstContainer, + PCB_LAYER_ID aLayerId, + int aClearanceValue ) +{ + AddShapeWithClearanceToContainer(&aDimension->Text(), aDstContainer, aLayerId, aClearanceValue); + + const int linewidth = aDimension->GetWidth() + (2 * aClearanceValue); + + std::pair segs[] = { + {&aDimension->m_crossBarO, &aDimension->m_crossBarF}, + {&aDimension->m_featureLineGO, &aDimension->m_featureLineGF}, + {&aDimension->m_featureLineDO, &aDimension->m_featureLineDF}, + {&aDimension->m_crossBarF, &aDimension->m_arrowD1F}, + {&aDimension->m_crossBarF, &aDimension->m_arrowD2F}, + {&aDimension->m_crossBarO, &aDimension->m_arrowG1F}, + {&aDimension->m_crossBarO, &aDimension->m_arrowG2F}}; + + for( auto const & ii : segs ) + { + const SFVEC2F start3DU( ii.first->x * m_biuTo3Dunits, + -ii.first->y * m_biuTo3Dunits ); + + const SFVEC2F end3DU ( ii.second->x * m_biuTo3Dunits, + -ii.second->y * m_biuTo3Dunits ); + + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + linewidth * m_biuTo3Dunits, + *aDimension ) ); + } +} + + +// Based on +// void MODULE::TransformGraphicShapesWithClearanceToPolygonSet +// board_items_to_polygon_shape_transform.cpp#L204 +void CINFO3D_VISU::AddGraphicsShapesWithClearanceToContainer( const MODULE* aModule, + CGENERICCONTAINER2D *aDstContainer, + PCB_LAYER_ID aLayerId, + int aInflateValue ) +{ + std::vector texts; // List of TEXTE_MODULE to convert + EDGE_MODULE* outline; + + for( EDA_ITEM* item = aModule->GraphicalItemsList(); + item != NULL; + item = item->Next() ) + { + switch( item->Type() ) + { + case PCB_MODULE_TEXT_T: + { + TEXTE_MODULE* text = static_cast( item ); + + if( text->GetLayer() == aLayerId && text->IsVisible() ) + texts.push_back( text ); + } + break; + + + case PCB_MODULE_EDGE_T: + { + outline = (EDGE_MODULE*) item; + + if( outline->GetLayer() != aLayerId ) + break; + + AddShapeWithClearanceToContainer( (const DRAWSEGMENT *)outline, + aDstContainer, + aLayerId, 0 ); + } + break; + + default: + break; + } + } + + // Convert texts sur modules + if( aModule->Reference().GetLayer() == aLayerId && aModule->Reference().IsVisible() ) + texts.push_back( &aModule->Reference() ); + + if( aModule->Value().GetLayer() == aLayerId && aModule->Value().IsVisible() ) + texts.push_back( &aModule->Value() ); + + s_boardItem = (const BOARD_ITEM *)&aModule->Value(); + s_dstcontainer = aDstContainer; + s_biuTo3Dunits = m_biuTo3Dunits; + s_boardBBox3DU = &m_board2dBBox3DU; + + for( unsigned ii = 0; ii < texts.size(); ++ii ) + { + TEXTE_MODULE *textmod = texts[ii]; + s_textWidth = textmod->GetThickness() + ( 2 * aInflateValue ); + wxSize size = textmod->GetTextSize(); + + if( textmod->IsMirrored() ) + size.x = -size.x; + + DrawGraphicText( NULL, NULL, textmod->GetTextPos(), BLACK, + textmod->GetShownText(), textmod->GetDrawRotation(), size, + textmod->GetHorizJustify(), textmod->GetVertJustify(), + textmod->GetThickness(), textmod->IsItalic(), + true, addTextSegmToContainer ); + } +} + + +COBJECT2D *CINFO3D_VISU::createNewTrack( const TRACK* aTrack, + int aClearanceValue ) const +{ + SFVEC2F start3DU( aTrack->GetStart().x * m_biuTo3Dunits, + -aTrack->GetStart().y * m_biuTo3Dunits ); // y coord is inverted + + switch( aTrack->Type() ) + { + case PCB_VIA_T: + { + const float radius = ( ( aTrack->GetWidth() / 2 ) + aClearanceValue ) * m_biuTo3Dunits; + + return new CFILLEDCIRCLE2D( start3DU, radius, *aTrack ); + } + break; + + default: + { + wxASSERT( aTrack->Type() == PCB_TRACE_T ); + + SFVEC2F end3DU ( aTrack->GetEnd().x * m_biuTo3Dunits, + -aTrack->GetEnd().y * m_biuTo3Dunits ); + + // Cannot add segments that have the same start and end point + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + const float radius = ((aTrack->GetWidth() / 2) + aClearanceValue) * m_biuTo3Dunits; + + return new CFILLEDCIRCLE2D( start3DU, radius, *aTrack ); + } + else + { + const float width = (aTrack->GetWidth() + 2 * aClearanceValue ) * m_biuTo3Dunits; + + return new CROUNDSEGMENT2D( start3DU, end3DU, width, *aTrack ); + } + } + break; + } + + return NULL; +} + + +// Based on: +// void D_PAD:: TransformShapeWithClearanceToPolygon( +// board_items_to_polygon_shape_transform.cpp +void CINFO3D_VISU::createNewPadWithClearance( const D_PAD* aPad, + CGENERICCONTAINER2D *aDstContainer, + int aClearanceValue ) const +{ + const int dx = (aPad->GetSize().x / 2) + aClearanceValue; + const int dy = (aPad->GetSize().y / 2) + aClearanceValue; + + if( !dx || !dy ) + { + wxLogTrace( m_logTrace, + wxT( "CINFO3D_VISU::createNewPadWithClearance - found an invalid pad" ) ); + + return; + } + + wxPoint PadShapePos = aPad->ShapePos(); // Note: for pad having a shape offset, + // the pad position is NOT the shape position + + switch( aPad->GetShape() ) + { + case PAD_SHAPE_CIRCLE: + { + const float radius = dx * m_biuTo3Dunits; + + const SFVEC2F center( PadShapePos.x * m_biuTo3Dunits, + -PadShapePos.y * m_biuTo3Dunits ); + + aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius, *aPad ) ); + } + break; + + case PAD_SHAPE_OVAL: + { + if( abs( dx - dy ) == 0 ) + { + // The segment object cannot store start and end the same position, + // so add a circle instead + const float radius = dx * m_biuTo3Dunits; + + const SFVEC2F center( PadShapePos.x * m_biuTo3Dunits, + -PadShapePos.y * m_biuTo3Dunits ); + + aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius, *aPad ) ); + } + else + { + // An oval pad has the same shape as a segment with rounded ends + + int iwidth; + wxPoint shape_offset = wxPoint( 0, 0 ); + + if( dy > dx ) // Oval pad X/Y ratio for choosing translation axis + { + shape_offset.y = dy - dx; + iwidth = dx * 2; + } + else //if( dy <= dx ) + { + shape_offset.x = dy - dx; + iwidth = dy * 2; + } + + RotatePoint( &shape_offset, aPad->GetOrientation() ); + + const wxPoint start = PadShapePos - shape_offset; + const wxPoint end = PadShapePos + shape_offset; + + const SFVEC2F start3DU( start.x * m_biuTo3Dunits, -start.y * m_biuTo3Dunits ); + const SFVEC2F end3DU ( end.x * m_biuTo3Dunits, -end.y * m_biuTo3Dunits ); + + // Cannot add segments that have the same start and end point + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + (iwidth / 2) * m_biuTo3Dunits, + *aPad ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + iwidth * m_biuTo3Dunits, + *aPad ) ); + } + } + } + break; + + case PAD_SHAPE_TRAPEZOID: + case PAD_SHAPE_RECT: + { + // https://github.com/KiCad/kicad-source-mirror/blob/0cab3e47ad8097db7b898b3cef2cf9b235318ca3/pcbnew/board_items_to_polygon_shape_transform.cpp#L613 + + wxPoint corners[4]; + aPad->BuildPadPolygon( corners, wxSize( 0, 0), aPad->GetOrientation() ); + + SFVEC2F corners3DU[4]; + + // Note: for pad having a shape offset, + // the pad position is NOT the shape position + for( unsigned int ii = 0; ii < 4; ++ii ) + { + corners[ii] += aPad->ShapePos(); // Shift origin to position + + corners3DU[ii] = SFVEC2F( corners[ii].x * m_biuTo3Dunits, + -corners[ii].y * m_biuTo3Dunits ); + } + + + // Learn more at: + // https://lists.launchpad.net/kicad-developers/msg18729.html + + // Add the PAD polygon + aDstContainer->Add( new CPOLYGON4PTS2D( corners3DU[0], + corners3DU[1], + corners3DU[2], + corners3DU[3], + *aPad ) ); + + // Add the PAD contours + // !TODO: check the corners because it cannot add + // roundsegments that are in the same start and end position + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[0], + corners3DU[1], + aClearanceValue * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[1], + corners3DU[2], + aClearanceValue * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[2], + corners3DU[3], + aClearanceValue * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[3], + corners3DU[0], + aClearanceValue * 2.0f * m_biuTo3Dunits, + *aPad ) ); + } + break; + + case PAD_SHAPE_ROUNDRECT: + { + const int pad_radius = aPad->GetRoundRectCornerRadius(); + const int rounding_radius = pad_radius + aClearanceValue; + + wxSize shapesize( aPad->GetSize() ); + shapesize.x += aClearanceValue * 2; + shapesize.y += aClearanceValue * 2; + + wxPoint corners[4]; + + GetRoundRectCornerCenters( corners, + rounding_radius, + PadShapePos, + shapesize, + aPad->GetOrientation() ); + + SFVEC2F corners3DU[4]; + + for( unsigned int ii = 0; ii < 4; ++ii ) + corners3DU[ii] = SFVEC2F( corners[ii].x * m_biuTo3Dunits, + -corners[ii].y * m_biuTo3Dunits ); + + // Add the PAD polygon (For some reason the corners need + // to be inverted to display with the correctly orientation) + aDstContainer->Add( new CPOLYGON4PTS2D( corners3DU[0], + corners3DU[3], + corners3DU[2], + corners3DU[1], + *aPad ) ); + + // Add the PAD contours + // !TODO: check the corners because it cannot add + // roundsegments that are in the same start and end position + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[0], + corners3DU[1], + rounding_radius * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[1], + corners3DU[2], + rounding_radius * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[2], + corners3DU[3], + rounding_radius * 2.0f * m_biuTo3Dunits, + *aPad ) ); + + aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[3], + corners3DU[0], + rounding_radius * 2.0f * m_biuTo3Dunits, + *aPad ) ); + } + break; + + case PAD_SHAPE_CUSTOM: + { + SHAPE_POLY_SET polyList; // Will contain the pad outlines in board coordinates + polyList.Append( aPad->GetCustomShapeAsPolygon() ); + aPad->CustomShapeAsPolygonToBoardPosition( &polyList, aPad->ShapePos(), aPad->GetOrientation() ); + + if( aClearanceValue ) + polyList.Inflate( aClearanceValue, 32 ); + + // This convert the poly in outline and holes + polyList.Simplify( SHAPE_POLY_SET::PM_FAST ); + polyList.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + + // Add the PAD polygon + Convert_shape_line_polygon_to_triangles( polyList, *aDstContainer, m_biuTo3Dunits, *aPad ); + + } + break; + } +} + + +// Based on: +// BuildPadDrillShapePolygon +// board_items_to_polygon_shape_transform.cpp +COBJECT2D *CINFO3D_VISU::createNewPadDrill( const D_PAD* aPad, int aInflateValue ) +{ + wxSize drillSize = aPad->GetDrillSize(); + + if( !drillSize.x || !drillSize.y ) + { + wxLogTrace( m_logTrace, wxT( "CINFO3D_VISU::createNewPadDrill - found an invalid pad" ) ); + return NULL; + } + + if( drillSize.x == drillSize.y ) // usual round hole + { + const int radius = (drillSize.x / 2) + aInflateValue; + + const SFVEC2F center( aPad->GetPosition().x * m_biuTo3Dunits, + -aPad->GetPosition().y * m_biuTo3Dunits ); + + return new CFILLEDCIRCLE2D( center, radius * m_biuTo3Dunits, *aPad ); + + } + else // Oblong hole + { + wxPoint start, end; + int width; + + aPad->GetOblongDrillGeometry( start, end, width ); + + width += aInflateValue * 2; + start += aPad->GetPosition(); + end += aPad->GetPosition(); + + SFVEC2F start3DU( start.x * m_biuTo3Dunits, + -start.y * m_biuTo3Dunits ); + + SFVEC2F end3DU ( end.x * m_biuTo3Dunits, + -end.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + return new CFILLEDCIRCLE2D( start3DU, (width / 2) * m_biuTo3Dunits, *aPad ); + } + else + { + return new CROUNDSEGMENT2D( start3DU, end3DU, width * m_biuTo3Dunits, *aPad ); + } + } + + return NULL; +} + + +// This function pretends to be like the +// void D_PAD::BuildPadShapePolygon( +// board_items_to_polygon_shape_transform.cpp +void CINFO3D_VISU::createNewPad( const D_PAD* aPad, + CGENERICCONTAINER2D *aDstContainer, + const wxSize &aInflateValue ) const +{ + switch( aPad->GetShape() ) + { + case PAD_SHAPE_CIRCLE: + case PAD_SHAPE_OVAL: + case PAD_SHAPE_ROUNDRECT: + case PAD_SHAPE_CUSTOM: + createNewPadWithClearance( aPad, aDstContainer, aInflateValue.x ); + break; + + case PAD_SHAPE_TRAPEZOID: + case PAD_SHAPE_RECT: + wxPoint corners[4]; + aPad->BuildPadPolygon( corners, aInflateValue, aPad->GetOrientation() ); + + // Note: for pad having a shape offset, + // the pad position is NOT the shape position + for( unsigned int ii = 0; ii < 4; ++ii ) + corners[ii] += aPad->ShapePos(); // Shift origin to position + + aDstContainer->Add( new CPOLYGON4PTS2D( + SFVEC2F( corners[0].x * m_biuTo3Dunits, + -corners[0].y * m_biuTo3Dunits ), + SFVEC2F( corners[1].x * m_biuTo3Dunits, + -corners[1].y * m_biuTo3Dunits ), + SFVEC2F( corners[2].x * m_biuTo3Dunits, + -corners[2].y * m_biuTo3Dunits ), + SFVEC2F( corners[3].x * m_biuTo3Dunits, + -corners[3].y * m_biuTo3Dunits ), + *aPad ) ); + + break; + } +} + + +void CINFO3D_VISU::AddPadsShapesWithClearanceToContainer( const MODULE* aModule, + CGENERICCONTAINER2D *aDstContainer, + PCB_LAYER_ID aLayerId, + int aInflateValue, + bool aSkipNPTHPadsWihNoCopper ) +{ + const D_PAD* pad = aModule->PadsList(); + + wxSize margin; + + for( ; pad != NULL; pad = pad->Next() ) + { + if( !pad->IsOnLayer( aLayerId ) ) + continue; + + // NPTH pads are not drawn on layers if the + // shape size and pos is the same as their hole: + if( aSkipNPTHPadsWihNoCopper && (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED) ) + { + if( (pad->GetDrillSize() == pad->GetSize()) && + (pad->GetOffset() == wxPoint( 0, 0 )) ) + { + switch( pad->GetShape() ) + { + case PAD_SHAPE_CIRCLE: + if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ) + continue; + break; + + case PAD_SHAPE_OVAL: + if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE ) + continue; + break; + + default: + break; + } + } + } + + switch( aLayerId ) + { + case F_Mask: + case B_Mask: + margin.x = margin.y = pad->GetSolderMaskMargin() + aInflateValue; + break; + + case F_Paste: + case B_Paste: + margin = pad->GetSolderPasteMargin(); + margin.x += aInflateValue; + margin.y += aInflateValue; + break; + + default: + margin.x = margin.y = aInflateValue; + break; + } + + createNewPad( pad, aDstContainer, margin ); + } +} + +// based on TransformArcToPolygon function from +// common/convert_basic_shapes_to_polygon.cpp +void CINFO3D_VISU::TransformArcToSegments( const wxPoint &aCentre, + const wxPoint &aStart, + double aArcAngle, + int aCircleToSegmentsCount, + int aWidth, + CGENERICCONTAINER2D *aDstContainer, + const BOARD_ITEM &aBoardItem ) +{ + wxPoint arc_start, arc_end; + int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree + + arc_end = arc_start = aStart; + + if( aArcAngle != 3600 ) + { + RotatePoint( &arc_end, aCentre, -aArcAngle ); + } + + if( aArcAngle < 0 ) + { + std::swap( arc_start, arc_end ); + aArcAngle = -aArcAngle; + } + + // Compute the ends of segments and creates poly + wxPoint curr_end = arc_start; + wxPoint curr_start = arc_start; + + for( int ii = delta; ii < aArcAngle; ii += delta ) + { + curr_end = arc_start; + RotatePoint( &curr_end, aCentre, -ii ); + + const SFVEC2F start3DU( curr_start.x * m_biuTo3Dunits, -curr_start.y * m_biuTo3Dunits ); + const SFVEC2F end3DU ( curr_end.x * m_biuTo3Dunits, -curr_end.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + ( aWidth / 2 ) * m_biuTo3Dunits, + aBoardItem ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + aWidth * m_biuTo3Dunits, + aBoardItem ) ); + } + + curr_start = curr_end; + } + + if( curr_end != arc_end ) + { + const SFVEC2F start3DU( curr_end.x * m_biuTo3Dunits, -curr_end.y * m_biuTo3Dunits ); + const SFVEC2F end3DU ( arc_end.x * m_biuTo3Dunits, -arc_end.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + ( aWidth / 2 ) * m_biuTo3Dunits, + aBoardItem ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + aWidth * m_biuTo3Dunits, + aBoardItem ) ); + } + } +} + +// Based on +// TransformShapeWithClearanceToPolygon +// board_items_to_polygon_shape_transform.cpp#L431 +void CINFO3D_VISU::AddShapeWithClearanceToContainer( const DRAWSEGMENT* aDrawSegment, + CGENERICCONTAINER2D *aDstContainer, + PCB_LAYER_ID aLayerId, + int aClearanceValue ) +{ + // The full width of the lines to create: + const int linewidth = aDrawSegment->GetWidth() + (2 * aClearanceValue); + + switch( aDrawSegment->GetShape() ) + { + case S_CIRCLE: + { + const SFVEC2F center3DU( aDrawSegment->GetCenter().x * m_biuTo3Dunits, + -aDrawSegment->GetCenter().y * m_biuTo3Dunits ); + + const float inner_radius = (aDrawSegment->GetRadius() - linewidth / 2) * m_biuTo3Dunits; + const float outter_radius = (aDrawSegment->GetRadius() + linewidth / 2) * m_biuTo3Dunits; + + aDstContainer->Add( new CRING2D( center3DU, + inner_radius, + outter_radius, + *aDrawSegment ) ); + } + break; + + case S_ARC: + { + const unsigned int nr_segments = + GetNrSegmentsCircle( aDrawSegment->GetBoundingBox().GetSizeMax() ); + + TransformArcToSegments( aDrawSegment->GetCenter(), + aDrawSegment->GetArcStart(), + aDrawSegment->GetAngle(), + nr_segments, + aDrawSegment->GetWidth(), + aDstContainer, + *aDrawSegment ); + } + break; + + case S_SEGMENT: + { + const SFVEC2F start3DU( aDrawSegment->GetStart().x * m_biuTo3Dunits, + -aDrawSegment->GetStart().y * m_biuTo3Dunits ); + + const SFVEC2F end3DU ( aDrawSegment->GetEnd().x * m_biuTo3Dunits, + -aDrawSegment->GetEnd().y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + ( linewidth / 2 ) * m_biuTo3Dunits, + *aDrawSegment ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, + end3DU, + linewidth * m_biuTo3Dunits, + *aDrawSegment ) ); + } + } + break; + + case S_POLYGON: + { + const int segcountforcircle = 16; + const double correctionFactor = GetCircleCorrectionFactor( segcountforcircle ); + SHAPE_POLY_SET polyList; + + aDrawSegment->TransformShapeWithClearanceToPolygon( polyList, aClearanceValue, + segcountforcircle, correctionFactor ); + // This convert the poly in outline and holes + // Note: This two sequencial calls are need in order to get + // the triangulation function to work properly. + polyList.Simplify( SHAPE_POLY_SET::PM_FAST ); + polyList.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + + if( polyList.IsEmpty() ) // Just for caution + break; + + Convert_shape_line_polygon_to_triangles( polyList, *aDstContainer, + m_biuTo3Dunits, *aDrawSegment ); + } + break; + + case S_CURVE: // Bezier curve (not yet in use in KiCad) + break; + + default: + break; + } +} + + +// Based on +// TransformSolidAreasShapesToPolygonSet +// board_items_to_polygon_shape_transform.cpp +void CINFO3D_VISU::AddSolidAreasShapesToContainer( const ZONE_CONTAINER* aZoneContainer, + CGENERICCONTAINER2D *aDstContainer, + PCB_LAYER_ID aLayerId ) +{ + // Copy the polys list because we have to simplify it + SHAPE_POLY_SET polyList = SHAPE_POLY_SET(aZoneContainer->GetFilledPolysList()); + + // This convert the poly in outline and holes + + // Note: This two sequencial calls are need in order to get + // the triangulation function to work properly. + polyList.Simplify( SHAPE_POLY_SET::PM_FAST ); + polyList.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + + if( polyList.IsEmpty() ) + return; + + Convert_shape_line_polygon_to_triangles( polyList, + *aDstContainer, + m_biuTo3Dunits, + *aZoneContainer ); + + + // add filled areas outlines, which are drawn with thick lines segments + // ///////////////////////////////////////////////////////////////////////// + for( int i = 0; i < polyList.OutlineCount(); ++i ) + { + // Add outline + const SHAPE_LINE_CHAIN& pathOutline = polyList.COutline( i ); + + for( int j = 0; j < pathOutline.PointCount(); ++j ) + { + const VECTOR2I& a = pathOutline.CPoint( j ); + const VECTOR2I& b = pathOutline.CPoint( j + 1 ); + + SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits ); + SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + (aZoneContainer->GetMinThickness() / 2) * + m_biuTo3Dunits, + *aZoneContainer ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, + aZoneContainer->GetMinThickness() * + m_biuTo3Dunits, + *aZoneContainer ) ); + } + } + + // Add holes (of the poly, ie: the open parts) for this outline + for( int h = 0; h < polyList.HoleCount( i ); ++h ) + { + const SHAPE_LINE_CHAIN& pathHole = polyList.CHole( i, h ); + + for( int j = 0; j < pathHole.PointCount(); j++ ) + { + const VECTOR2I& a = pathHole.CPoint( j ); + const VECTOR2I& b = pathHole.CPoint( j + 1 ); + + SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits ); + SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( + new CFILLEDCIRCLE2D( start3DU, + (aZoneContainer->GetMinThickness() / 2) * + m_biuTo3Dunits, + *aZoneContainer ) ); + } + else + { + aDstContainer->Add( + new CROUNDSEGMENT2D( start3DU, end3DU, + aZoneContainer->GetMinThickness() * + m_biuTo3Dunits, + *aZoneContainer ) ); + } + } + } + } +} + + + +void CINFO3D_VISU::buildPadShapeThickOutlineAsSegments( const D_PAD* aPad, + CGENERICCONTAINER2D *aDstContainer, + int aWidth ) +{ + if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) // Draw a ring + { + const SFVEC2F center3DU( aPad->ShapePos().x * m_biuTo3Dunits, + -aPad->ShapePos().y * m_biuTo3Dunits ); + + const int radius = aPad->GetSize().x / 2; + const float inner_radius = (radius - aWidth / 2) * m_biuTo3Dunits; + const float outter_radius = (radius + aWidth / 2) * m_biuTo3Dunits; + + aDstContainer->Add( new CRING2D( center3DU, + inner_radius, + outter_radius, + *aPad ) ); + + return; + } + + // For other shapes, draw polygon outlines + SHAPE_POLY_SET corners; + + const int segcountforcircle = GetNrSegmentsCircle( glm::min( aPad->GetSize().x, + aPad->GetSize().y) ); + + const double correctionFactor = GetCircleCorrectionFactor( segcountforcircle ); + + aPad->BuildPadShapePolygon( corners, wxSize( 0, 0 ), + // This two factors are only expected to be used if render an oval + segcountforcircle, correctionFactor ); + + + // Add outlines as thick segments in polygon buffer + + const SHAPE_LINE_CHAIN& path = corners.COutline( 0 ); + + for( int j = 0; j < path.PointCount(); j++ ) + { + const VECTOR2I& a = path.CPoint( j ); + const VECTOR2I& b = path.CPoint( j + 1 ); + + SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits ); + SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits ); + + if( Is_segment_a_circle( start3DU, end3DU ) ) + { + aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, + (aWidth / 2) * m_biuTo3Dunits, + *aPad ) ); + } + else + { + aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, + aWidth * m_biuTo3Dunits, + *aPad ) ); + } + } +} diff --git a/3d-viewer/3d_canvas/create_layer_items.cpp b/3d-viewer/3d_canvas/create_layer_items.cpp index 4d46a792e5..a6f5a71594 100644 --- a/3d-viewer/3d_canvas/create_layer_items.cpp +++ b/3d-viewer/3d_canvas/create_layer_items.cpp @@ -54,956 +54,7 @@ #include #include - - -// These variables are parameters used in addTextSegmToContainer. -// But addTextSegmToContainer is a call-back function, -// so we cannot send them as arguments. -static int s_textWidth; -static CGENERICCONTAINER2D *s_dstcontainer = NULL; -static float s_biuTo3Dunits; -static const CBBOX2D *s_boardBBox3DU = NULL; -static const BOARD_ITEM *s_boardItem = NULL; - -// This is a call back function, used by DrawGraphicText to draw the 3D text shape: -void addTextSegmToContainer( int x0, int y0, int xf, int yf ) -{ - wxASSERT( s_boardBBox3DU != NULL ); - wxASSERT( s_dstcontainer != NULL ); - - const SFVEC2F start3DU( x0 * s_biuTo3Dunits, -y0 * s_biuTo3Dunits ); - const SFVEC2F end3DU ( xf * s_biuTo3Dunits, -yf * s_biuTo3Dunits ); - - if( Is_segment_a_circle( start3DU, end3DU ) ) - s_dstcontainer->Add( new CFILLEDCIRCLE2D( start3DU, - s_textWidth * s_biuTo3Dunits, - *s_boardItem) ); - else - s_dstcontainer->Add( new CROUNDSEGMENT2D( start3DU, - end3DU, - s_textWidth * s_biuTo3Dunits, - *s_boardItem ) ); -} - - -// Based on -// void TEXTE_PCB::TransformShapeWithClearanceToPolygonSet -// board_items_to_polygon_shape_transform.cpp -void CINFO3D_VISU::AddShapeWithClearanceToContainer( const TEXTE_PCB* aTextPCB, - CGENERICCONTAINER2D *aDstContainer, - PCB_LAYER_ID aLayerId, - int aClearanceValue ) -{ - wxSize size = aTextPCB->GetTextSize(); - - if( aTextPCB->IsMirrored() ) - size.x = -size.x; - - s_boardItem = (const BOARD_ITEM *)&aTextPCB; - s_dstcontainer = aDstContainer; - s_textWidth = aTextPCB->GetThickness() + ( 2 * aClearanceValue ); - s_biuTo3Dunits = m_biuTo3Dunits; - s_boardBBox3DU = &m_board2dBBox3DU; - - // not actually used, but needed by DrawGraphicText - const COLOR4D dummy_color = COLOR4D::BLACK; - - if( aTextPCB->IsMultilineAllowed() ) - { - wxArrayString strings_list; - wxStringSplit( aTextPCB->GetShownText(), strings_list, '\n' ); - std::vector positions; - positions.reserve( strings_list.Count() ); - aTextPCB->GetPositionsOfLinesOfMultilineText( positions, - strings_list.Count() ); - - for( unsigned ii = 0; ii < strings_list.Count(); ++ii ) - { - wxString txt = strings_list.Item( ii ); - - DrawGraphicText( NULL, NULL, positions[ii], dummy_color, - txt, aTextPCB->GetTextAngle(), size, - aTextPCB->GetHorizJustify(), aTextPCB->GetVertJustify(), - aTextPCB->GetThickness(), aTextPCB->IsItalic(), - true, addTextSegmToContainer ); - } - } - else - { - DrawGraphicText( NULL, NULL, aTextPCB->GetTextPos(), dummy_color, - aTextPCB->GetShownText(), aTextPCB->GetTextAngle(), size, - aTextPCB->GetHorizJustify(), aTextPCB->GetVertJustify(), - aTextPCB->GetThickness(), aTextPCB->IsItalic(), - true, addTextSegmToContainer ); - } -} - - -void CINFO3D_VISU::AddShapeWithClearanceToContainer( const DIMENSION* aDimension, - CGENERICCONTAINER2D *aDstContainer, - PCB_LAYER_ID aLayerId, - int aClearanceValue ) -{ - AddShapeWithClearanceToContainer(&aDimension->Text(), aDstContainer, aLayerId, aClearanceValue); - - const int linewidth = aDimension->GetWidth() + (2 * aClearanceValue); - - std::pair segs[] = { - {&aDimension->m_crossBarO, &aDimension->m_crossBarF}, - {&aDimension->m_featureLineGO, &aDimension->m_featureLineGF}, - {&aDimension->m_featureLineDO, &aDimension->m_featureLineDF}, - {&aDimension->m_crossBarF, &aDimension->m_arrowD1F}, - {&aDimension->m_crossBarF, &aDimension->m_arrowD2F}, - {&aDimension->m_crossBarO, &aDimension->m_arrowG1F}, - {&aDimension->m_crossBarO, &aDimension->m_arrowG2F}}; - - for( auto const & ii : segs ) - { - const SFVEC2F start3DU( ii.first->x * m_biuTo3Dunits, - -ii.first->y * m_biuTo3Dunits ); - - const SFVEC2F end3DU ( ii.second->x * m_biuTo3Dunits, - -ii.second->y * m_biuTo3Dunits ); - - aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, - end3DU, - linewidth * m_biuTo3Dunits, - *aDimension ) ); - } -} - - -// Based on -// void MODULE::TransformGraphicShapesWithClearanceToPolygonSet -// board_items_to_polygon_shape_transform.cpp#L204 -void CINFO3D_VISU::AddGraphicsShapesWithClearanceToContainer( const MODULE* aModule, - CGENERICCONTAINER2D *aDstContainer, - PCB_LAYER_ID aLayerId, - int aInflateValue ) -{ - std::vector texts; // List of TEXTE_MODULE to convert - EDGE_MODULE* outline; - - for( EDA_ITEM* item = aModule->GraphicalItemsList(); - item != NULL; - item = item->Next() ) - { - switch( item->Type() ) - { - case PCB_MODULE_TEXT_T: - { - TEXTE_MODULE* text = static_cast( item ); - - if( text->GetLayer() == aLayerId && text->IsVisible() ) - texts.push_back( text ); - } - break; - - - case PCB_MODULE_EDGE_T: - { - outline = (EDGE_MODULE*) item; - - if( outline->GetLayer() != aLayerId ) - break; - - AddShapeWithClearanceToContainer( (const DRAWSEGMENT *)outline, - aDstContainer, - aLayerId, 0 ); - } - break; - - default: - break; - } - } - - // Convert texts sur modules - if( aModule->Reference().GetLayer() == aLayerId && aModule->Reference().IsVisible() ) - texts.push_back( &aModule->Reference() ); - - if( aModule->Value().GetLayer() == aLayerId && aModule->Value().IsVisible() ) - texts.push_back( &aModule->Value() ); - - s_boardItem = (const BOARD_ITEM *)&aModule->Value(); - s_dstcontainer = aDstContainer; - s_biuTo3Dunits = m_biuTo3Dunits; - s_boardBBox3DU = &m_board2dBBox3DU; - - for( unsigned ii = 0; ii < texts.size(); ++ii ) - { - TEXTE_MODULE *textmod = texts[ii]; - s_textWidth = textmod->GetThickness() + ( 2 * aInflateValue ); - wxSize size = textmod->GetTextSize(); - - if( textmod->IsMirrored() ) - size.x = -size.x; - - DrawGraphicText( NULL, NULL, textmod->GetTextPos(), BLACK, - textmod->GetShownText(), textmod->GetDrawRotation(), size, - textmod->GetHorizJustify(), textmod->GetVertJustify(), - textmod->GetThickness(), textmod->IsItalic(), - true, addTextSegmToContainer ); - } -} - - -COBJECT2D *CINFO3D_VISU::createNewTrack( const TRACK* aTrack, - int aClearanceValue ) const -{ - SFVEC2F start3DU( aTrack->GetStart().x * m_biuTo3Dunits, - -aTrack->GetStart().y * m_biuTo3Dunits ); // y coord is inverted - - switch( aTrack->Type() ) - { - case PCB_VIA_T: - { - const float radius = ( ( aTrack->GetWidth() / 2 ) + aClearanceValue ) * m_biuTo3Dunits; - - return new CFILLEDCIRCLE2D( start3DU, radius, *aTrack ); - } - break; - - default: - { - wxASSERT( aTrack->Type() == PCB_TRACE_T ); - - SFVEC2F end3DU ( aTrack->GetEnd().x * m_biuTo3Dunits, - -aTrack->GetEnd().y * m_biuTo3Dunits ); - - // Cannot add segments that have the same start and end point - if( Is_segment_a_circle( start3DU, end3DU ) ) - { - const float radius = ((aTrack->GetWidth() / 2) + aClearanceValue) * m_biuTo3Dunits; - - return new CFILLEDCIRCLE2D( start3DU, radius, *aTrack ); - } - else - { - const float width = (aTrack->GetWidth() + 2 * aClearanceValue ) * m_biuTo3Dunits; - - return new CROUNDSEGMENT2D( start3DU, end3DU, width, *aTrack ); - } - } - break; - } - - return NULL; -} - - -// Based on: -// void D_PAD:: TransformShapeWithClearanceToPolygon( -// board_items_to_polygon_shape_transform.cpp -void CINFO3D_VISU::createNewPadWithClearance( const D_PAD* aPad, - CGENERICCONTAINER2D *aDstContainer, - int aClearanceValue ) const -{ - const int dx = (aPad->GetSize().x / 2) + aClearanceValue; - const int dy = (aPad->GetSize().y / 2) + aClearanceValue; - - if( !dx || !dy ) - { - wxLogTrace( m_logTrace, - wxT( "CINFO3D_VISU::createNewPadWithClearance - found an invalid pad" ) ); - - return; - } - - wxPoint PadShapePos = aPad->ShapePos(); // Note: for pad having a shape offset, - // the pad position is NOT the shape position - - switch( aPad->GetShape() ) - { - case PAD_SHAPE_CIRCLE: - { - const float radius = dx * m_biuTo3Dunits; - - const SFVEC2F center( PadShapePos.x * m_biuTo3Dunits, - -PadShapePos.y * m_biuTo3Dunits ); - - aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius, *aPad ) ); - } - break; - - case PAD_SHAPE_OVAL: - { - if( abs( dx - dy ) == 0 ) - { - // The segment object cannot store start and end the same position, - // so add a circle instead - const float radius = dx * m_biuTo3Dunits; - - const SFVEC2F center( PadShapePos.x * m_biuTo3Dunits, - -PadShapePos.y * m_biuTo3Dunits ); - - aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius, *aPad ) ); - } - else - { - // An oval pad has the same shape as a segment with rounded ends - - int iwidth; - wxPoint shape_offset = wxPoint( 0, 0 ); - - if( dy > dx ) // Oval pad X/Y ratio for choosing translation axis - { - shape_offset.y = dy - dx; - iwidth = dx * 2; - } - else //if( dy <= dx ) - { - shape_offset.x = dy - dx; - iwidth = dy * 2; - } - - RotatePoint( &shape_offset, aPad->GetOrientation() ); - - const wxPoint start = PadShapePos - shape_offset; - const wxPoint end = PadShapePos + shape_offset; - - const SFVEC2F start3DU( start.x * m_biuTo3Dunits, -start.y * m_biuTo3Dunits ); - const SFVEC2F end3DU ( end.x * m_biuTo3Dunits, -end.y * m_biuTo3Dunits ); - - // Cannot add segments that have the same start and end point - if( Is_segment_a_circle( start3DU, end3DU ) ) - { - aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, - (iwidth / 2) * m_biuTo3Dunits, - *aPad ) ); - } - else - { - aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, - end3DU, - iwidth * m_biuTo3Dunits, - *aPad ) ); - } - } - } - break; - - case PAD_SHAPE_TRAPEZOID: - case PAD_SHAPE_RECT: - { - // https://github.com/KiCad/kicad-source-mirror/blob/0cab3e47ad8097db7b898b3cef2cf9b235318ca3/pcbnew/board_items_to_polygon_shape_transform.cpp#L613 - - wxPoint corners[4]; - aPad->BuildPadPolygon( corners, wxSize( 0, 0), aPad->GetOrientation() ); - - SFVEC2F corners3DU[4]; - - // Note: for pad having a shape offset, - // the pad position is NOT the shape position - for( unsigned int ii = 0; ii < 4; ++ii ) - { - corners[ii] += aPad->ShapePos(); // Shift origin to position - - corners3DU[ii] = SFVEC2F( corners[ii].x * m_biuTo3Dunits, - -corners[ii].y * m_biuTo3Dunits ); - } - - - // Learn more at: - // https://lists.launchpad.net/kicad-developers/msg18729.html - - // Add the PAD polygon - aDstContainer->Add( new CPOLYGON4PTS2D( corners3DU[0], - corners3DU[1], - corners3DU[2], - corners3DU[3], - *aPad ) ); - - // Add the PAD contours - // !TODO: check the corners because it cannot add - // roundsegments that are in the same start and end position - aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[0], - corners3DU[1], - aClearanceValue * 2.0f * m_biuTo3Dunits, - *aPad ) ); - - aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[1], - corners3DU[2], - aClearanceValue * 2.0f * m_biuTo3Dunits, - *aPad ) ); - - aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[2], - corners3DU[3], - aClearanceValue * 2.0f * m_biuTo3Dunits, - *aPad ) ); - - aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[3], - corners3DU[0], - aClearanceValue * 2.0f * m_biuTo3Dunits, - *aPad ) ); - } - break; - - case PAD_SHAPE_ROUNDRECT: - { - const int pad_radius = aPad->GetRoundRectCornerRadius(); - const int rounding_radius = pad_radius + aClearanceValue; - - wxSize shapesize( aPad->GetSize() ); - shapesize.x += aClearanceValue * 2; - shapesize.y += aClearanceValue * 2; - - wxPoint corners[4]; - - GetRoundRectCornerCenters( corners, - rounding_radius, - PadShapePos, - shapesize, - aPad->GetOrientation() ); - - SFVEC2F corners3DU[4]; - - for( unsigned int ii = 0; ii < 4; ++ii ) - corners3DU[ii] = SFVEC2F( corners[ii].x * m_biuTo3Dunits, - -corners[ii].y * m_biuTo3Dunits ); - - // Add the PAD polygon (For some reason the corners need - // to be inverted to display with the correctly orientation) - aDstContainer->Add( new CPOLYGON4PTS2D( corners3DU[0], - corners3DU[3], - corners3DU[2], - corners3DU[1], - *aPad ) ); - - // Add the PAD contours - // !TODO: check the corners because it cannot add - // roundsegments that are in the same start and end position - aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[0], - corners3DU[1], - rounding_radius * 2.0f * m_biuTo3Dunits, - *aPad ) ); - - aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[1], - corners3DU[2], - rounding_radius * 2.0f * m_biuTo3Dunits, - *aPad ) ); - - aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[2], - corners3DU[3], - rounding_radius * 2.0f * m_biuTo3Dunits, - *aPad ) ); - - aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[3], - corners3DU[0], - rounding_radius * 2.0f * m_biuTo3Dunits, - *aPad ) ); - } - break; - - case PAD_SHAPE_CUSTOM: - { - SHAPE_POLY_SET polyList; // Will contain the pad outlines in board coordinates - polyList.Append( aPad->GetCustomShapeAsPolygon() ); - aPad->CustomShapeAsPolygonToBoardPosition( &polyList, aPad->ShapePos(), aPad->GetOrientation() ); - - if( aClearanceValue ) - polyList.Inflate( aClearanceValue, 32 ); - - // This convert the poly in outline and holes - polyList.Simplify( SHAPE_POLY_SET::PM_FAST ); - polyList.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - - // Add the PAD polygon - Convert_shape_line_polygon_to_triangles( polyList, *aDstContainer, m_biuTo3Dunits, *aPad ); - - } - break; - } -} - - -// Based on: -// BuildPadDrillShapePolygon -// board_items_to_polygon_shape_transform.cpp -COBJECT2D *CINFO3D_VISU::createNewPadDrill( const D_PAD* aPad, int aInflateValue ) -{ - wxSize drillSize = aPad->GetDrillSize(); - - if( !drillSize.x || !drillSize.y ) - { - wxLogTrace( m_logTrace, wxT( "CINFO3D_VISU::createNewPadDrill - found an invalid pad" ) ); - return NULL; - } - - if( drillSize.x == drillSize.y ) // usual round hole - { - const int radius = (drillSize.x / 2) + aInflateValue; - - const SFVEC2F center( aPad->GetPosition().x * m_biuTo3Dunits, - -aPad->GetPosition().y * m_biuTo3Dunits ); - - return new CFILLEDCIRCLE2D( center, radius * m_biuTo3Dunits, *aPad ); - - } - else // Oblong hole - { - wxPoint start, end; - int width; - - aPad->GetOblongDrillGeometry( start, end, width ); - - width += aInflateValue * 2; - start += aPad->GetPosition(); - end += aPad->GetPosition(); - - SFVEC2F start3DU( start.x * m_biuTo3Dunits, - -start.y * m_biuTo3Dunits ); - - SFVEC2F end3DU ( end.x * m_biuTo3Dunits, - -end.y * m_biuTo3Dunits ); - - if( Is_segment_a_circle( start3DU, end3DU ) ) - { - return new CFILLEDCIRCLE2D( start3DU, (width / 2) * m_biuTo3Dunits, *aPad ); - } - else - { - return new CROUNDSEGMENT2D( start3DU, end3DU, width * m_biuTo3Dunits, *aPad ); - } - } - - return NULL; -} - - -// This function pretends to be like the -// void D_PAD::BuildPadShapePolygon( -// board_items_to_polygon_shape_transform.cpp -void CINFO3D_VISU::createNewPad( const D_PAD* aPad, - CGENERICCONTAINER2D *aDstContainer, - const wxSize &aInflateValue ) const -{ - switch( aPad->GetShape() ) - { - case PAD_SHAPE_CIRCLE: - case PAD_SHAPE_OVAL: - case PAD_SHAPE_ROUNDRECT: - case PAD_SHAPE_CUSTOM: - createNewPadWithClearance( aPad, aDstContainer, aInflateValue.x ); - break; - - case PAD_SHAPE_TRAPEZOID: - case PAD_SHAPE_RECT: - wxPoint corners[4]; - aPad->BuildPadPolygon( corners, aInflateValue, aPad->GetOrientation() ); - - // Note: for pad having a shape offset, - // the pad position is NOT the shape position - for( unsigned int ii = 0; ii < 4; ++ii ) - corners[ii] += aPad->ShapePos(); // Shift origin to position - - aDstContainer->Add( new CPOLYGON4PTS2D( - SFVEC2F( corners[0].x * m_biuTo3Dunits, - -corners[0].y * m_biuTo3Dunits ), - SFVEC2F( corners[1].x * m_biuTo3Dunits, - -corners[1].y * m_biuTo3Dunits ), - SFVEC2F( corners[2].x * m_biuTo3Dunits, - -corners[2].y * m_biuTo3Dunits ), - SFVEC2F( corners[3].x * m_biuTo3Dunits, - -corners[3].y * m_biuTo3Dunits ), - *aPad ) ); - - break; - } -} - - -void CINFO3D_VISU::AddPadsShapesWithClearanceToContainer( const MODULE* aModule, - CGENERICCONTAINER2D *aDstContainer, - PCB_LAYER_ID aLayerId, - int aInflateValue, - bool aSkipNPTHPadsWihNoCopper ) -{ - const D_PAD* pad = aModule->PadsList(); - - wxSize margin; - - for( ; pad != NULL; pad = pad->Next() ) - { - if( !pad->IsOnLayer( aLayerId ) ) - continue; - - // NPTH pads are not drawn on layers if the - // shape size and pos is the same as their hole: - if( aSkipNPTHPadsWihNoCopper && (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED) ) - { - if( (pad->GetDrillSize() == pad->GetSize()) && - (pad->GetOffset() == wxPoint( 0, 0 )) ) - { - switch( pad->GetShape() ) - { - case PAD_SHAPE_CIRCLE: - if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ) - continue; - break; - - case PAD_SHAPE_OVAL: - if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE ) - continue; - break; - - default: - break; - } - } - } - - switch( aLayerId ) - { - case F_Mask: - case B_Mask: - margin.x = margin.y = pad->GetSolderMaskMargin() + aInflateValue; - break; - - case F_Paste: - case B_Paste: - margin = pad->GetSolderPasteMargin(); - margin.x += aInflateValue; - margin.y += aInflateValue; - break; - - default: - margin.x = margin.y = aInflateValue; - break; - } - - createNewPad( pad, aDstContainer, margin ); - } -} - -// based on TransformArcToPolygon function from -// common/convert_basic_shapes_to_polygon.cpp -void CINFO3D_VISU::TransformArcToSegments( const wxPoint &aCentre, - const wxPoint &aStart, - double aArcAngle, - int aCircleToSegmentsCount, - int aWidth, - CGENERICCONTAINER2D *aDstContainer, - const BOARD_ITEM &aBoardItem ) -{ - wxPoint arc_start, arc_end; - int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree - - arc_end = arc_start = aStart; - - if( aArcAngle != 3600 ) - { - RotatePoint( &arc_end, aCentre, -aArcAngle ); - } - - if( aArcAngle < 0 ) - { - std::swap( arc_start, arc_end ); - aArcAngle = -aArcAngle; - } - - // Compute the ends of segments and creates poly - wxPoint curr_end = arc_start; - wxPoint curr_start = arc_start; - - for( int ii = delta; ii < aArcAngle; ii += delta ) - { - curr_end = arc_start; - RotatePoint( &curr_end, aCentre, -ii ); - - const SFVEC2F start3DU( curr_start.x * m_biuTo3Dunits, -curr_start.y * m_biuTo3Dunits ); - const SFVEC2F end3DU ( curr_end.x * m_biuTo3Dunits, -curr_end.y * m_biuTo3Dunits ); - - if( Is_segment_a_circle( start3DU, end3DU ) ) - { - aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, - ( aWidth / 2 ) * m_biuTo3Dunits, - aBoardItem ) ); - } - else - { - aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, - end3DU, - aWidth * m_biuTo3Dunits, - aBoardItem ) ); - } - - curr_start = curr_end; - } - - if( curr_end != arc_end ) - { - const SFVEC2F start3DU( curr_end.x * m_biuTo3Dunits, -curr_end.y * m_biuTo3Dunits ); - const SFVEC2F end3DU ( arc_end.x * m_biuTo3Dunits, -arc_end.y * m_biuTo3Dunits ); - - if( Is_segment_a_circle( start3DU, end3DU ) ) - { - aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, - ( aWidth / 2 ) * m_biuTo3Dunits, - aBoardItem ) ); - } - else - { - aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, - end3DU, - aWidth * m_biuTo3Dunits, - aBoardItem ) ); - } - } -} - -// Based on -// TransformShapeWithClearanceToPolygon -// board_items_to_polygon_shape_transform.cpp#L431 -void CINFO3D_VISU::AddShapeWithClearanceToContainer( const DRAWSEGMENT* aDrawSegment, - CGENERICCONTAINER2D *aDstContainer, - PCB_LAYER_ID aLayerId, - int aClearanceValue ) -{ - // The full width of the lines to create: - const int linewidth = aDrawSegment->GetWidth() + (2 * aClearanceValue); - - switch( aDrawSegment->GetShape() ) - { - case S_CIRCLE: - { - const SFVEC2F center3DU( aDrawSegment->GetCenter().x * m_biuTo3Dunits, - -aDrawSegment->GetCenter().y * m_biuTo3Dunits ); - - const float inner_radius = (aDrawSegment->GetRadius() - linewidth / 2) * m_biuTo3Dunits; - const float outter_radius = (aDrawSegment->GetRadius() + linewidth / 2) * m_biuTo3Dunits; - - aDstContainer->Add( new CRING2D( center3DU, - inner_radius, - outter_radius, - *aDrawSegment ) ); - } - break; - - case S_ARC: - { - const unsigned int nr_segments = - GetNrSegmentsCircle( aDrawSegment->GetBoundingBox().GetSizeMax() ); - - TransformArcToSegments( aDrawSegment->GetCenter(), - aDrawSegment->GetArcStart(), - aDrawSegment->GetAngle(), - nr_segments, - aDrawSegment->GetWidth(), - aDstContainer, - *aDrawSegment ); - } - break; - - case S_SEGMENT: - { - const SFVEC2F start3DU( aDrawSegment->GetStart().x * m_biuTo3Dunits, - -aDrawSegment->GetStart().y * m_biuTo3Dunits ); - - const SFVEC2F end3DU ( aDrawSegment->GetEnd().x * m_biuTo3Dunits, - -aDrawSegment->GetEnd().y * m_biuTo3Dunits ); - - if( Is_segment_a_circle( start3DU, end3DU ) ) - { - aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, - ( linewidth / 2 ) * m_biuTo3Dunits, - *aDrawSegment ) ); - } - else - { - aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, - end3DU, - linewidth * m_biuTo3Dunits, - *aDrawSegment ) ); - } - } - break; - - case S_POLYGON: - { - const int segcountforcircle = 16; - const double correctionFactor = GetCircleCorrectionFactor( segcountforcircle ); - SHAPE_POLY_SET polyList; - - aDrawSegment->TransformShapeWithClearanceToPolygon( polyList, aClearanceValue, - segcountforcircle, correctionFactor ); - // This convert the poly in outline and holes - // Note: This two sequencial calls are need in order to get - // the triangulation function to work properly. - polyList.Simplify( SHAPE_POLY_SET::PM_FAST ); - polyList.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - - if( polyList.IsEmpty() ) // Just for caution - break; - - Convert_shape_line_polygon_to_triangles( polyList, *aDstContainer, - m_biuTo3Dunits, *aDrawSegment ); - } - break; - - case S_CURVE: // Bezier curve (not yet in use in KiCad) - break; - - default: - break; - } -} - - -// Based on -// TransformSolidAreasShapesToPolygonSet -// board_items_to_polygon_shape_transform.cpp -void CINFO3D_VISU::AddSolidAreasShapesToContainer( const ZONE_CONTAINER* aZoneContainer, - CGENERICCONTAINER2D *aDstContainer, - PCB_LAYER_ID aLayerId ) -{ - // Copy the polys list because we have to simplify it - SHAPE_POLY_SET polyList = SHAPE_POLY_SET(aZoneContainer->GetFilledPolysList()); - - // This convert the poly in outline and holes - - // Note: This two sequencial calls are need in order to get - // the triangulation function to work properly. - polyList.Simplify( SHAPE_POLY_SET::PM_FAST ); - polyList.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - - if( polyList.IsEmpty() ) - return; - - Convert_shape_line_polygon_to_triangles( polyList, - *aDstContainer, - m_biuTo3Dunits, - *aZoneContainer ); - - - // add filled areas outlines, which are drawn with thick lines segments - // ///////////////////////////////////////////////////////////////////////// - for( int i = 0; i < polyList.OutlineCount(); ++i ) - { - // Add outline - const SHAPE_LINE_CHAIN& pathOutline = polyList.COutline( i ); - - for( int j = 0; j < pathOutline.PointCount(); ++j ) - { - const VECTOR2I& a = pathOutline.CPoint( j ); - const VECTOR2I& b = pathOutline.CPoint( j + 1 ); - - SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits ); - SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits ); - - if( Is_segment_a_circle( start3DU, end3DU ) ) - { - aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, - (aZoneContainer->GetMinThickness() / 2) * - m_biuTo3Dunits, - *aZoneContainer ) ); - } - else - { - aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, - aZoneContainer->GetMinThickness() * - m_biuTo3Dunits, - *aZoneContainer ) ); - } - } - - // Add holes (of the poly, ie: the open parts) for this outline - for( int h = 0; h < polyList.HoleCount( i ); ++h ) - { - const SHAPE_LINE_CHAIN& pathHole = polyList.CHole( i, h ); - - for( int j = 0; j < pathHole.PointCount(); j++ ) - { - const VECTOR2I& a = pathHole.CPoint( j ); - const VECTOR2I& b = pathHole.CPoint( j + 1 ); - - SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits ); - SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits ); - - if( Is_segment_a_circle( start3DU, end3DU ) ) - { - aDstContainer->Add( - new CFILLEDCIRCLE2D( start3DU, - (aZoneContainer->GetMinThickness() / 2) * - m_biuTo3Dunits, - *aZoneContainer ) ); - } - else - { - aDstContainer->Add( - new CROUNDSEGMENT2D( start3DU, end3DU, - aZoneContainer->GetMinThickness() * - m_biuTo3Dunits, - *aZoneContainer ) ); - } - } - } - } -} - - - -void CINFO3D_VISU::buildPadShapeThickOutlineAsSegments( const D_PAD* aPad, - CGENERICCONTAINER2D *aDstContainer, - int aWidth ) -{ - if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) // Draw a ring - { - const SFVEC2F center3DU( aPad->ShapePos().x * m_biuTo3Dunits, - -aPad->ShapePos().y * m_biuTo3Dunits ); - - const int radius = aPad->GetSize().x / 2; - const float inner_radius = (radius - aWidth / 2) * m_biuTo3Dunits; - const float outter_radius = (radius + aWidth / 2) * m_biuTo3Dunits; - - aDstContainer->Add( new CRING2D( center3DU, - inner_radius, - outter_radius, - *aPad ) ); - - return; - } - - // For other shapes, draw polygon outlines - SHAPE_POLY_SET corners; - - const int segcountforcircle = GetNrSegmentsCircle( glm::min( aPad->GetSize().x, - aPad->GetSize().y) ); - - const double correctionFactor = GetCircleCorrectionFactor( segcountforcircle ); - - aPad->BuildPadShapePolygon( corners, wxSize( 0, 0 ), - // This two factors are only expected to be used if render an oval - segcountforcircle, correctionFactor ); - - - // Add outlines as thick segments in polygon buffer - - const SHAPE_LINE_CHAIN& path = corners.COutline( 0 ); - - for( int j = 0; j < path.PointCount(); j++ ) - { - const VECTOR2I& a = path.CPoint( j ); - const VECTOR2I& b = path.CPoint( j + 1 ); - - SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits ); - SFVEC2F end3DU ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits ); - - if( Is_segment_a_circle( start3DU, end3DU ) ) - { - aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, - (aWidth / 2) * m_biuTo3Dunits, - *aPad ) ); - } - else - { - aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, - aWidth * m_biuTo3Dunits, - *aPad ) ); - } - } -} - +#include void CINFO3D_VISU::destroyLayers() { @@ -1589,9 +640,7 @@ void CINFO3D_VISU::createLayers( REPORTER *aStatusTextReporter ) SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id]; // ADD PADS - for( const MODULE* module = m_board->m_Modules; - module; - module = module->Next() ) + for( const MODULE* module = m_board->m_Modules; module; module = module->Next() ) { // Construct polys // ///////////////////////////////////////////////////////////// @@ -1812,7 +861,7 @@ void CINFO3D_VISU::createLayers( REPORTER *aStatusTextReporter ) // ///////////////////////////////////////////////////////////////////////// if( aStatusTextReporter ) - aStatusTextReporter->Report( _( "Simplifying polygons" ) ); + aStatusTextReporter->Report( _( "Simplifying copper layers polygons" ) ); if( GetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS ) && (m_render_engine == RENDER_ENGINE_OPENGL_LEGACY) ) @@ -1881,7 +930,6 @@ void CINFO3D_VISU::createLayers( REPORTER *aStatusTextReporter ) unsigned stats_endCopperLayersTime = GetRunningMicroSecs(); #endif - // Build Tech layers // Based on: https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L1059 // ///////////////////////////////////////////////////////////////////////// @@ -1913,6 +961,7 @@ void CINFO3D_VISU::createLayers( REPORTER *aStatusTextReporter ) }; // User layers are not drawn here, only technical layers + for( LSEQ seq = LSET::AllNonCuMask().Seq( teckLayerList, DIM( teckLayerList ) ); seq; ++seq ) @@ -2102,7 +1151,7 @@ void CINFO3D_VISU::createLayers( REPORTER *aStatusTextReporter ) } } - // This will make a union of all added contourns + // This will make a union of all added contours layerPoly->Simplify( SHAPE_POLY_SET::PM_FAST ); } // End Build Tech layers @@ -2118,6 +1167,8 @@ void CINFO3D_VISU::createLayers( REPORTER *aStatusTextReporter ) #ifdef PRINT_STATISTICS_3D_VIEWER unsigned stats_startHolesBVHTime = GetRunningMicroSecs(); #endif + if( aStatusTextReporter ) + aStatusTextReporter->Report( _( "Build BVH for holes and vias" ) ); m_through_holes_inner.BuildBVH(); m_through_holes_outer.BuildBVH(); diff --git a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp index ec48bd8a5b..cdc25a8b69 100644 --- a/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp +++ b/3d-viewer/3d_rendering/3d_render_ogl_legacy/c3d_render_createscene_ogl_legacy.cpp @@ -562,6 +562,7 @@ void C3D_RENDER_OGL_LEGACY::reload( REPORTER *aStatusTextReporter ) m_triangles[layer_id] = layerTriangles; + // Load the 2D (X,Y axis) component of shapes for( LIST_OBJECT2D::const_iterator itemOnLayer = listObject2d.begin(); itemOnLayer != listObject2d.end(); ++itemOnLayer ) @@ -608,6 +609,7 @@ void C3D_RENDER_OGL_LEGACY::reload( REPORTER *aStatusTextReporter ) const MAP_POLY &map_poly = m_settings.GetPolyMap(); + // Load the vertical (Z axis) component of shapes if( map_poly.find( layer_id ) != map_poly.end() ) { const SHAPE_POLY_SET *polyList = map_poly.at( layer_id ); diff --git a/3d-viewer/CMakeLists.txt b/3d-viewer/CMakeLists.txt index c2ba198f98..c8f2bd02a1 100644 --- a/3d-viewer/CMakeLists.txt +++ b/3d-viewer/CMakeLists.txt @@ -47,6 +47,7 @@ set(3D-VIEWER_SRCS ../polygon/poly2tri/sweep/sweep_context.cc 3d_canvas/cinfo3d_visu.cpp 3d_canvas/create_layer_items.cpp + 3d_canvas/create_3Dgraphic_brd_items.cpp 3d_canvas/create_layer_poly.cpp 3d_canvas/eda_3d_canvas.cpp 3d_canvas/eda_3d_canvas_pivot.cpp