lib_rectangle.cpp 11.5 KB
Newer Older
1 2 3
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
4 5
 * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
 * Copyright (C) 2004-2012 KiCad Developers, see change_log.txt for contributors.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * 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 lib_rectangle.cpp
 */
28

29 30 31 32 33 34 35 36
#include <fctsys.h>
#include <gr_basic.h>
#include <macros.h>
#include <class_drawpanel.h>
#include <plot_common.h>
#include <trigo.h>
#include <wxstruct.h>
#include <richio.h>
37
#include <base_units.h>
38
#include <msgpanel.h>
39

40 41 42
#include <general.h>
#include <lib_rectangle.h>
#include <transform.h>
43 44 45


LIB_RECTANGLE::LIB_RECTANGLE( LIB_COMPONENT* aParent ) :
46
    LIB_ITEM( LIB_RECTANGLE_T, aParent )
47 48 49 50 51 52 53 54 55 56 57
{
    m_Width                = 0;
    m_Fill                 = NO_FILL;
    m_isFillable           = true;
    m_typeName             = _( "Rectangle" );
    m_isHeightLocked       = false;
    m_isWidthLocked        = false;
    m_isStartPointSelected = false;
}


58
bool LIB_RECTANGLE::Save( OUTPUTFORMATTER& aFormatter )
59
{
60 61
    aFormatter.Print( 0, "S %d %d %d %d %d %d %d %c\n", m_Pos.x, m_Pos.y,
                      m_End.x, m_End.y, m_Unit, m_Convert, m_Width, fill_tab[m_Fill] );
62 63 64 65 66

    return true;
}


67
bool LIB_RECTANGLE::Load( LINE_READER& aLineReader, wxString& aErrorMsg )
68 69 70
{
    int  cnt;
    char tmp[256];
71
    char* line = (char*)aLineReader;
72

73
    cnt = sscanf( line + 2, "%d %d %d %d %d %d %d %s", &m_Pos.x, &m_Pos.y,
74 75 76 77
                  &m_End.x, &m_End.y, &m_Unit, &m_Convert, &m_Width, tmp );

    if( cnt < 7 )
    {
78
        aErrorMsg.Printf( _( "Rectangle only had %d parameters of the required 7" ), cnt );
79 80 81 82 83
        return false;
    }

    if( tmp[0] == 'F' )
        m_Fill = FILLED_SHAPE;
84

85 86 87 88 89 90 91
    if( tmp[0] == 'f' )
        m_Fill = FILLED_WITH_BG_BODYCOLOR;

    return true;
}


92
EDA_ITEM* LIB_RECTANGLE::Clone() const
93
{
94
    return new LIB_RECTANGLE( *this );
95 96 97
}


98
int LIB_RECTANGLE::compare( const LIB_ITEM& aOther ) const
99
{
100
    wxASSERT( aOther.Type() == LIB_RECTANGLE_T );
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

    const LIB_RECTANGLE* tmp = ( LIB_RECTANGLE* ) &aOther;

    if( m_Pos.x != tmp->m_Pos.x )
        return m_Pos.x - tmp->m_Pos.x;

    if( m_Pos.y != tmp->m_Pos.y )
        return m_Pos.y - tmp->m_Pos.y;

    if( m_End.x != tmp->m_End.x )
        return m_End.x - tmp->m_End.x;

    if( m_End.y != tmp->m_End.y )
        return m_End.y - tmp->m_End.y;

    return 0;
}


120
void LIB_RECTANGLE::SetOffset( const wxPoint& aOffset )
121 122 123 124 125 126
{
    m_Pos += aOffset;
    m_End += aOffset;
}


127
bool LIB_RECTANGLE::Inside( EDA_RECT& aRect ) const
128
{
129
    return aRect.Contains( m_Pos.x, -m_Pos.y ) || aRect.Contains( m_End.x, -m_End.y );
130 131 132
}


133
void LIB_RECTANGLE::Move( const wxPoint& aPosition )
134 135 136 137 138 139 140
{
    wxPoint size = m_End - m_Pos;
    m_Pos = aPosition;
    m_End = aPosition + size;
}


141
void LIB_RECTANGLE::MirrorHorizontal( const wxPoint& aCenter )
142 143 144 145 146 147 148 149 150
{
    m_Pos.x -= aCenter.x;
    m_Pos.x *= -1;
    m_Pos.x += aCenter.x;
    m_End.x -= aCenter.x;
    m_End.x *= -1;
    m_End.x += aCenter.x;
}

151

152
void LIB_RECTANGLE::MirrorVertical( const wxPoint& aCenter )
153 154 155 156 157 158 159 160 161
{
    m_Pos.y -= aCenter.y;
    m_Pos.y *= -1;
    m_Pos.y += aCenter.y;
    m_End.y -= aCenter.y;
    m_End.y *= -1;
    m_End.y += aCenter.y;
}

162

163
void LIB_RECTANGLE::Rotate( const wxPoint& aCenter, bool aRotateCCW )
164
{
165 166 167
    int rot_angle = aRotateCCW ? -900 : 900;
    RotatePoint( &m_Pos, aCenter, rot_angle );
    RotatePoint( &m_End, aCenter, rot_angle );
168 169
}

170

171 172
void LIB_RECTANGLE::Plot( PLOTTER* aPlotter, const wxPoint& aOffset, bool aFill,
                          const TRANSFORM& aTransform )
173 174 175 176 177 178 179 180
{
    wxASSERT( aPlotter != NULL );

    wxPoint pos = aTransform.TransformCoordinate( m_Pos ) + aOffset;
    wxPoint end = aTransform.TransformCoordinate( m_End ) + aOffset;

    if( aFill && m_Fill == FILLED_WITH_BG_BODYCOLOR )
    {
181
        aPlotter->SetColor( GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
182
        aPlotter->Rect( pos, end, FILLED_WITH_BG_BODYCOLOR, 0 );
183 184 185
    }

    bool already_filled = m_Fill == FILLED_WITH_BG_BODYCOLOR;
186
    aPlotter->SetColor( GetLayerColor( LAYER_DEVICE ) );
187
    aPlotter->Rect( pos, end, already_filled ? NO_FILL : m_Fill, GetPenSize() );
188 189 190
}


191
int LIB_RECTANGLE::GetPenSize() const
192
{
193
    return ( m_Width == 0 ) ? GetDefaultLineThickness() : m_Width;
194 195
}

196

197
void LIB_RECTANGLE::drawGraphic( EDA_DRAW_PANEL* aPanel, wxDC* aDC,
198
                                 const wxPoint& aOffset, EDA_COLOR_T aColor, GR_DRAWMODE aDrawMode,
199 200 201 202
                                 void* aData, const TRANSFORM& aTransform )
{
    wxPoint pos1, pos2;

203
    EDA_COLOR_T color = GetLayerColor( LAYER_DEVICE );
204 205 206

    if( aColor < 0 )       // Used normal color or selected color
    {
207
        if( IsSelected() )
208
            color = GetItemSelectedColor();
209 210
    }
    else
211
    {
212
        color = aColor;
213
    }
214 215 216 217 218

    pos1 = aTransform.TransformCoordinate( m_Pos ) + aOffset;
    pos2 = aTransform.TransformCoordinate( m_End ) + aOffset;

    FILL_T fill = aData ? NO_FILL : m_Fill;
219

220 221 222 223 224
    if( aColor >= 0 )
        fill = NO_FILL;

    GRSetDrawMode( aDC, aDrawMode );

225
    EDA_RECT* const clipbox  = aPanel? aPanel->GetClipBox() : NULL;
226
    if( fill == FILLED_WITH_BG_BODYCOLOR && !aData )
227
        GRFilledRect( clipbox, aDC, pos1.x, pos1.y, pos2.x, pos2.y, GetPenSize( ),
228 229
                      (m_Flags & IS_MOVED) ? color : GetLayerColor( LAYER_DEVICE_BACKGROUND ),
                      GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
230
    else if( m_Fill == FILLED_SHAPE  && !aData )
231
        GRFilledRect( clipbox, aDC, pos1.x, pos1.y, pos2.x, pos2.y,
232 233
                      GetPenSize(), color, color );
    else
234
        GRRect( clipbox, aDC, pos1.x, pos1.y, pos2.x, pos2.y, GetPenSize(), color );
235 236 237 238

    /* Set to one (1) to draw bounding box around rectangle to validate
     * bounding box calculation. */
#if 0
239
    EDA_RECT bBox = GetBoundingBox();
240
    bBox.Inflate( m_Thickness + 1, m_Thickness + 1 );
241
    GRRect( clipbox, aDC, bBox.GetOrigin().x, bBox.GetOrigin().y,
242 243 244 245 246
            bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA );
#endif
}


247
void LIB_RECTANGLE::GetMsgPanelInfo( MSG_PANEL_ITEMS& aList )
248 249 250
{
    wxString msg;

251
    LIB_ITEM::GetMsgPanelInfo( aList );
252

253
    msg = StringFromValue( g_UserUnit, m_Width, true );
254

255
    aList.push_back( MSG_PANEL_ITEM( _( "Line width" ), msg, BLUE ) );
256 257 258
}


259
const EDA_RECT LIB_RECTANGLE::GetBoundingBox() const
260
{
261
    EDA_RECT rect;
262 263 264

    rect.SetOrigin( m_Pos.x, m_Pos.y * -1 );
    rect.SetEnd( m_End.x, m_End.y * -1 );
jean-pierre charras's avatar
jean-pierre charras committed
265
    rect.Inflate( (GetPenSize() / 2) + 1 );
266 267 268 269
    return rect;
}


270
bool LIB_RECTANGLE::HitTest( const wxPoint& aPosition )
271
{
jean-pierre charras's avatar
jean-pierre charras committed
272
    int mindist = ( GetPenSize() / 2 ) + 1;
273 274 275 276 277

    // Have a minimal tolerance for hit test
    if( mindist < MINIMUM_SELECTION_DISTANCE )
        mindist = MINIMUM_SELECTION_DISTANCE;

278
    return HitTest( aPosition, mindist, DefaultTransform );
279 280 281
}


282
bool LIB_RECTANGLE::HitTest( wxPoint aPosition, int aThreshold, const TRANSFORM& aTransform )
283
{
284 285 286
    if( aThreshold < 0 )
        aThreshold = GetPenSize() / 2;

287 288 289 290 291 292 293 294 295
    wxPoint actualStart = aTransform.TransformCoordinate( m_Pos );
    wxPoint actualEnd   = aTransform.TransformCoordinate( m_End );

    // locate lower segment
    wxPoint start, end;

    start = actualStart;
    end.x = actualEnd.x;
    end.y = actualStart.y;
296

297
    if( TestSegmentHit( aPosition, start, end, aThreshold ) )
298 299 300 301 302
        return true;

    // locate right segment
    start.x = actualEnd.x;
    end.y   = actualEnd.y;
303

304
    if( TestSegmentHit( aPosition, start, end, aThreshold ) )
305 306 307 308 309
        return true;

    // locate upper segment
    start.y = actualEnd.y;
    end.x   = actualStart.x;
310

311
    if( TestSegmentHit( aPosition, start, end, aThreshold ) )
312 313 314 315 316 317
        return true;

    // locate left segment
    start = actualStart;
    end.x = actualStart.x;
    end.y = actualEnd.y;
318

319
    if( TestSegmentHit( aPosition, start, end, aThreshold ) )
320 321 322 323 324 325
        return true;

    return false;
}


326 327 328
wxString LIB_RECTANGLE::GetSelectMenuText() const
{
    return wxString::Format( _( "Rectangle from (%s, %s) to (%s, %s)" ),
329 330 331 332
                             GetChars( CoordinateToString( m_Pos.x ) ),
                             GetChars( CoordinateToString( m_Pos.y ) ),
                             GetChars( CoordinateToString( m_End.x ) ),
                             GetChars( CoordinateToString( m_End.y ) ) );
333 334 335
}


336
void LIB_RECTANGLE::BeginEdit( STATUS_FLAGS aEditMode, const wxPoint aPosition )
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
{
    wxCHECK_RET( ( aEditMode & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
                 wxT( "Invalid edit mode for LIB_RECTANGLE object." ) );

    if( aEditMode == IS_NEW )
    {
        m_Pos = m_End = aPosition;
    }
    else if( aEditMode == IS_RESIZED )
    {
        m_isStartPointSelected = abs( m_Pos.x - aPosition.x ) < MINIMUM_SELECTION_DISTANCE
            || abs( m_Pos.y - aPosition.y ) < MINIMUM_SELECTION_DISTANCE;

        if( m_isStartPointSelected )
        {
            m_isWidthLocked = abs( m_Pos.x - aPosition.x ) >= MINIMUM_SELECTION_DISTANCE;
            m_isHeightLocked = abs( m_Pos.y - aPosition.y ) >= MINIMUM_SELECTION_DISTANCE;
        }
        else
        {
            m_isWidthLocked = abs( m_End.x - aPosition.x ) >= MINIMUM_SELECTION_DISTANCE;
            m_isHeightLocked = abs( m_End.y - aPosition.y ) >= MINIMUM_SELECTION_DISTANCE;
        }

        SetEraseLastDrawItem();
    }
    else if( aEditMode == IS_MOVED )
    {
        m_initialPos = m_Pos;
        m_initialCursorPos = aPosition;
        SetEraseLastDrawItem();
    }

    m_Flags = aEditMode;
}


bool LIB_RECTANGLE::ContinueEdit( const wxPoint aPosition )
{
    wxCHECK_MSG( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0, false,
                   wxT( "Bad call to ContinueEdit().  LIB_RECTANGLE is not being edited." ) );

    return false;
}


void LIB_RECTANGLE::EndEdit( const wxPoint& aPosition, bool aAbort )
{
    wxCHECK_RET( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
                   wxT( "Bad call to EndEdit().  LIB_RECTANGLE is not being edited." ) );

    m_Flags = 0;
    m_isHeightLocked = false;
    m_isWidthLocked  = false;
    SetEraseLastDrawItem( false );
}


void LIB_RECTANGLE::calcEdit( const wxPoint& aPosition )
{
    if( m_Flags == IS_NEW )
    {
        m_End = aPosition;
        SetEraseLastDrawItem();
    }
    else if( m_Flags == IS_RESIZED )
    {
        if( m_isHeightLocked )
        {
            if( m_isStartPointSelected )
                m_Pos.x = aPosition.x;
            else
                m_End.x = aPosition.x;
        }
        else if( m_isWidthLocked )
        {
            if( m_isStartPointSelected )
                m_Pos.y = aPosition.y;
            else
                m_End.y = aPosition.y;
        }
        else
        {
            if( m_isStartPointSelected )
                m_Pos = aPosition;
            else
                m_End = aPosition;
        }
    }
    else if( m_Flags == IS_MOVED )
    {
        Move( m_initialPos + aPosition - m_initialCursorPos );
    }
}