/** * Draw labelled ticks on a line. Ticks are spaced according to a * maximum density. Miror ticks are not labelled. * * @param aGal the GAL to draw on * @param aOrigin start of line to draw ticks on * @param aLine line vector * @param aMinorTickLen length of minor ticks in IU */ void drawTicksAlongLine( KIGFX::GAL& aGal, const VECTOR2D& aOrigin, const VECTOR2D& aLine, double aMinorTickLen ) { VECTOR2D tickLine = aLine.Rotate( -M_PI_2 ); double tickSpace; TICK_FORMAT tickF = getTickFormatForScale( aGal.GetWorldScale(), tickSpace ); // number of ticks in whole ruler int numTicks = (int) std::ceil( aLine.EuclideanNorm() / tickSpace ); // work out which way up the tick labels go double labelAngle = -tickLine.Angle(); if( aLine.Angle() > 0 ) { aGal.SetHorizontalJustify( GR_TEXT_HJUSTIFY_LEFT ); } else { aGal.SetHorizontalJustify( GR_TEXT_HJUSTIFY_RIGHT ); labelAngle += M_PI; } // text and ticks are dimmed aGal.SetStrokeColor( PreviewOverlayDefaultColor().WithAlpha( PreviewOverlayDeemphAlpha( true ) ) ); const auto labelOffset = tickLine.Resize( aMinorTickLen * ( majorTickLengthFactor + 1 ) ); for( int i = 0; i < numTicks; ++i ) { const auto tickPos = aOrigin + aLine.Resize( tickSpace * i ); double length = aMinorTickLen; bool drawLabel = false; if( i % tickF.majorStep == 0) { drawLabel = true; length *= majorTickLengthFactor; } else if( tickF.midStep && i % tickF.midStep == 0 ) { drawLabel = true; length *= midTickLengthFactor; } aGal.DrawLine( tickPos, tickPos + tickLine.Resize( length ) ); if( drawLabel ) { wxString label = DimensionLabel( "", tickSpace * i, g_UserUnit ); // FIXME: spaces choke OpenGL lp:1668455 label.erase( std::remove( label.begin(), label.end(), ' ' ), label.end() ); aGal.BitmapText( label, tickPos + labelOffset, labelAngle ); } } }
SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::circleQuad( VECTOR2D aP, VECTOR2D aDir, bool aSide ) { SHAPE_LINE_CHAIN lc; if( aDir.EuclideanNorm( ) == 0.0f ) { lc.Append( aP ); return lc; } VECTOR2D dir_u( aDir ); VECTOR2D dir_v( aDir.Perpendicular( ) ); const int ArcSegments = Settings().m_cornerArcSegments; for( int i = ArcSegments - 1; i >= 0; i-- ) { VECTOR2D p; double alpha = (double) i / (double) ( ArcSegments - 1 ) * M_PI / 2.0; p = aP + dir_u * cos( alpha ) + dir_v * ( aSide ? -1.0 : 1.0 ) * ( 1.0 - sin( alpha ) ); lc.Append( ( int ) p.x, ( int ) p.y ); } return lc; }
SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::circleQuad( VECTOR2D aP, VECTOR2D aDir, bool aSide ) { SHAPE_LINE_CHAIN lc; if( aDir.EuclideanNorm( ) == 0.0f ) { lc.Append( aP ); return lc; } VECTOR2D dir_u( aDir ); VECTOR2D dir_v( aDir.Perpendicular( ) ); const int ArcSegments = Settings().m_cornerArcSegments; double radius = (double) aDir.EuclideanNorm(); double angleStep = M_PI / 2.0 / (double) ArcSegments; double correction = 12.0 * radius * ( 1.0 - cos( angleStep / 2.0 ) ); if( !m_dual ) correction = 0.0; else if( radius < m_meanCornerRadius ) correction = 0.0; VECTOR2D p = aP; lc.Append( ( int ) p.x, ( int ) p.y ); VECTOR2D dir_uu = dir_u.Resize( radius - correction ); VECTOR2D dir_vv = dir_v.Resize( radius - correction ); VECTOR2D shift = dir_u.Resize( correction ); for( int i = ArcSegments - 1; i >= 0; i-- ) { double alpha = (double) i / (double) ( ArcSegments - 1 ) * M_PI / 2.0; p = aP + shift + dir_uu * cos( alpha ) + dir_vv * ( aSide ? -1.0 : 1.0 ) * ( 1.0 - sin( alpha ) ); lc.Append( ( int ) p.x, ( int ) p.y ); } p = aP + dir_u + dir_v * ( aSide ? -1.0 : 1.0 ); lc.Append( ( int ) p.x, ( int ) p.y ); return lc; }
/** * Draw simple ticks on the back of a line such that the line is * divided into n parts. * * @param aGal the GAL to draw on * @param aOrigin start of line to draw ticks on * @param aLine line vector * @param aTickLen length of ticks in IU * @param aNumDivisions number of parts to divide the line into */ void drawBacksideTicks( KIGFX::GAL& aGal, const VECTOR2D& aOrigin, const VECTOR2D& aLine, double aTickLen, int aNumDivisions ) { const double backTickSpace = aLine.EuclideanNorm() / aNumDivisions; const auto backTickVec = aLine.Rotate( M_PI_2 ).Resize( aTickLen ); for( int i = 0; i < aNumDivisions + 1; ++i ) { const auto backTickPos = aOrigin + aLine.Resize( backTickSpace * i ); aGal.DrawLine( backTickPos, backTickPos + backTickVec ); } }
void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) { /* Helper drawing: ____--- v3 ^ * ____---- ... \ \ * ____---- ... \ end \ * v1 ____---- ... ____---- \ width * ---- ...___---- \ \ * \ ___...-- \ v * \ ____----... ____---- v2 * ---- ... ____---- * start \ ... ____---- * \... ____---- * ---- * v0 * dots mark triangles' hypotenuses */ VECTOR2D startEndVector = aEndPoint - aStartPoint; double lineLength = startEndVector.EuclideanNorm(); if( lineLength <= 0.0 ) return; double scale = 0.5 * lineWidth / lineLength; // The perpendicular vector also needs transformations glm::vec4 vector = currentManager->GetTransformation() * glm::vec4( -startEndVector.y * scale, startEndVector.x * scale, 0.0, 0.0 ); // Line width is maintained by the vertex shader currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth ); currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth ); // v0 currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth ); currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth ); // v1 currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth ); currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth ); // v3 currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth ); currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth ); // v0 currentManager->Shader( SHADER_LINE, -vector.x, -vector.y, lineWidth ); currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth ); // v3 currentManager->Shader( SHADER_LINE, vector.x, vector.y, lineWidth ); currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth ); // v2 }
void OPENGL_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint, double aWidth ) { VECTOR2D startEndVector = aEndPoint - aStartPoint; double lineAngle = startEndVector.Angle(); if( isFillEnabled ) { // Filled tracks currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); SetLineWidth( aWidth ); drawLineQuad( aStartPoint, aEndPoint ); // Draw line caps drawFilledSemiCircle( aStartPoint, aWidth / 2, lineAngle + M_PI / 2 ); drawFilledSemiCircle( aEndPoint, aWidth / 2, lineAngle - M_PI / 2 ); } else { // Outlined tracks double lineLength = startEndVector.EuclideanNorm(); currentManager->Color( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); Save(); currentManager->Translate( aStartPoint.x, aStartPoint.y, 0.0 ); currentManager->Rotate( lineAngle, 0.0f, 0.0f, 1.0f ); drawLineQuad( VECTOR2D( 0.0, aWidth / 2.0 ), VECTOR2D( lineLength, aWidth / 2.0 ) ); drawLineQuad( VECTOR2D( 0.0, -aWidth / 2.0 ), VECTOR2D( lineLength, -aWidth / 2.0 ) ); // Draw line caps drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), aWidth / 2, M_PI / 2 ); drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), aWidth / 2, -M_PI / 2 ); Restore(); } }
static void drawCursorStrings( KIGFX::GAL& aGal, const VECTOR2D& aCursor, const VECTOR2D& aRulerVec ) { // draw the cursor labels std::vector<wxString> cursorStrings; cursorStrings.push_back( DimensionLabel( "r", aRulerVec.EuclideanNorm(), g_UserUnit ) ); double degs = RAD2DECIDEG( -aRulerVec.Angle() ); cursorStrings.push_back( DimensionLabel( "θ", degs, DEGREES ) ); for( auto& str: cursorStrings ) { // FIXME: remove spaces that choke OpenGL lp:1668455 str.erase( std::remove( str.begin(), str.end(), ' ' ), str.end() ); } auto temp = aRulerVec; DrawTextNextToCursor( aGal, aCursor, -temp, cursorStrings ); }
void CAIRO_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint, double aWidth ) { if( isFillEnabled ) { // Filled tracks mode SetLineWidth( aWidth ); cairo_move_to( currentContext, (double) aStartPoint.x, (double) aStartPoint.y ); cairo_line_to( currentContext, (double) aEndPoint.x, (double) aEndPoint.y ); } else { // Outline mode for tracks VECTOR2D startEndVector = aEndPoint - aStartPoint; double lineAngle = atan2( startEndVector.y, startEndVector.x ); double lineLength = startEndVector.EuclideanNorm(); cairo_save( currentContext ); cairo_translate( currentContext, aStartPoint.x, aStartPoint.y ); cairo_rotate( currentContext, lineAngle ); cairo_arc( currentContext, 0.0, 0.0, aWidth / 2.0, M_PI / 2.0, 3.0 * M_PI / 2.0 ); cairo_arc( currentContext, lineLength, 0.0, aWidth / 2.0, -M_PI / 2.0, M_PI / 2.0 ); cairo_move_to( currentContext, 0.0, aWidth / 2.0 ); cairo_line_to( currentContext, lineLength, aWidth / 2.0 ); cairo_move_to( currentContext, 0.0, -aWidth / 2.0 ); cairo_line_to( currentContext, lineLength, -aWidth / 2.0 ); cairo_restore( currentContext ); } isElementAdded = true; }
void PCB_PAINTER::draw( const TRACK* aTrack, int aLayer ) { VECTOR2D start( aTrack->GetStart() ); VECTOR2D end( aTrack->GetEnd() ); int width = aTrack->GetWidth(); if( m_pcbSettings.m_netNamesOnTracks && IsNetnameLayer( aLayer ) ) { // If there is a net name - display it on the track if( aTrack->GetNetCode() > NETINFO_LIST::UNCONNECTED ) { VECTOR2D line = ( end - start ); double length = line.EuclideanNorm(); // Check if the track is long enough to have a netname displayed if( length < 10 * width ) return; const wxString& netName = aTrack->GetShortNetname(); VECTOR2D textPosition = start + line / 2.0; // center of the track double textOrientation = -atan( line.y / line.x ); double textSize = std::min( static_cast<double>( width ), length / netName.length() ); // Set a proper color for the label const COLOR4D& color = m_pcbSettings.GetColor( aTrack, aTrack->GetLayer() ); COLOR4D labelColor = m_pcbSettings.GetColor( NULL, aLayer ); if( color.GetBrightness() > 0.5 ) m_gal->SetStrokeColor( labelColor.Inverted() ); else m_gal->SetStrokeColor( labelColor ); m_gal->SetLineWidth( width / 10.0 ); m_gal->SetBold( false ); m_gal->SetItalic( false ); m_gal->SetMirrored( false ); m_gal->SetGlyphSize( VECTOR2D( textSize * 0.7, textSize * 0.7 ) ); m_gal->SetHorizontalJustify( GR_TEXT_HJUSTIFY_CENTER ); m_gal->SetVerticalJustify( GR_TEXT_VJUSTIFY_CENTER ); m_gal->StrokeText( netName, textPosition, textOrientation ); } } else if( IsCopperLayer( aLayer ) ) { // Draw a regular track const COLOR4D& color = m_pcbSettings.GetColor( aTrack, aLayer ); m_gal->SetStrokeColor( color ); m_gal->SetIsStroke( true ); if( m_pcbSettings.m_sketchMode[TRACKS_VISIBLE] ) { // Outline mode m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); m_gal->SetIsFill( false ); } else { // Filled mode m_gal->SetFillColor( color ); m_gal->SetIsFill( true ); } m_gal->DrawSegment( start, end, width ); } }
void PCB_PAINTER::draw( const TRACK* aTrack, int aLayer ) { VECTOR2D start( aTrack->GetStart() ); VECTOR2D end( aTrack->GetEnd() ); int width = aTrack->GetWidth(); if( m_pcbSettings.m_netNamesOnTracks && IsNetnameLayer( aLayer ) ) { // If there is a net name - display it on the track if( aTrack->GetNetCode() > NETINFO_LIST::UNCONNECTED ) { VECTOR2D line = ( end - start ); double length = line.EuclideanNorm(); // Check if the track is long enough to have a netname displayed if( length < 10 * width ) return; const wxString& netName = aTrack->GetShortNetname(); VECTOR2D textPosition = start + line / 2.0; // center of the track double textOrientation; if( end.y == start.y ) // horizontal textOrientation = 0; else if( end.x == start.x ) // vertical textOrientation = M_PI / 2; else textOrientation = -atan( line.y / line.x ); double textSize = width; m_gal->SetIsStroke( true ); m_gal->SetIsFill( false ); m_gal->SetStrokeColor( m_pcbSettings.GetColor( NULL, aLayer ) ); m_gal->SetLineWidth( width / 10.0 ); m_gal->SetFontBold( false ); m_gal->SetFontItalic( false ); m_gal->SetTextMirrored( false ); m_gal->SetGlyphSize( VECTOR2D( textSize * 0.7, textSize * 0.7 ) ); m_gal->SetHorizontalJustify( GR_TEXT_HJUSTIFY_CENTER ); m_gal->SetVerticalJustify( GR_TEXT_VJUSTIFY_CENTER ); m_gal->BitmapText( netName, textPosition, textOrientation ); } } else if( IsCopperLayer( aLayer ) ) { // Draw a regular track const COLOR4D& color = m_pcbSettings.GetColor( aTrack, aLayer ); bool outline_mode = m_pcbSettings.m_sketchMode[LAYER_TRACKS]; m_gal->SetStrokeColor( color ); m_gal->SetFillColor( color ); m_gal->SetIsStroke( outline_mode ); m_gal->SetIsFill( not outline_mode ); m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); m_gal->DrawSegment( start, end, width ); // Clearance lines constexpr int clearanceFlags = PCB_RENDER_SETTINGS::CL_EXISTING | PCB_RENDER_SETTINGS::CL_TRACKS; if( ( m_pcbSettings.m_clearance & clearanceFlags ) == clearanceFlags ) { m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); m_gal->SetIsFill( false ); m_gal->SetIsStroke( true ); m_gal->SetStrokeColor( color ); m_gal->DrawSegment( start, end, width + aTrack->GetClearance() * 2 ); } } }