/**
 * In a contiguous list of wires, remove wires that backtrack over the previous
 * wire. Example:
 *
 * Wire is added:
 * ---------------------------------------->
 *
 * A second wire backtracks over it:
 * -------------------<====================>
 *
 * RemoveBacktracks is called:
 * ------------------->
 */
static void RemoveBacktracks( DLIST<SCH_ITEM>& aWires )
{
    SCH_LINE* last_line = NULL;

    EDA_ITEM* first = aWires.GetFirst();
    for( EDA_ITEM* p = first; p; )
    {
        SCH_LINE *line = dynamic_cast<SCH_LINE*>( p );
        if( !line )
        {
            wxFAIL_MSG( "RemoveBacktracks() requires SCH_LINE items" );
            break;
        }
        p = line->Next();

        if( last_line )
        {
            wxASSERT_MSG( last_line->GetEndPoint() == line->GetStartPoint(),
                    "RemoveBacktracks() requires contiguous lines" );
            if( IsPointOnSegment( last_line->GetStartPoint(), line->GetStartPoint(),
                        line->GetEndPoint() ) )
            {
                last_line->SetEndPoint( line->GetEndPoint() );
                delete s_wires.Remove( line );
            }
            else
                last_line = line;
        }
        else
            last_line = line;
    }
}
void SCH_EDIT_FRAME::CheckListConnections( PICKED_ITEMS_LIST& aItemsList, bool aAppend )
{
    std::vector< wxPoint > pts;
    std::vector< wxPoint > connections;

    GetSchematicConnections( connections );
    for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
    {
        SCH_ITEM* item = (SCH_ITEM*) aItemsList.GetPickedItem( ii );
        std::vector< wxPoint > new_pts;

        if( !item->IsConnectable() )
            continue;

        item->GetConnectionPoints( new_pts );
        pts.insert( pts.end(), new_pts.begin(), new_pts.end() );

        // If the item is a line, we also add any connection points from the rest of the schematic
        // that terminate on the line after it is moved.
        if( item->Type() == SCH_LINE_T )
        {
            SCH_LINE* line = (SCH_LINE*) item;
            for( auto i : connections )
                if( IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), i ) )
                    pts.push_back( i );
        }
        else
        {
            // Clean up any wires that short non-wire connections in the list
            for( auto point = new_pts.begin(); point != new_pts.end(); point++ )
            {
                for( auto second_point = point + 1; second_point != new_pts.end(); second_point++ )
                {
                    aAppend |= TrimWire( *point, *second_point, aAppend );
                }
            }
        }
    }

    // We always have some overlapping connection points.  Drop duplicates here
    std::sort( pts.begin(), pts.end(),
            []( const wxPoint& a, const wxPoint& b ) -> bool
            { return a.x < b.x || (a.x == b.x && a.y < b.y); } );
    pts.erase( unique( pts.begin(), pts.end() ), pts.end() );

    for( auto point : pts )
    {
        if( GetScreen()->IsJunctionNeeded( point, true ) )
        {
            AddJunction( point, aAppend );
            aAppend = true;
        }
    }
}
void SCH_SCREEN::MarkConnections( SCH_LINE* aSegment )
{
    wxCHECK_RET( (aSegment) && (aSegment->Type() == SCH_LINE_T),
                 wxT( "Invalid object pointer." ) );

    for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() )
    {
        if( item->GetFlags() & CANDIDATE )
            continue;

        if( item->Type() == SCH_JUNCTION_T )
        {
            SCH_JUNCTION* junction = (SCH_JUNCTION*) item;

            if( aSegment->IsEndPoint( junction->GetPosition() ) )
                item->SetFlags( CANDIDATE );

            continue;
        }

        if( item->Type() != SCH_LINE_T )
            continue;

        SCH_LINE* segment = (SCH_LINE*) item;

        if( aSegment->IsEndPoint( segment->GetStartPoint() )
            && !GetPin( segment->GetStartPoint(), NULL, true ) )
        {
            item->SetFlags( CANDIDATE );
            MarkConnections( segment );
        }

        if( aSegment->IsEndPoint( segment->GetEndPoint() )
            && !GetPin( segment->GetEndPoint(), NULL, true ) )
        {
            item->SetFlags( CANDIDATE );
            MarkConnections( segment );
        }
    }
}
/**
 * In a contiguous list of wires, remove wires that backtrack over the previous
 * wire. Example:
 *
 * Wire is added:
 * ---------------------------------------->
 *
 * A second wire backtracks over it:
 * -------------------<====================>
 *
 * RemoveBacktracks is called:
 * ------------------->
 */
static void RemoveBacktracks( DLIST<SCH_ITEM>& aWires )
{
    EDA_ITEM* first = aWires.GetFirst();
    std::vector<SCH_LINE*> last_lines;

    for( EDA_ITEM* p = first; p; )
    {
        SCH_LINE *line = static_cast<SCH_LINE*>( p );
        p = line->Next();

        if( !last_lines.empty() )
        {
            SCH_LINE* last_line = last_lines[last_lines.size() - 1];
            bool contiguous = ( last_line->GetEndPoint() == line->GetStartPoint() );
            bool backtracks = IsPointOnSegment( last_line->GetStartPoint(),
                    last_line->GetEndPoint(), line->GetEndPoint() );
            bool total_backtrack = ( last_line->GetStartPoint() == line->GetEndPoint() );

            if( contiguous && backtracks )
            {
                if( total_backtrack )
                {
                    delete s_wires.Remove( last_line );
                    delete s_wires.Remove( line );
                    last_lines.pop_back();
                }
                else
                {
                    last_line->SetEndPoint( line->GetEndPoint() );
                    delete s_wires.Remove( line );
                }
            }
            else
            {
                last_lines.push_back( line );
            }
        }
        else
        {
            last_lines.push_back( line );
        }
    }
}
void SCH_SCREEN::addConnectedItemsToBlock( const wxPoint& position )
{
    SCH_ITEM* item;
    ITEM_PICKER picker;
    bool addinlist = true;

    for( item = m_drawList.begin(); item; item = item->Next() )
    {
        picker.SetItem( item );

        if( !item->IsConnectable() || !item->IsConnected( position )
            || (item->GetFlags() & SKIP_STRUCT) )
            continue;

        if( item->IsSelected() && item->Type() != SCH_LINE_T )
            continue;

        // A line having 2 ends, it can be tested twice: one time per end
        if( item->Type() == SCH_LINE_T )
        {
            if( ! item->IsSelected() )      // First time this line is tested
                item->SetFlags( SELECTED | STARTPOINT | ENDPOINT );
            else      // second time (or more) this line is tested
                addinlist = false;

            SCH_LINE* line = (SCH_LINE*) item;

            if( line->GetStartPoint() == position )
                item->ClearFlags( STARTPOINT );
            else if( line->GetEndPoint() == position )
                item->ClearFlags( ENDPOINT );
        }
        else
            item->SetFlags( SELECTED );

        if( addinlist )
        {
            picker.SetFlags( item->GetFlags() );
            m_BlockLocate.GetItems().PushItem( picker );
        }
    }
}
void SCH_EDIT_FRAME::EndSegment( wxDC* DC )
{
    SCH_SCREEN* screen = GetScreen();
    SCH_LINE* segment = (SCH_LINE*) screen->GetCurItem();

    if( segment == NULL || segment->Type() != SCH_LINE_T || !segment->IsNew() )
        return;

    // Delete zero length segments and clear item flags.
    SCH_ITEM* item = s_wires.begin();

    while( item )
    {
        item->ClearFlags();

        wxCHECK_RET( item->Type() == SCH_LINE_T, wxT( "Unexpected object type in wire list." ) );

        segment = (SCH_LINE*) item;
        item = item->Next();

        if( segment->IsNull() )
            delete s_wires.Remove( segment );
    }

    if( s_wires.GetCount() == 0 )
        return;

    // Get the last non-null wire (this is the last created segment).
    SetRepeatItem( segment = (SCH_LINE*) s_wires.GetLast() );

    screen->SetCurItem( NULL );
    m_canvas->EndMouseCapture( -1, -1, wxEmptyString, false );

    // store the terminal point of this last segment: a junction could be needed
    // (the last wire could be merged/deleted/modified, and lost)
    wxPoint endpoint = segment->GetEndPoint();

    // store the starting point of this first segment: a junction could be needed
    SCH_LINE* firstsegment = (SCH_LINE*) s_wires.GetFirst();
    wxPoint startPoint = firstsegment->GetStartPoint();

    // Save the old wires for the undo command
    DLIST< SCH_ITEM > oldWires;                     // stores here the old wires
    GetScreen()->ExtractWires( oldWires, true );    // Save them in oldWires list
    // Put the snap shot of the previous wire, buses, and junctions in the undo/redo list.
    PICKED_ITEMS_LIST oldItems;
    oldItems.m_Status = UR_WIRE_IMAGE;

    while( oldWires.GetCount() != 0 )
    {
        ITEM_PICKER picker = ITEM_PICKER( oldWires.PopFront(), UR_WIRE_IMAGE );
        oldItems.PushItem( picker );
    }

    SaveCopyInUndoList( oldItems, UR_WIRE_IMAGE );

    // Remove segments backtracking over others
    RemoveBacktracks( s_wires );

    // Add the new wires
    screen->Append( s_wires );

    // Correct and remove segments that need to be merged.
    screen->SchematicCleanUp();

    // A junction could be needed to connect the end point of the last created segment.
    if( screen->IsJunctionNeeded( endpoint ) )
        screen->Append( AddJunction( DC, endpoint ) );

    // A junction could be needed to connect the start point of the set of new created wires
    if( screen->IsJunctionNeeded( startPoint ) )
        screen->Append( AddJunction( DC, startPoint ) );

    m_canvas->Refresh();

    OnModify();
}
int SCH_SCREEN::GetConnection( const wxPoint& aPosition, PICKED_ITEMS_LIST& aList,
                               bool aFullConnection )
{
    SCH_ITEM* item;
    EDA_ITEM* tmp;
    EDA_ITEMS list;

    // Clear flags member for all items.
    ClearDrawingState();
    BreakSegmentsOnJunctions();

    if( GetNode( aPosition, list ) == 0 )
        return 0;

    for( size_t i = 0;  i < list.size();  i++ )
    {
        item = (SCH_ITEM*) list[ i ];
        item->SetFlags( SELECTEDNODE | STRUCT_DELETED );

        /* Put this structure in the picked list: */
        ITEM_PICKER picker( item, UR_DELETED );
        aList.PushItem( picker );
    }

    // Mark all wires, junctions, .. connected to the item(s) found.
    if( aFullConnection )
    {
        SCH_LINE* segment;

        for( item = m_drawList.begin(); item; item = item->Next() )
        {
            if( !(item->GetFlags() & SELECTEDNODE) )
                continue;

            if( item->Type() != SCH_LINE_T )
                continue;

            MarkConnections( (SCH_LINE*) item );
        }

        // Search all attached wires (i.e wire with one new dangling end )
        for( item = m_drawList.begin(); item; item = item->Next() )
        {
            bool noconnect = false;

            if( item->GetFlags() & STRUCT_DELETED )
                continue;                                   // Already seen

            if( !(item->GetFlags() & CANDIDATE) )
                continue;                                   // not a candidate

            if( item->Type() != SCH_LINE_T )
                continue;

            item->SetFlags( SKIP_STRUCT );

            segment = (SCH_LINE*) item;

            /* If the wire start point is connected to a wire that was already found
             * and now is not connected, add the wire to the list. */
            for( tmp = m_drawList.begin(); tmp; tmp = tmp->Next() )
            {
                // Ensure tmp is a previously deleted segment:
                if( ( tmp->GetFlags() & STRUCT_DELETED ) == 0 )
                    continue;

                if( tmp->Type() != SCH_LINE_T )
                    continue;

                SCH_LINE* testSegment = (SCH_LINE*) tmp;

               // Test for segment connected to the previously deleted segment:
                if( testSegment->IsEndPoint( segment->GetStartPoint() ) )
                    break;
            }

            // when tmp != NULL, segment is a new candidate:
            // put it in deleted list if
            // the start point is not connected to an other item (like pin)
            if( tmp && !CountConnectedItems( segment->GetStartPoint(), true ) )
                noconnect = true;

            /* If the wire end point is connected to a wire that has already been found
             * and now is not connected, add the wire to the list. */
            for( tmp = m_drawList.begin(); tmp; tmp = tmp->Next() )
            {
                // Ensure tmp is a previously deleted segment:
                if( ( tmp->GetFlags() & STRUCT_DELETED ) == 0 )
                    continue;

                if( tmp->Type() != SCH_LINE_T )
                    continue;

                SCH_LINE* testSegment = (SCH_LINE*) tmp;

                // Test for segment connected to the previously deleted segment:
                if( testSegment->IsEndPoint( segment->GetEndPoint() ) )
                    break;
            }

            // when tmp != NULL, segment is a new candidate:
            // put it in deleted list if
            // the end point is not connected to an other item (like pin)
            if( tmp && !CountConnectedItems( segment->GetEndPoint(), true ) )
                noconnect = true;

            item->ClearFlags( SKIP_STRUCT );

            if( noconnect )
            {
                item->SetFlags( STRUCT_DELETED );

                ITEM_PICKER picker( item, UR_DELETED );
                aList.PushItem( picker );

                item = m_drawList.begin();
            }
        }

        // Get redundant junctions (junctions which connect < 3 end wires
        // and no pin)
        for( item = m_drawList.begin(); item; item = item->Next() )
        {
            if( item->GetFlags() & STRUCT_DELETED )
                continue;

            if( !(item->GetFlags() & CANDIDATE) )
                continue;

            if( item->Type() != SCH_JUNCTION_T )
                continue;

            SCH_JUNCTION* junction = (SCH_JUNCTION*) item;

            if( CountConnectedItems( junction->GetPosition(), false ) <= 2 )
            {
                item->SetFlags( STRUCT_DELETED );

                ITEM_PICKER picker( item, UR_DELETED );
                aList.PushItem( picker );
            }
        }

        for( item = m_drawList.begin(); item;  item = item->Next() )
        {
            if( item->GetFlags() & STRUCT_DELETED )
                continue;

            if( item->Type() != SCH_LABEL_T )
                continue;

            tmp = GetWireOrBus( ( (SCH_TEXT*) item )->GetPosition() );

            if( tmp && tmp->GetFlags() & STRUCT_DELETED )
            {
                item->SetFlags( STRUCT_DELETED );

                ITEM_PICKER picker( item, UR_DELETED );
                aList.PushItem( picker );
            }
        }
    }

    ClearDrawingState();

    return aList.GetCount();
}
Exemple #8
0
void SCH_SCREEN::addConnectedItemsToBlock( const SCH_ITEM* aItem, const wxPoint& position )
{
    SCH_ITEM* item;
    ITEM_PICKER picker;

    for( item = m_drawList.begin(); item; item = item->Next() )
    {

        if( !item->IsConnectable() || ( item->GetFlags() & SKIP_STRUCT )
                || !item->CanConnect( aItem ) || item == aItem )
            continue;

        // A line having 2 ends, it can be tested twice: one time per end
        if( item->Type() == SCH_LINE_T )
        {
            SCH_LINE* line = (SCH_LINE*) item;

            if( !item->HitTest( position ) )
                continue;

            // First time through.  Flags set to denote an end that is not moving
            if( !item->IsSelected() )
                item->SetFlags( CANDIDATE | STARTPOINT | ENDPOINT );

            if( line->GetStartPoint() == position )
                item->ClearFlags( STARTPOINT );
            else if( line->GetEndPoint() == position )
                item->ClearFlags( ENDPOINT );
            else
                // This picks up items such as labels that can connect to the middle of a line
                item->ClearFlags( STARTPOINT | ENDPOINT );
        }
        // We want to move a mid-connected label or bus entry when the full line is being moved
        else if( !item->IsSelected()
                && aItem->Type() == SCH_LINE_T
                && !( aItem->GetFlags() & ( ENDPOINT | STARTPOINT ) ) )
        {
            std::vector< wxPoint > connections;
            item->GetConnectionPoints( connections );

            for( auto conn : connections )
            {
                if( aItem->HitTest( conn ) )
                {
                    item->SetFlags( CANDIDATE );
                    break;
                }
            }
        }

        if( item->IsSelected() )
            continue;

        if( ( item->GetFlags() & CANDIDATE ) || item->IsConnected( position ) ) // Deal with all non-line items
        {
            item->ClearFlags( CANDIDATE );
            item->SetFlags( SELECTED );
            picker.SetItem( item );
            picker.SetFlags( item->GetFlags() );
            m_BlockLocate.GetItems().PushItem( picker );
        }
    }
}