diff --git a/CMakeLists.txt b/CMakeLists.txt index cb3fd9589e..7d2fbf142a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,10 @@ option(KICAD_SCRIPTING_MODULES option(KICAD_SCRIPTING_WXPYTHON "set this option ON to build wxpython implementation for wx interface building in python and py.shell" ) + +option(KICAD_GAL + "set this option ON to build KICAD using Graphics Abstraction Layer as a rendering backend" + ) # when option KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES is enabled: # PYTHON_EXECUTABLE can be defined when invoking cmake @@ -189,6 +193,10 @@ if(USE_WX_GRAPHICS_CONTEXT) add_definitions(-DUSE_WX_GRAPHICS_CONTEXT) endif() +if(KICAD_GAL) + add_definitions(-DKICAD_GAL) +endif() + # Allow user to override the default settings for adding images to menu items. By default # images in menu items are enabled on all platforms except OSX. This can be over ridden by # defining -DUSE_IMAGES_IN_MENUS=ON/OFF to force the preferred behavior. @@ -257,6 +265,20 @@ add_definitions(-DWX_COMPATIBILITY) find_package(OpenGL QUIET) check_find_package_result(OPENGL_FOUND "OpenGL") +if(KICAD_GAL) +##################### +# Find GLEW library # +##################### +find_package(GLEW) +check_find_package_result(GLEW_FOUND "GLEW") + +###################### +# Find Cairo library # +###################### +find_package(Cairo 1.8.1 QUIET) +check_find_package_result(CAIRO_FOUND "Cairo") +endif(KICAD_GAL) + ###################### # Find Boost library # ###################### diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 8961793ec1..8c320220bd 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -4,12 +4,29 @@ include_directories( ./dialogs ./dialog_about ${Boost_INCLUDE_DIR} + ${CAIRO_INCLUDE_DIR} ../3d-viewer ../pcbnew ../polygon ${INC_AFTER} ) +if(KICAD_GAL) +set(GAL_SRCS + drawpanel_gal.cpp + painter.cpp + gal/graphics_abstraction_layer.cpp + gal/stroke_font.cpp + gal/color4d.cpp + gal/opengl/opengl_gal.cpp + gal/opengl/shader.cpp + gal/cairo/cairo_gal.cpp + view/wx_view_controls.cpp + ) + +add_library(gal STATIC ${GAL_SRCS}) +endif(KICAD_GAL) + set(COMMON_ABOUT_DLG_SRCS dialog_about/AboutDialog_main.cpp dialog_about/dialog_about.cpp @@ -82,6 +99,14 @@ set(COMMON_SRCS zoom.cpp ) +if(KICAD_GAL) +set(COMMON_SRCS + ${COMMON_SRCS} + view/view.cpp + view/view_item.cpp + ) +endif(KICAD_GAL) + add_library(common STATIC ${COMMON_SRCS}) set(PCB_COMMON_SRCS @@ -131,6 +156,12 @@ set(PCB_COMMON_SRCS fp_lib_table.cpp ) +if(KICAD_GAL) +set(PCB_COMMON_SRCS + ${PCB_COMMON_SRCS} + ../pcbnew/pcb_painter.cpp + ) +endif(KICAD_GAL) # add -DPCBNEW to compilation of these PCBNEW sources set_source_files_properties( ${PCB_COMMON_SRCS} PROPERTIES diff --git a/common/base_struct.cpp b/common/base_struct.cpp index 3b9cb8d3e6..4e72427c7a 100644 --- a/common/base_struct.cpp +++ b/common/base_struct.cpp @@ -222,6 +222,22 @@ EDA_ITEM& EDA_ITEM::operator=( const EDA_ITEM& aItem ) } +const BOX2I EDA_ITEM::ViewBBox() const +{ + // Basic fallback + return BOX2I( VECTOR2I( GetBoundingBox().GetOrigin() ), + VECTOR2I( GetBoundingBox().GetSize() ) ); +} + + +void EDA_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const +{ + // Basic fallback + aCount = 1; + aLayers[0] = 0; +} + + #if defined(DEBUG) // A function that should have been in wxWidgets diff --git a/common/drawframe.cpp b/common/drawframe.cpp index e2b86f57c5..391041d343 100644 --- a/common/drawframe.cpp +++ b/common/drawframe.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,7 @@ BEGIN_EVENT_TABLE( EDA_DRAW_FRAME, EDA_BASE_FRAME ) EVT_MENU_OPEN( EDA_DRAW_FRAME::OnMenuOpen ) EVT_ACTIVATE( EDA_DRAW_FRAME::OnActivate ) EVT_MENU_RANGE( ID_ZOOM_IN, ID_ZOOM_REDRAW, EDA_DRAW_FRAME::OnZoom ) + EVT_MENU( ID_SWITCH_CANVAS, EDA_DRAW_FRAME::OnZoom ) EVT_MENU_RANGE( ID_OFFCENTER_ZOOM_IN, ID_OFFCENTER_ZOOM_OUT, EDA_DRAW_FRAME::OnZoom ) EVT_MENU_RANGE( ID_POPUP_ZOOM_START_RANGE, ID_POPUP_ZOOM_END_RANGE, EDA_DRAW_FRAME::OnZoom ) @@ -100,6 +102,8 @@ EDA_DRAW_FRAME::EDA_DRAW_FRAME( wxWindow* aParent, m_HotkeysZoomAndGridList = NULL; m_canvas = NULL; + m_galCanvas = NULL; + m_galCanvasActive = false; m_messagePanel = NULL; m_currentScreen = NULL; m_toolId = ID_NO_TOOL_SELECTED; @@ -937,3 +941,27 @@ void EDA_DRAW_FRAME::AdjustScrollBars( const wxPoint& aCenterPositionIU ) screen->m_ScrollbarPos.x, screen->m_ScrollbarPos.y, noRefresh ); } + + +void EDA_DRAW_FRAME::UseGalCanvas( bool aEnable ) +{ + if( !( aEnable ^ m_galCanvasActive ) ) + return; + + if( aEnable ) + { + m_canvas->Hide(); + m_galCanvas->Show(); + m_galCanvas->Raise(); + m_galCanvas->Refresh(); + } + else + { + m_galCanvas->Hide(); + m_canvas->Show(); + m_canvas->Raise(); + m_canvas->Refresh(); + } + + m_galCanvasActive = aEnable; +} diff --git a/common/drawpanel.cpp b/common/drawpanel.cpp index 47c2996935..cdb2db0dfe 100644 --- a/common/drawpanel.cpp +++ b/common/drawpanel.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -74,6 +75,9 @@ BEGIN_EVENT_TABLE( EDA_DRAW_PANEL, wxScrolledWindow ) EVT_ERASE_BACKGROUND( EDA_DRAW_PANEL::OnEraseBackground ) EVT_SCROLLWIN( EDA_DRAW_PANEL::OnScroll ) EVT_ACTIVATE( EDA_DRAW_PANEL::OnActivate ) +#ifdef KICAD_GAL + EVT_SIZE( EDA_DRAW_PANEL::OnSize ) +#endif EVT_MENU_RANGE( ID_PAN_UP, ID_PAN_RIGHT, EDA_DRAW_PANEL::OnPan ) END_EVENT_TABLE() @@ -1377,6 +1381,18 @@ void EDA_DRAW_PANEL::OnPan( wxCommandEvent& event ) } +#ifdef KICAD_GAL +void EDA_DRAW_PANEL::OnSize( wxSizeEvent& SizeEv ) +{ + if( GetParent()->GetGalCanvas() != NULL ) + { + GetParent()->GetGalCanvas()->SetPosition( GetPosition() ); + GetParent()->GetGalCanvas()->SetSize( GetSize() ); + } +} +#endif + + void EDA_DRAW_PANEL::EndMouseCapture( int id, int cursor, const wxString& title, bool aCallEndFunc ) { diff --git a/common/drawpanel_gal.cpp b/common/drawpanel_gal.cpp new file mode 100644 index 0000000000..6a09411c7d --- /dev/null +++ b/common/drawpanel_gal.cpp @@ -0,0 +1,150 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * 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 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define METRIC_UNIT_LENGTH (1e9) + +void EDA_DRAW_PANEL_GAL::onPaint( wxEvent& event ) +{ + m_gal->BeginDrawing(); + m_gal->SetBackgroundColor( KiGfx::COLOR4D( 0, 0, 0, 1.0 ) ); + m_gal->ClearScreen(); + m_gal->SetGridOrigin( VECTOR2D( 0, 0 ) ); + m_gal->SetGridOriginMarkerSize( 15 ); + m_gal->SetGridSize( VECTOR2D( METRIC_UNIT_LENGTH / 10000.0, METRIC_UNIT_LENGTH / 10000.0 ) ); + m_gal->SetGridDrawThreshold( 10 ); + m_gal->SetLayerDepth( 0 ); + + m_gal->DrawGrid(); + m_view->Redraw(); + + m_gal->EndDrawing(); + m_gal->Flush(); +} + + +void EDA_DRAW_PANEL_GAL::onSize( wxSizeEvent& aEvent ) +{ + m_gal->ResizeScreen( aEvent.GetSize().x, aEvent.GetSize().y ); +} + + +EDA_DRAW_PANEL_GAL::EDA_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWindowId, + const wxPoint& aPosition, const wxSize& aSize, + GalType aGalType ) : + wxWindow( aParentWindow, aWindowId, aPosition, aSize ), + m_screenSize( aSize.x, aSize.y ), m_parentFrame( aParentWindow ) +{ + m_gal = NULL; + m_view = NULL; + + m_galShaderPath = std::string( ::wxGetCwd().mb_str() ) + "/../../gal/opengl/shader/"; + + SwitchBackend( aGalType, false ); + SetBackgroundStyle( wxBG_STYLE_CUSTOM ); + + // Initial display settings + m_gal->SetLookAtPoint( VECTOR2D( 0, 0 ) ); + m_gal->SetZoomFactor( 1.0 ); + m_gal->ComputeWorldScreenMatrix(); + + m_view = new KiGfx::VIEW( true ); + m_view->SetGAL( m_gal ); + + // View uses layers to display EDA_ITEMs (item may be displayed on several layers, for example + // pad may be shown on pad, pad hole nad solder paste layers). There are usual copper layers + // (eg. F.Cu, B.Cu, internal and so on) and layers for displaying objects such as texts, + // silkscreen, pads, vias, etc. + for( int i = 0; i < TOTAL_LAYER_COUNT; i++ ) + { + m_view->AddLayer( i ); + } + + m_viewControls = new KiGfx::WX_VIEW_CONTROLS( m_view, this ); + + m_painter = new KiGfx::PCB_PAINTER( m_gal ); + m_view->SetPainter( m_painter ); + + // FIXME Cairo needs this to be uncommented to remove blinking on refreshing + // Connect( wxEVT_PAINT, wxEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), NULL, this ); + Connect(KiGfx::EVT_GAL_REDRAW, wxEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), NULL, this ); + Connect(wxEVT_SIZE, wxSizeEventHandler( EDA_DRAW_PANEL_GAL::onSize ), NULL, this ); +} + + +EDA_DRAW_PANEL_GAL::~EDA_DRAW_PANEL_GAL() +{ + if( m_painter ) + delete m_painter; + + if( m_viewControls ) + delete m_viewControls; + + if( m_view ) + delete m_view; + + if( m_gal ) + delete m_gal; +} + + +void EDA_DRAW_PANEL_GAL::SwitchBackend( GalType aGalType, bool aUseShaders ) +{ + if( m_gal ) + delete m_gal; + + switch( aGalType ) + { + case GAL_TYPE_OPENGL: + m_gal = new KiGfx::OPENGL_GAL( this, this, this, aUseShaders ); + static_cast (m_gal)->SetShaderPath( m_galShaderPath ); + break; + + case GAL_TYPE_CAIRO: + m_gal = new KiGfx::CAIRO_GAL( this, this, this ); + break; + } + + m_gal->SetWorldUnitLength( 1.0 / METRIC_UNIT_LENGTH * 2.54 ); // 1 inch in nanometers + m_gal->SetScreenDPI( 106 ); // Display resolution setting + m_gal->ComputeWorldScreenMatrix(); + + if( m_view ) + m_view->SetGAL( m_gal ); +} diff --git a/common/gal/cairo/cairo_gal.cpp b/common/gal/cairo/cairo_gal.cpp new file mode 100644 index 0000000000..3b7d7e8fa8 --- /dev/null +++ b/common/gal/cairo/cairo_gal.cpp @@ -0,0 +1,968 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * CAIRO_GAL - Graphics Abstraction Layer for Cairo + * + * 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 + */ + +#include +#include +#include + +#include + +using namespace KiGfx; + +CAIRO_GAL::CAIRO_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener, + wxEvtHandler* aPaintListener, const wxString& aName ) : + wxWindow( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxEXPAND, aName ) +{ + // Default values + fillColor = COLOR4D( 0, 0, 0, 1 ); + strokeColor = COLOR4D( 1, 1, 1, 1 ); + screenSize = VECTOR2D( 20, 20 ); // window will be soon resized + + parentWindow = aParent; + mouseListener = aMouseListener; + paintListener = aPaintListener; + + isGrouping = false; + zoomFactor = 1.0; + + SetSize( aParent->GetSize() ); + + // Connecting the event handlers + // Mouse events are skipped to the parent + this->Connect( wxEVT_SIZE, wxSizeEventHandler( CAIRO_GAL::onSize ) ); + this->Connect( wxEVT_PAINT, wxPaintEventHandler( CAIRO_GAL::onPaint ) ); + this->Connect( wxEVT_ERASE_BACKGROUND, wxEraseEventHandler( CAIRO_GAL::onEraseBackground ) ); + aParent->Connect( wxEVT_ERASE_BACKGROUND, + wxEraseEventHandler( CAIRO_GAL::onEraseBackground ) ); + aParent->GetParent()->Connect( wxEVT_ERASE_BACKGROUND, + wxEraseEventHandler( CAIRO_GAL::onEraseBackground ) ); + + SetBackgroundStyle( wxBG_STYLE_CUSTOM ); + aParent->SetBackgroundStyle( wxBG_STYLE_CUSTOM ); + aParent->GetParent()->SetBackgroundStyle( wxBG_STYLE_CUSTOM ); + + this->Connect( wxEVT_MOTION, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + this->Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + this->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + this->Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + this->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + this->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) ); + + // Initialize line attributes map + lineCapMap[LINE_CAP_BUTT] = CAIRO_LINE_CAP_BUTT; + lineCapMap[LINE_CAP_ROUND] = CAIRO_LINE_CAP_ROUND; + lineCapMap[LINE_CAP_SQUARED] = CAIRO_LINE_CAP_SQUARE; + + lineJoinMap[LINE_JOIN_BEVEL] = CAIRO_LINE_JOIN_BEVEL; + lineJoinMap[LINE_JOIN_ROUND] = CAIRO_LINE_JOIN_ROUND; + lineJoinMap[LINE_JOIN_MITER] = CAIRO_LINE_JOIN_MITER; + + isDeleteSavedPixels = false; + + isGrouping = false; + + // Initialize the cursor shape + SetCursorColor( COLOR4D( 1.0, 1.0, 1.0, 1.0 ) ); + initCursor( 21 ); + + screenSizeY = screenSize.y; + + // Allocate memory + allocateBitmaps(); + + // Set grid defaults + SetGridColor( COLOR4D( 0.5, 0.5, 0.5, 0.3 ) ); + SetCoarseGrid( 10 ); + SetGridLineWidth( 0.5 ); + + Refresh(); +} + + +CAIRO_GAL::~CAIRO_GAL() +{ + // TODO Deleting of list contents like groups and paths + delete[] bitmapBuffer; + delete[] bitmapBufferBackup; + delete wxBitmap_; +} + + +void CAIRO_GAL::onPaint( wxPaintEvent& aEvent ) +{ + PostPaint(); +} + + +void CAIRO_GAL::onEraseBackground( wxEraseEvent& aEvent ) +{ + // FIXME +} + + +void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight ) +{ + deleteBitmaps(); + + screenSizeY = aHeight; + screenSize = VECTOR2D( aWidth, aHeight ); + + // Recreate the bitmaps + allocateBitmaps(); + + SetSize( wxSize( aWidth, aHeight ) ); + + PostPaint(); +} + + +void CAIRO_GAL::onSize( wxSizeEvent& aEvent ) +{ + wxSize newSize = aEvent.GetSize(); + + ResizeScreen( newSize.x, newSize.y ); +} + + +void CAIRO_GAL::skipMouseEvent( wxMouseEvent& aEvent ) +{ + // Post the mouse event to the event listener registered in constructor, if any + if( mouseListener ) + wxPostEvent( mouseListener, aEvent ); +} + + +void CAIRO_GAL::BeginDrawing() throw( int ) +{ + // The size of the client area needs to be greater than zero + clientRectangle = parentWindow->GetClientRect(); + + if( clientRectangle.width == 0 || clientRectangle.height == 0 ) + throw EXCEPTION_ZERO_CLIENT_RECTANGLE; + + // clientDC = new wxClientDC( this ); + + // Create the CAIRO surface + cairoSurface = cairo_image_surface_create_for_data( (unsigned char*) bitmapBuffer, + CAIRO_FORMAT_RGB24, clientRectangle.width, + clientRectangle.height, stride ); + cairoImage = cairo_create( cairoSurface ); + + // ----------------------------------------------------------------- + + cairo_set_antialias( cairoImage, CAIRO_ANTIALIAS_SUBPIXEL ); + + // Clear the screen + ClearScreen(); + + // Compute the world <-> screen transformations + ComputeWorldScreenMatrix(); + + cairo_matrix_init( &cairoWorldScreenMatrix, worldScreenMatrix.m_data[0][0], + worldScreenMatrix.m_data[1][0], worldScreenMatrix.m_data[0][1], + worldScreenMatrix.m_data[1][1], worldScreenMatrix.m_data[0][2], + worldScreenMatrix.m_data[1][2] ); + + cairo_set_matrix( cairoImage, &cairoWorldScreenMatrix ); + + isSetAttributes = false; + + // Start drawing with a new path + cairo_new_path( cairoImage ); + isElementAdded = true; + + cairo_set_line_join( cairoImage, lineJoinMap[lineJoin] ); + cairo_set_line_cap( cairoImage, lineCapMap[lineCap] ); + + lineWidth = 0; + + isDeleteSavedPixels = true; +} + + +void CAIRO_GAL::EndDrawing() +{ + // Force remaining objects to be drawn + Flush(); + + // FIXME Accelerate support for wxWidgets 2.8.10 + +#if wxCHECK_VERSION( 2, 9, 0 ) + // Copy the cairo image contents to the wxBitmap + wxNativePixelData pixelData( *wxBitmap_ ); + + if( !pixelData ) + { + wxLogError( wxString::FromUTF8( "Can't access pixel data!" ) ); + return; + } + + wxNativePixelData::Iterator pixelIterator( pixelData ); + + int offset = 0; + + // Copy the cairo image to the wxDC bitmap + for( int j = 0; j < screenSizeY; j++ ) + { + offset = j * (int) screenSize.x; + + for( int column = 0; column < clientRectangle.width; column++ ) + { + unsigned int value = bitmapBuffer[offset + column]; + pixelIterator.Red() = value >> 16; + pixelIterator.Green() = value >> 8; + pixelIterator.Blue() = value >> 0; + pixelIterator++; + } + + pixelIterator.MoveTo( pixelData, 0, j ); + } + + // Blit the contents to the screen + wxClientDC client_dc( this ); + wxBufferedDC dc( &client_dc ); + dc.DrawBitmap( *wxBitmap_, 0, 0 ); + +#elif wxCHECK_VERSION( 2, 8, 0 ) + + // This code was taken from the wxCairo example - it's not the most efficient one + // Here is a good place for optimizations + + // Now translate the raw image data from the format stored + // by cairo into a format understood by wxImage. + unsigned char* wxOutputPtr = wxOutput; + for( size_t count = 0; count < bufferSize; count++ ) + { + unsigned int value = bitmapBuffer[count]; + // Red pixel + *wxOutputPtr++ = (value >> 16) & 0xff; + // Green pixel + *wxOutputPtr++ = (value >> 8) & 0xff; + // Blue pixel + *wxOutputPtr++ = (value >> 0) & 0xff; + } + + wxImage img( (int) screenSize.x, (int) screenSize.y, (unsigned char*) wxOutput, true); + wxBitmap bmp( img ); + wxClientDC client_dc( this ); + wxBufferedDC dc; + // client_dc.DrawBitmap(bmp, 0, 0, false); + dc.Init( &client_dc, bmp ); + +#else +#error "need wxWidgets-2.8 as a minimum" +#endif + + // Destroy Cairo objects + cairo_destroy( cairoImage ); + cairo_surface_destroy( cairoSurface ); +} + + +void CAIRO_GAL::SaveScreen() +{ + // Copy the current bitmap to the backup buffer + int offset = 0; + + for( int j = 0; j < screenSizeY; j++ ) + { + for( int i = 0; i < stride; i++ ) + { + bitmapBufferBackup[offset + i] = bitmapBuffer[offset + i]; + offset += stride; + } + } +} + + +void CAIRO_GAL::RestoreScreen() +{ + int offset = 0; + + for( int j = 0; j < screenSizeY; j++ ) + { + for( int i = 0; i < stride; i++ ) + { + bitmapBuffer[offset + i] = bitmapBufferBackup[offset + i]; + offset += stride; + } + } +} + + +void CAIRO_GAL::DrawLine( VECTOR2D aStartPoint, VECTOR2D aEndPoint ) +{ + cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y ); + cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y ); + isElementAdded = true; +} + + +void CAIRO_GAL::DrawCircle( VECTOR2D aCenterPoint, double aRadius ) +{ + // A circle is drawn using an arc + cairo_new_sub_path( cairoImage ); + cairo_arc( cairoImage, aCenterPoint.x, aCenterPoint.y, aRadius, 0.0, 2 * M_PI ); + isElementAdded = true; +} + + +void CAIRO_GAL::DrawArc( VECTOR2D aCenterPoint, double aRadius, double aStartAngle, + double aEndAngle ) +{ + cairo_new_sub_path( cairoImage ); + cairo_arc( cairoImage, aCenterPoint.x, aCenterPoint.y, aRadius, aStartAngle, aEndAngle ); + isElementAdded = true; +} + + +void CAIRO_GAL::DrawPolyline( std::deque& aPointList ) +{ + bool isFirstPoint = true; + + // Iterate over the point list and draw the segments + for( std::deque::iterator it = aPointList.begin(); it != aPointList.end(); ++it ) + { + if( isFirstPoint ) + { + cairo_move_to( cairoImage, it->x, it->y ); + isFirstPoint = false; + } + else + { + cairo_line_to( cairoImage, it->x, it->y ); + } + } + + isElementAdded = true; +} + + +void CAIRO_GAL::DrawPolygon( const std::deque& aPointList ) +{ + bool isFirstPoint = true; + + // Iterate over the point list and draw the polygon + for( std::deque::const_iterator it = aPointList.begin(); it != aPointList.end(); ++it ) + { + if( isFirstPoint ) + { + cairo_move_to( cairoImage, it->x, it->y ); + isFirstPoint = false; + } + else + { + cairo_line_to( cairoImage, it->x, it->y ); + } + } + + isElementAdded = true; +} + + +void CAIRO_GAL::DrawRectangle( VECTOR2D aStartPoint, VECTOR2D aEndPoint ) +{ + // Calculate the diagonal points + VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y ); + VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y ); + + // The path is composed from 4 segments + cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y ); + cairo_line_to( cairoImage, diagonalPointA.x, diagonalPointA.y ); + cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y ); + cairo_line_to( cairoImage, diagonalPointB.x, diagonalPointB.y ); + cairo_close_path( cairoImage ); + + isElementAdded = true; +} + + +void CAIRO_GAL::DrawCurve( VECTOR2D aStartPoint, VECTOR2D aControlPointA, + VECTOR2D aControlPointB, VECTOR2D aEndPoint ) +{ + cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y ); + cairo_curve_to( cairoImage, aControlPointA.x, aControlPointA.y, aControlPointB.x, + aControlPointB.y, aEndPoint.x, aEndPoint.y ); + cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y ); + isElementAdded = true; +} + + +void CAIRO_GAL::SetBackgroundColor( COLOR4D aColor ) +{ + backgroundColor = aColor; +} + + +void CAIRO_GAL::SetIsFill( bool aIsFillEnabled ) +{ + storePath(); + isFillEnabled = aIsFillEnabled; + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_SET_FILL; + groupElement.boolArgument = aIsFillEnabled; + groups.back().push_back( groupElement ); + } +} + + +void CAIRO_GAL::SetIsStroke( bool aIsStrokeEnabled ) +{ + storePath(); + isStrokeEnabled = aIsStrokeEnabled; + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_SET_STROKE; + groupElement.boolArgument = aIsStrokeEnabled; + groups.back().push_back( groupElement ); + } +} + + +void CAIRO_GAL::SetStrokeColor( COLOR4D aColor ) +{ + storePath(); + + strokeColor = aColor; + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_SET_STROKECOLOR; + groupElement.arguments[0] = strokeColor.r; + groupElement.arguments[1] = strokeColor.g; + groupElement.arguments[2] = strokeColor.b; + groupElement.arguments[3] = strokeColor.a; + groups.back().push_back( groupElement ); + } +} + + +void CAIRO_GAL::SetFillColor( COLOR4D aColor ) +{ + storePath(); + fillColor = aColor; + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_SET_FILLCOLOR; + groupElement.arguments[0] = fillColor.r; + groupElement.arguments[1] = fillColor.g; + groupElement.arguments[2] = fillColor.b; + groupElement.arguments[3] = fillColor.a; + groups.back().push_back( groupElement ); + } +} + + +void CAIRO_GAL::SetLineWidth( double aLineWidth ) +{ + storePath(); + + lineWidth = aLineWidth; + cairo_set_line_width( cairoImage, aLineWidth ); + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_SET_LINE_WIDTH; + groupElement.arguments[0] = aLineWidth; + groups.back().push_back( groupElement ); + } +} + + +void CAIRO_GAL::SetLineCap( LineCap aLineCap ) +{ + storePath(); + + lineCap = aLineCap; + + cairo_set_line_cap( cairoImage, lineCapMap[aLineCap] ); + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_SET_LINE_CAP; + groupElement.intArgument = (int) aLineCap; + groups.back().push_back( groupElement ); + } +} + + +void CAIRO_GAL::SetLineJoin( LineJoin aLineJoin ) +{ + storePath(); + + lineJoin = aLineJoin; + + cairo_set_line_join( cairoImage, lineJoinMap[aLineJoin] ); + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_SET_LINE_JOIN; + groupElement.intArgument = (int) aLineJoin; + groups.back().push_back( groupElement ); + } +} + + +void CAIRO_GAL::ClearScreen() +{ + // Clear screen + cairo_set_source_rgba( cairoImage, + backgroundColor.r, backgroundColor.g, backgroundColor.b, 1.0 ); + cairo_rectangle( cairoImage, 0.0, 0.0, screenSize.x, screenSize.y ); + cairo_fill( cairoImage ); +} + + +void CAIRO_GAL::Transform( MATRIX3x3D aTransformation ) +{ + cairo_matrix_t cairoTransformation; + + cairo_matrix_init( &cairoTransformation, + aTransformation.m_data[0][0], + aTransformation.m_data[1][0], + aTransformation.m_data[0][1], + aTransformation.m_data[1][1], + aTransformation.m_data[0][2], + aTransformation.m_data[1][2] ); + + cairo_transform( cairoImage, &cairoTransformation ); +} + + +void CAIRO_GAL::Rotate( double aAngle ) +{ + storePath(); + + cairo_rotate( cairoImage, aAngle ); + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_ROTATE; + groupElement.arguments[0] = aAngle; + groups.back().push_back( groupElement ); + } +} + + +void CAIRO_GAL::Translate( VECTOR2D aTranslation ) +{ + storePath(); + + cairo_translate( cairoImage, aTranslation.x, aTranslation.y ); + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_TRANSLATE; + groupElement.arguments[0] = aTranslation.x; + groupElement.arguments[1] = aTranslation.y; + groups.back().push_back( groupElement ); + } +} + + +void CAIRO_GAL::Scale( VECTOR2D aScale ) +{ + storePath(); + + cairo_scale( cairoImage, aScale.x, aScale.y ); + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_SCALE; + groupElement.arguments[0] = aScale.x; + groupElement.arguments[1] = aScale.y; + groups.back().push_back( groupElement ); + } +} + + +void CAIRO_GAL::Save() +{ + storePath(); + + cairo_save( cairoImage ); + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_SAVE; + groups.back().push_back( groupElement ); + } +} + + +void CAIRO_GAL::Restore() +{ + storePath(); + + cairo_restore( cairoImage ); + + if( isGrouping ) + { + GroupElement groupElement; + groupElement.command = CMD_RESTORE; + groups.back().push_back( groupElement ); + } +} + + +int CAIRO_GAL::BeginGroup() +{ + // If the grouping is started: the actual path is stored in the group, when + // a attribute was changed or when grouping stops with the end group method. + storePath(); + Group group; + groups.push_back( group ); + isGrouping = true; + return groups.size() - 1; +} + + +void CAIRO_GAL::EndGroup() +{ + storePath(); + isGrouping = false; +} + + +void CAIRO_GAL::DeleteGroup( int aGroupNumber ) +{ + storePath(); + + // Delete the Cairo paths + for( std::deque::iterator it = groups[aGroupNumber].begin(); + it != groups[aGroupNumber].end(); ++it ) + { + if( it->command == CMD_FILL_PATH || it->command == CMD_STROKE_PATH ) + { + cairo_path_destroy( it->cairoPath ); + } + } + + // Delete the group + groups.erase( groups.begin() + aGroupNumber ); +} + + +void CAIRO_GAL::DrawGroup( int aGroupNumber ) +{ + // This method implements a small Virtual Machine - all stored commands + // are executed; nested calling is also possible + + storePath(); + + for( Group::iterator it = groups[aGroupNumber].begin(); + it != groups[aGroupNumber].end(); ++it ) + { + switch( it->command ) + { + case CMD_SET_FILL: + isFillEnabled = it->boolArgument; + break; + + case CMD_SET_STROKE: + isStrokeEnabled = it->boolArgument; + break; + + case CMD_SET_FILLCOLOR: + fillColor = COLOR4D( it->arguments[0], it->arguments[1], it->arguments[2], + it->arguments[3] ); + break; + + case CMD_SET_STROKECOLOR: + strokeColor = COLOR4D( it->arguments[0], it->arguments[1], it->arguments[2], + it->arguments[3] ); + break; + + case CMD_SET_LINE_WIDTH: + cairo_set_line_width( cairoImage, it->arguments[0] ); + break; + + case CMD_SET_LINE_JOIN: + cairo_set_line_join( cairoImage, lineJoinMap[(LineJoin) ( it->intArgument )] ); + break; + + case CMD_SET_LINE_CAP: + cairo_set_line_cap( cairoImage, lineCapMap[(LineCap) ( it->intArgument )] ); + break; + + case CMD_STROKE_PATH: + cairo_set_source_rgba( cairoImage, strokeColor.r, strokeColor.g, strokeColor.b, + strokeColor.a ); + cairo_append_path( cairoImage, it->cairoPath ); + cairo_stroke( cairoImage ); + break; + + case CMD_FILL_PATH: + cairo_set_source_rgba( cairoImage, fillColor.r, fillColor.g, fillColor.b, + fillColor.a ); + cairo_append_path( cairoImage, it->cairoPath ); + cairo_fill( cairoImage ); + break; + + case CMD_TRANSFORM: + cairo_matrix_t matrix; + cairo_matrix_init( &matrix, it->arguments[0], it->arguments[1], it->arguments[2], + it->arguments[3], it->arguments[4], it->arguments[5] ); + cairo_transform( cairoImage, &matrix ); + break; + + case CMD_ROTATE: + cairo_rotate( cairoImage, it->arguments[0] ); + break; + + case CMD_TRANSLATE: + cairo_translate( cairoImage, it->arguments[0], it->arguments[1] ); + break; + + case CMD_SCALE: + cairo_scale( cairoImage, it->arguments[0], it->arguments[1] ); + break; + + case CMD_SAVE: + cairo_save( cairoImage ); + break; + + case CMD_RESTORE: + cairo_restore( cairoImage ); + break; + + case CMD_CALL_GROUP: + DrawGroup( it->intArgument ); + break; + } + } +} + + +void CAIRO_GAL::Flush() +{ + storePath(); +} + + +void CAIRO_GAL::ComputeWorldScreenMatrix() +{ + ComputeWorldScale(); + + worldScreenMatrix.SetIdentity(); + + MATRIX3x3D translation; + translation.SetIdentity(); + translation.SetTranslation( 0.5 * VECTOR2D( screenSize.x, screenSize.y ) ); + + MATRIX3x3D scale; + scale.SetIdentity(); + scale.SetScale( VECTOR2D( worldScale, -worldScale ) ); + + MATRIX3x3D lookat; + lookat.SetIdentity(); + lookat.SetTranslation( -lookAtPoint ); + + worldScreenMatrix = translation * scale * lookat * worldScreenMatrix; +} + + +void CAIRO_GAL::storePath() +{ + if( isElementAdded ) + { + isElementAdded = false; + + if( !isGrouping ) + { + if( isFillEnabled ) + { + cairo_set_source_rgba( cairoImage, fillColor.r, fillColor.g, fillColor.b, + fillColor.a ); + cairo_fill_preserve( cairoImage ); + } + + if( isStrokeEnabled ) + { + cairo_set_source_rgba( cairoImage, strokeColor.r, strokeColor.g, strokeColor.b, + strokeColor.a ); + cairo_stroke_preserve( cairoImage ); + } + } + else + { + // Copy the actual path, append it to the global path list + // then check, if the path needs to be stroked/filled and + // add this command to the group list; + + cairo_path_t* path = cairo_copy_path( cairoImage ); + pathList.push_back( path ); + + if( isStrokeEnabled ) + { + GroupElement groupElement; + groupElement.cairoPath = path; + groupElement.command = CMD_STROKE_PATH; + groups.back().push_back( groupElement ); + } + + if( isFillEnabled ) + { + GroupElement groupElement; + groupElement.cairoPath = path; + groupElement.command = CMD_FILL_PATH; + groups.back().push_back( groupElement ); + } + } + + cairo_new_path( cairoImage ); + } +} + + +// --------------- +// Cursor handling +// --------------- + + +void CAIRO_GAL::initCursor( int aCursorSize ) +{ + cursorPixels = new wxBitmap( aCursorSize, aCursorSize ); + cursorPixelsSaved = new wxBitmap( aCursorSize, aCursorSize ); + cursorSize = aCursorSize; + + wxMemoryDC cursorShape( *cursorPixels ); + + cursorShape.SetBackground( *wxTRANSPARENT_BRUSH ); + wxColour color( cursorColor.r * cursorColor.a * 255, cursorColor.g * cursorColor.a * 255, + cursorColor.b * cursorColor.a * 255, 255 ); + wxPen pen = wxPen( color ); + cursorShape.SetPen( pen ); + cursorShape.Clear(); + + cursorShape.DrawLine( 0, aCursorSize / 2, aCursorSize, aCursorSize / 2 ); + cursorShape.DrawLine( aCursorSize / 2, 0, aCursorSize / 2, aCursorSize ); +} + + +VECTOR2D CAIRO_GAL::ComputeCursorToWorld( VECTOR2D aCursorPosition ) +{ + + MATRIX3x3D inverseMatrix = worldScreenMatrix.Inverse(); + VECTOR2D cursorPositionWorld = inverseMatrix * aCursorPosition; + + return cursorPositionWorld; +} + + +void CAIRO_GAL::DrawCursor( VECTOR2D aCursorPosition ) +{ + if( !IsShownOnScreen() ) + return; + + wxClientDC clientDC( this ); + wxMemoryDC cursorSave( *cursorPixelsSaved ); + wxMemoryDC cursorShape( *cursorPixels ); + + // Snap to grid + VECTOR2D cursorPositionWorld = ComputeCursorToWorld( aCursorPosition ); + + cursorPositionWorld.x = round( cursorPositionWorld.x / gridSize.x ) * gridSize.x; + cursorPositionWorld.y = round( cursorPositionWorld.y / gridSize.y ) * gridSize.y; + aCursorPosition = worldScreenMatrix * cursorPositionWorld; + aCursorPosition = aCursorPosition - VECTOR2D( cursorSize / 2, cursorSize / 2 ); + + if( !isDeleteSavedPixels ) + { + clientDC.Blit( savedCursorPosition.x, savedCursorPosition.y, cursorSize, cursorSize, + &cursorSave, 0, 0 ); + } + else + { + isDeleteSavedPixels = false; + } + + cursorSave.Blit( 0, 0, cursorSize, cursorSize, &clientDC, aCursorPosition.x, + aCursorPosition.y ); + + clientDC.Blit( aCursorPosition.x, aCursorPosition.y, cursorSize, cursorSize, &cursorShape, 0, + 0, wxOR ); + + savedCursorPosition.x = (wxCoord) aCursorPosition.x; + savedCursorPosition.y = (wxCoord) aCursorPosition.y; +} + + +void CAIRO_GAL::DrawGridLine( VECTOR2D aStartPoint, VECTOR2D aEndPoint ) +{ + cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y ); + cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y ); + cairo_set_source_rgba( cairoImage, gridColor.r, gridColor.g, gridColor.b, gridColor.a ); + cairo_stroke( cairoImage ); +} + + +void CAIRO_GAL::allocateBitmaps() +{ + // Create buffer, use the system independent CAIRO image back end + stride = cairo_format_stride_for_width( CAIRO_FORMAT_RGB24, screenSize.x ); + bufferSize = stride * screenSize.y; + + bitmapBuffer = new unsigned int[bufferSize]; + bitmapBufferBackup = new unsigned int[bufferSize]; + wxOutput = new unsigned char[bufferSize * 4]; + wxBitmap_ = new wxBitmap( screenSize.x, screenSize.y, SCREEN_DEPTH ); +} + + +void CAIRO_GAL::deleteBitmaps() +{ + delete[] bitmapBuffer; + delete[] bitmapBufferBackup; + delete[] wxOutput; + delete wxBitmap_; +} + + +bool CAIRO_GAL::Show( bool aShow ) +{ + bool s = wxWindow::Show( aShow ); + + if( aShow ) + wxWindow::Raise(); + + return s; +} diff --git a/common/gal/graphics_abstraction_layer.cpp b/common/gal/graphics_abstraction_layer.cpp new file mode 100644 index 0000000000..e34efb2cba --- /dev/null +++ b/common/gal/graphics_abstraction_layer.cpp @@ -0,0 +1,157 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Graphics Abstraction Layer (GAL) - base class + * + * 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 + */ + +#include + +#include +#include +#include + + +using namespace KiGfx; + +const wxEventType KiGfx::EVT_GAL_REDRAW = wxNewEventType(); + +GAL::GAL() +{ + // Set the default values for the internal variables + SetIsFill( false ); + SetIsStroke( true ); + SetLineJoin( LINE_JOIN_ROUND ); + SetLineCap( LINE_CAP_ROUND ); + SetIsCursorEnabled( false ); + SetZoomFactor( 1.0 ); + SetFillColor( COLOR4D( 0.0, 0.0, 0.0, 0.0 ) ); + SetStrokeColor( COLOR4D( 1.0, 1.0, 1.0, 1.0 ) ); + SetGridColor( COLOR4D( 1, 1, 1, 0.1 ) ); + SetCoarseGrid( 5 ); + SetLineWidth( 1.0 ); + SetDepthRange( VECTOR2D( -2048, 2047 ) ); +} + + +GAL::~GAL() +{ +} + + +void GAL::DrawGrid() +{ + // The grid consists of lines + // For the drawing the start points, end points and increments have to be calculated in world coordinates + VECTOR2D screenStartPoint( 0, 0 ); + VECTOR2D screenEndPoint( screenSize.x, screenSize.y ); + MATRIX3x3D inverseMatrix = worldScreenMatrix.Inverse(); + VECTOR2D worldStartPoint = inverseMatrix * screenStartPoint; + VECTOR2D worldEndPoint = inverseMatrix * screenEndPoint; + + // Compute grid variables + int gridStartX = round( worldStartPoint.x / gridSize.x ); + int gridEndX = round( worldEndPoint.x / gridSize.x ); + int gridStartY = round( worldStartPoint.y / gridSize.y ); + int gridEndY = round( worldEndPoint.y / gridSize.y ); + + int gridScreenSizeDense = round( gridSize.x * worldScale ); + int gridScreenSizeCoarse = round( gridSize.x * (double) gridTick * worldScale ); + + // Swap the coordinates, if they have not the right order + SWAP( gridEndX, <, gridStartX ); + SWAP( gridEndY, <, gridStartY ); + + // Correct the index, else some lines are not correctly painted + gridStartX -= 1; + gridStartY -= 1; + gridEndX += 1; + gridEndY += 1; + + double savedLineWidth = GetLineWidth(); + COLOR4D savedColor = GetStrokeColor(); + + // Compute the line width of the grid + ComputeWorldScale(); + double width = gridLineWidth / worldScale; + double doubleWidth = 2 * width; + + // Set line width & color + SetLineWidth( width ); + + double origSize = (double) gridOriginMarkerSize / worldScale; + + SetStrokeColor( COLOR4D( 1.0, 1.0, 1.0, 1.0 ) ); + SetIsFill( false ); + DrawLine( gridOrigin + VECTOR2D( -origSize, -origSize ), gridOrigin + VECTOR2D( origSize, origSize ) ); + DrawLine( gridOrigin + VECTOR2D( -origSize, origSize ), gridOrigin + VECTOR2D( origSize, -origSize ) ); + DrawCircle( gridOrigin, origSize * 0.7 ); + + SetStrokeColor( gridColor ); + + if( std::max( gridScreenSizeDense, gridScreenSizeCoarse ) < gridDrawThreshold ) + return; + + // Now draw the grid, every coarse grid line gets the double width + for( int j = gridStartY; j < gridEndY; j += 1 ) + { + if( j % gridTick == 0 && gridScreenSizeDense > gridDrawThreshold ) + { + SetLineWidth( doubleWidth ); + } + else + { + SetLineWidth( width ); + } + + if( ( j % gridTick == 0 && gridScreenSizeCoarse > gridDrawThreshold ) + || gridScreenSizeDense > gridDrawThreshold ) + { + DrawGridLine( VECTOR2D( gridStartX * gridSize.x, j * gridSize.y ), + VECTOR2D( gridEndX * gridSize.x, j * gridSize.y ) ); + } + } + + for( int i = gridStartX; i < gridEndX; i += 1 ) + { + if( i % gridTick == 0 && gridScreenSizeDense > gridDrawThreshold ) + { + SetLineWidth( doubleWidth ); + } + else + { + SetLineWidth( width ); + } + + if( ( i % gridTick == 0 && gridScreenSizeCoarse > gridDrawThreshold ) + || gridScreenSizeDense > gridDrawThreshold ) + { + DrawGridLine( VECTOR2D( i * gridSize.x, gridStartY * gridSize.y ), + VECTOR2D( i * gridSize.x, gridEndY * gridSize.y ) ); + } + } + + // Restore old values + SetLineWidth( savedLineWidth ); + SetStrokeColor( savedColor ); +} + diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp new file mode 100644 index 0000000000..853c69d5d4 --- /dev/null +++ b/common/gal/opengl/opengl_gal.cpp @@ -0,0 +1,1591 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Graphics Abstraction Layer (GAL) for OpenGL + * + * 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 + */ + +#include +#include +#include + +#include + +#ifndef CALLBACK +#define CALLBACK +#endif + +using namespace KiGfx; + +// Prototypes +void InitTesselatorCallbacks( GLUtesselator* aTesselator ); + +// FIXME Checking of attributes + +// #if defined(__WXGTK__) +const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0 }; +// #elif defined(__WXMSW__) +// #define glAttributes NULL +// #endif + + +OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener, + wxEvtHandler* aPaintListener, bool isUseShaders, const wxString& aName ) : + wxGLCanvas( aParent, wxID_ANY, (int*) glAttributes, wxDefaultPosition, wxDefaultSize, + wxEXPAND, aName ) +{ + // Create the OpenGL-Context + glContext = new wxGLContext( this ); + parentWindow = aParent; + mouseListener = aMouseListener; + paintListener = aPaintListener; + + // Set the cursor size + initCursor( 20 ); + SetCursorColor( COLOR4D( 1.0, 1.0, 1.0, 1.0 ) ); + + // Initialize the flags + isCreated = false; + isDeleteSavedPixels = true; + isGlewInitialized = false; + isFrameBufferInitialized = false; + isUseShader = isUseShaders; + isShaderInitialized = false; + isGroupStarted = false; + shaderPath = "../../../gal/opengl/shader/"; + wxSize parentSize = aParent->GetSize(); + + SetSize( parentSize ); + + screenSize.x = parentSize.x; + screenSize.y = parentSize.y; + + currentShader = -1; + + // Set grid defaults + SetGridColor( COLOR4D( 0.3, 0.3, 0.3, 0.3 ) ); + SetCoarseGrid( 10 ); + SetGridLineWidth( 1.0 ); + + // Connecting the event handlers. + this->Connect( wxEVT_SIZE, wxSizeEventHandler( OPENGL_GAL::onSize ) ); + this->Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::onPaint ) ); + + // Mouse events are skipped to the parent + this->Connect( wxEVT_MOTION, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + this->Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + this->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + this->Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + this->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); + this->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); +} + + +OPENGL_GAL::~OPENGL_GAL() +{ + glFlush(); + + // Delete the stored display lists + for( std::deque::iterator group = displayListsGroup.begin(); + group != displayListsGroup.end(); group++ ) + { + glDeleteLists( *group, 1 ); + } + + // Delete the buffers + if( isFrameBufferInitialized ) + { + deleteFrameBuffer( &frameBuffer, &depthBuffer, &texture ); + deleteFrameBuffer( &frameBufferBackup, &depthBufferBackup, &textureBackup ); + } + + delete glContext; +} + + +void OPENGL_GAL::onPaint( wxPaintEvent& aEvent ) +{ + PostPaint(); +} + + +void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight ) +{ + screenSize = VECTOR2D( aWidth, aHeight ); + + // Delete old buffers for resizing + if( isFrameBufferInitialized ) + { + deleteFrameBuffer( &frameBuffer, &depthBuffer, &texture ); + deleteFrameBuffer( &frameBufferBackup, &depthBufferBackup, &textureBackup ); + + // This flag is used for recreating the buffers + isFrameBufferInitialized = false; + } + + wxGLCanvas::SetSize( aWidth, aHeight ); +} + + +void OPENGL_GAL::onSize( wxSizeEvent& aEvent ) +{ + ResizeScreen( aEvent.GetSize().x, aEvent.GetSize().y ); + PostPaint(); +} + + +void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent ) +{ + // Post the mouse event to the event listener registered in constructor, if any + if( mouseListener ) + wxPostEvent( mouseListener, aEvent ); +} + + +void OPENGL_GAL::generateFrameBuffer( GLuint* aFrameBuffer, GLuint* aDepthBuffer, + GLuint* aTexture ) +{ + // We need frame buffer objects for drawing the screen contents + + // Generate frame buffer and a depth buffer + glGenFramebuffersEXT( 1, aFrameBuffer ); + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, *aFrameBuffer ); + + // Allocate memory for the depth buffer + // Attach the depth buffer to the frame buffer + glGenRenderbuffersEXT( 1, aDepthBuffer ); + glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, *aDepthBuffer ); + + // Use here a size of 24 bits for the depth buffer, 8 bits for the stencil buffer + // this is required later for anti-aliasing + glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, screenSize.x, + screenSize.y ); + glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, + *aDepthBuffer ); + glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, *aDepthBuffer ); + + // Generate the texture for the pixel storage + // Attach the texture to the frame buffer + glGenTextures( 1, aTexture ); + glBindTexture( GL_TEXTURE_2D, *aTexture ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, screenSize.x, screenSize.y, 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, + *aTexture, 0 ); + + // Check the status, exit if the frame buffer can't be created + GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ); + + if( status != GL_FRAMEBUFFER_COMPLETE_EXT ) + { + wxLogError( wxT( "Can't create the frame buffer." ) ); + exit( 1 ); + } + + isFrameBufferInitialized = true; +} + + +void OPENGL_GAL::deleteFrameBuffer( GLuint* aFrameBuffer, GLuint* aDepthBuffer, GLuint* aTexture ) +{ + glDeleteFramebuffers( 1, aFrameBuffer ); + glDeleteRenderbuffers( 1, aDepthBuffer ); + glDeleteTextures( 1, aTexture ); +} + + +void OPENGL_GAL::initFrameBuffers() +{ + generateFrameBuffer( &frameBuffer, &depthBuffer, &texture ); + generateFrameBuffer( &frameBufferBackup, &depthBufferBackup, &textureBackup ); +} + + +void OPENGL_GAL::SaveScreen() +{ + glBindFramebuffer( GL_DRAW_FRAMEBUFFER, frameBufferBackup ); + glBindFramebuffer( GL_READ_FRAMEBUFFER, frameBuffer ); + glBlitFramebuffer( 0, 0, screenSize.x, screenSize.y, 0, 0, screenSize.x, screenSize.y, + GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, + GL_NEAREST ); + glBindFramebuffer( GL_DRAW_FRAMEBUFFER, frameBuffer ); +} + + +void OPENGL_GAL::RestoreScreen() +{ + glBindFramebuffer( GL_DRAW_FRAMEBUFFER, frameBuffer ); + glBindFramebuffer( GL_READ_FRAMEBUFFER, frameBufferBackup ); + glBlitFramebuffer( 0, 0, screenSize.x, screenSize.y, 0, 0, screenSize.x, screenSize.y, + GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, + GL_NEAREST ); +} + + +void OPENGL_GAL::initGlew() +{ + // Initialize GLEW library + GLenum err = glewInit(); + + if( GLEW_OK != err ) + { + wxLogError( wxString::FromUTF8( (char*) glewGetErrorString( err ) ) ); + exit( 1 ); + } + else + { + wxLogDebug( wxString( "Status: Using GLEW " ) + + wxString::FromUTF8( (char*) glewGetString( GLEW_VERSION ) ) ); + } + + // Check the OpenGL version (minimum 2.1 is required) + if( GLEW_VERSION_2_1 ) + { + wxLogInfo( wxT( "OpenGL Version 2.1 supported." ) ); + } + else + { + wxLogError( wxT( "OpenGL Version 2.1 is not supported!" ) ); + exit( 1 ); + } + + // Frame buffers have to be supported + if( !GLEW_ARB_framebuffer_object ) + { + wxLogError( wxT( "Framebuffer objects are not supported!" ) ); + exit( 1 ); + } + + // Compute the unit circles, used for speed up of the circle drawing + computeUnitCircle(); + computeUnitSemiCircle(); + computeUnitArcs(); + + isGlewInitialized = true; +} + + +void OPENGL_GAL::BeginDrawing() +{ + SetCurrent( *glContext ); + + clientDC = new wxClientDC( this ); + + // Initialize GLEW & FBOs + if( !isGlewInitialized ) + { + initGlew(); + } + + if( !isFrameBufferInitialized ) + { + initFrameBuffers(); + } + + // Compile the shaders + if( !isShaderInitialized && isUseShader ) + { + std::string shaderNames[SHADER_NUMBER] = { std::string( "round" ) }; + + for( int i = 0; i < 1; i++ ) + { + shaderList.push_back( SHADER() ); + + shaderList[i].AddSource( shaderPath + std::string( "/" ) + shaderNames[i] + + std::string( ".frag" ), SHADER_TYPE_FRAGMENT ); + shaderList[i].AddSource( shaderPath + std::string( "/" ) + shaderNames[i] + + std::string( ".vert" ), SHADER_TYPE_VERTEX ); + + shaderList[i].Link(); + } + + isShaderInitialized = true; + } + + // Bind the main frame buffer object - all contents are drawn there + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, frameBuffer ); + + // Disable 2D Textures + glDisable( GL_TEXTURE_2D ); + + // Enable the depth buffer + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LESS ); + // Setup blending, required for transparent objects + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + // Enable smooth lines + glEnable( GL_LINE_SMOOTH ); + + // Set up the view port + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glViewport( 0, 0, (GLsizei) screenSize.x, (GLsizei) screenSize.y ); + + // Create the screen transformation + glOrtho( 0, (GLint) screenSize.x, 0, (GLsizei) screenSize.y, -depthRange.x, -depthRange.y ); + + glMatrixMode( GL_MODELVIEW ); + + // Set up the world <-> screen transformation + ComputeWorldScreenMatrix(); + GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; + matrixData[0] = worldScreenMatrix.m_data[0][0]; + matrixData[1] = worldScreenMatrix.m_data[1][0]; + matrixData[2] = worldScreenMatrix.m_data[2][0]; + matrixData[4] = worldScreenMatrix.m_data[0][1]; + matrixData[5] = worldScreenMatrix.m_data[1][1]; + matrixData[6] = worldScreenMatrix.m_data[2][1]; + matrixData[12] = worldScreenMatrix.m_data[0][2]; + matrixData[13] = worldScreenMatrix.m_data[1][2]; + matrixData[14] = worldScreenMatrix.m_data[2][2]; + glLoadMatrixd( matrixData ); + + // Set defaults + SetFillColor( fillColor ); + SetStrokeColor( strokeColor ); + isDeleteSavedPixels = true; +} + + +void OPENGL_GAL::blitMainTexture( bool aIsClearFrameBuffer ) +{ + selectShader( -1 ); + // Don't use blending for the final blitting + glDisable( GL_BLEND ); + + glColor4d( 1.0, 1.0, 1.0, 1.0 ); + + // Switch to the main frame buffer and blit the scene + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); + + if( aIsClearFrameBuffer ) + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); + + // Enable texturing and bind the main texture + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, texture ); + + // Draw a full screen quad with the texture + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glLoadIdentity(); + glBegin( GL_QUADS ); + glTexCoord2i( 0, 1 ); + glVertex3i( -1, -1, 0 ); + glTexCoord2i( 1, 1 ); + glVertex3i( 1, -1, 0 ); + glTexCoord2i( 1, 0 ); + glVertex3i( 1, 1, 0 ); + glTexCoord2i( 0, 0 ); + glVertex3i( -1, 1, 0 ); + glEnd(); + glPopMatrix(); + glMatrixMode( GL_MODELVIEW ); + glPopMatrix(); +} + + +void OPENGL_GAL::EndDrawing() +{ + // Draw the remaining contents, blit the main texture to the screen, swap the buffers + glFlush(); + blitMainTexture( true ); + SwapBuffers(); + + delete clientDC; +} + + +inline void OPENGL_GAL::selectShader( int aIndex ) +{ + if( currentShader != aIndex ) + { + if( currentShader >= 0 ) + shaderList[currentShader].Deactivate(); + + if( aIndex >= 0 ) + shaderList[aIndex].Use(); + + currentShader = aIndex; + } +} + +void OPENGL_GAL::drawRoundedSegment( VECTOR2D aStartPoint, VECTOR2D aEndPoint, + double aWidth, bool aStroke, bool aGlBegin ) +{ + VECTOR2D l = (aEndPoint - aStartPoint); + double lnorm = l.EuclideanNorm(); + double aspect; + + if( l.x == 0 && l.y == 0 ) + { + l = VECTOR2D( aWidth / 2.0, 0.0 ); + aspect = 0.0; + } + else + { + l = l.Resize( aWidth / 2.0 ); + aspect = lnorm / (lnorm + aWidth); + } + + VECTOR2D p = l.Perpendicular(); + VECTOR2D corners[4] = { aStartPoint - l - p, aEndPoint + l - p, + aEndPoint + l + p, aStartPoint - l + p }; + + if( aStroke ) + { + glColor4d( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + } + else + { + glColor4d( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + } + + selectShader( 0 ); + + if( aGlBegin ) + glBegin( GL_QUADS ); + + glNormal3d( aspect, 0, 0 ); + glTexCoord2f( 0.0, 0.0 ); + glVertex3d( corners[0].x, corners[0].y, layerDepth ); + glNormal3d( aspect, 0, 0 ); + glTexCoord2f( 1.0, 0.0 ); + glVertex3d( corners[1].x, corners[1].y, layerDepth ); + glNormal3d( aspect, 0, 0 ); + glTexCoord2f( 1.0, 1.0 ); + glVertex3d( corners[2].x, corners[2].y, layerDepth ); + glNormal3d( aspect, 0, 0 ); + glTexCoord2f( 0.0, 1.0 ); + glVertex3d( corners[3].x, corners[3].y, layerDepth ); + + if( aGlBegin ) + glEnd(); +} + + +inline void OPENGL_GAL::drawLineQuad( VECTOR2D aStartPoint, VECTOR2D aEndPoint ) +{ + VECTOR2D startEndVector = aEndPoint - aStartPoint; + double lineLength = startEndVector.EuclideanNorm(); + + // Limit the width of the line to a minimum of one pixel + // this looks best without anti-aliasing + // XXX Should be improved later. + double scale = 0.5 * lineWidth / lineLength; + double scale1pix = 0.5001 / worldScale / lineLength; + if( lineWidth * worldScale < 1.0002 && !isGroupStarted ) + { + scale = scale1pix; + } + + VECTOR2D perpendicularVector( -startEndVector.y * scale, startEndVector.x * scale ); + + // Compute the edge points of the line + VECTOR2D point1 = aStartPoint + perpendicularVector; + VECTOR2D point2 = aStartPoint - perpendicularVector; + VECTOR2D point3 = aEndPoint + perpendicularVector; + VECTOR2D point4 = aEndPoint - perpendicularVector; + + glBegin( GL_QUADS ); + glVertex3d( point1.x, point1.y, layerDepth ); + glVertex3d( point2.x, point2.y, layerDepth ); + glVertex3d( point4.x, point4.y, layerDepth ); + glVertex3d( point3.x, point3.y, layerDepth ); + glEnd(); +} + + +inline void OPENGL_GAL::drawLineCap( VECTOR2D aStartPoint, VECTOR2D aEndPoint, double aDepthOffset ) +{ + VECTOR2D startEndVector = aEndPoint - aStartPoint; + double lineLength = startEndVector.EuclideanNorm(); + double lineAngle = atan2( startEndVector.y, startEndVector.x ); + + switch( lineCap ) + { + case LINE_CAP_BUTT: + // TODO + break; + + case LINE_CAP_ROUND: + // Add a semicircle at the line end + drawSemiCircle( aStartPoint, lineWidth / 2, lineAngle + M_PI / 2, aDepthOffset ); + break; + + case LINE_CAP_SQUARED: + VECTOR2D offset; + offset = startEndVector * ( lineWidth / lineLength / 2.0 ); + aStartPoint = aStartPoint - offset; + aEndPoint = aEndPoint + offset; + break; + } +} + + +void OPENGL_GAL::DrawLine( VECTOR2D aStartPoint, VECTOR2D aEndPoint ) +{ + if( isUseShader ) + { + drawRoundedSegment( aStartPoint, aEndPoint, lineWidth, true, true ); + } + else + { + VECTOR2D startEndVector = aEndPoint - aStartPoint; + double lineLength = startEndVector.EuclideanNorm(); + if( lineLength > 0.0 ) + { + glColor4d( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + + drawLineCap( aStartPoint, aEndPoint, layerDepth ); + drawLineCap( aEndPoint, aStartPoint, layerDepth ); + drawLineQuad( aStartPoint, aEndPoint ); + } + } +} + + +void OPENGL_GAL::DrawPolyline( std::deque& aPointList ) +{ + LineCap savedLineCap = lineCap; + + bool isFirstPoint = true; + bool isFirstLine = true; + VECTOR2D startEndVector; + VECTOR2D lastStartEndVector; + VECTOR2D lastPoint; + + unsigned int i = 0; + + // Draw for each segment a line + for( std::deque::const_iterator it = aPointList.begin(); it != aPointList.end(); it++ ) + { + // First point + if( it == aPointList.begin() ) + { + isFirstPoint = false; + lastPoint = *it; + } + else + { + VECTOR2D actualPoint = *it; + startEndVector = actualPoint - lastPoint; + + if( isFirstLine ) + { + drawLineCap( lastPoint, actualPoint, layerDepth ); + isFirstLine = false; + } + else + { + // Compute some variables for the joints + double lineLengthA = lastStartEndVector.EuclideanNorm(); + double scale = 0.5 * lineWidth / lineLengthA; + VECTOR2D perpendicularVector1( -lastStartEndVector.y * scale, + lastStartEndVector.x * scale ); + double lineLengthB = startEndVector.EuclideanNorm(); + scale = 0.5 * lineWidth / lineLengthB; + VECTOR2D perpendicularVector2( -startEndVector.y * scale, + startEndVector.x * scale ); + + switch( lineJoin ) + { + case LINE_JOIN_ROUND: + { + // Insert a triangle fan at the line joint + // Compute the start and end angle for the triangle fan + double angle1 = startEndVector.Angle(); + double angle2 = lastStartEndVector.Angle(); + double angleDiff = angle1 - angle2; + // Determines the side of the triangle fan + double adjust = angleDiff < 0 ? -0.5 * lineWidth : 0.5 * lineWidth; + + // Angle correction for some special cases + if( angleDiff < -M_PI ) + { + if( angle1 < 0 ) + { + angle1 += 2 * M_PI; + } + if( angle2 < 0 ) + { + angle2 += 2 * M_PI; + } + adjust = -adjust; + } + else if( angleDiff > M_PI ) + { + if( angle1 > 0 ) + { + angle1 -= 2 * M_PI; + + } + if( angle2 > 0 ) + { + angle2 -= 2 * M_PI; + } + adjust = -adjust; + } + + // Now draw the fan + glBegin( GL_TRIANGLE_FAN ); + glVertex3d( lastPoint.x, lastPoint.y, layerDepth ); + + SWAP( angle1, >, angle2 ); + + glVertex3d( lastPoint.x + adjust * sin( angle1 ), + lastPoint.y - adjust * cos( angle1 ), layerDepth ); + for( double a = angle1 + M_PI / 32; a < angle2; a += M_PI / 32 ) + { + glVertex3d( lastPoint.x + adjust * sin( a ), + lastPoint.y - adjust * cos( a ), layerDepth ); + } + glVertex3d( lastPoint.x + adjust * sin( angle2 ), + lastPoint.y - adjust * cos( angle2 ), layerDepth ); + + glEnd(); + break; + } + case LINE_JOIN_BEVEL: + { + // We compute the edge points of the line segments at the joint + VECTOR2D edgePoint1; + VECTOR2D edgePoint2; + // Determine the correct side + if( lastStartEndVector.x * startEndVector.y + - lastStartEndVector.y * startEndVector.x + < 0 ) + { + edgePoint1 = lastPoint + perpendicularVector1; + edgePoint2 = lastPoint + perpendicularVector2; + } + else + { + edgePoint1 = lastPoint - perpendicularVector1; + edgePoint2 = lastPoint - perpendicularVector2; + } + + // Insert a triangle at the joint to close the gap + glBegin( GL_TRIANGLES ); + glVertex3d( edgePoint1.x, edgePoint1.y, layerDepth ); + glVertex3d( edgePoint2.x, edgePoint2.y, layerDepth ); + glVertex3d( lastPoint.x, lastPoint.y, layerDepth ); + glEnd(); + + break; + } + case LINE_JOIN_MITER: + { + // Compute points of the outer edges + VECTOR2D point1 = lastPoint - perpendicularVector1; + VECTOR2D point3 = lastPoint - perpendicularVector2; + if( lastStartEndVector.x * startEndVector.y + - lastStartEndVector.y * startEndVector.x + < 0 ) + { + point1 = lastPoint + perpendicularVector1; + point3 = lastPoint + perpendicularVector2; + + } + + VECTOR2D point2 = point1 - lastStartEndVector; + VECTOR2D point4 = point3 + startEndVector; + + // Now compute the intersection point of the edges + double c1 = point1.Cross( point2 ); + double c2 = point3.Cross( point4 ); + double quot = startEndVector.Cross( lastStartEndVector ); + + VECTOR2D miterPoint( -c1 * startEndVector.x - c2 * lastStartEndVector.x, + -c1 * startEndVector.y - c2 * lastStartEndVector.y ); + + miterPoint = ( 1 / quot ) * miterPoint; + + // Check if the point is outside the limit + if( ( lastPoint - miterPoint ).EuclideanNorm() > 2 * lineWidth ) + { + // if it's outside cut the edge and insert three triangles + double limit = MITER_LIMIT * lineWidth; + VECTOR2D mp1 = point1 + ( limit / lineLengthA ) * lastStartEndVector; + VECTOR2D mp2 = point3 - ( limit / lineLengthB ) * startEndVector; + glBegin( GL_TRIANGLE_FAN ); + glVertex3d( lastPoint.x, lastPoint.y, layerDepth ); + glVertex3d( point1.x, point1.y, layerDepth ); + glVertex3d( mp1.x, mp1.y, layerDepth ); + glVertex3d( mp2.x, mp2.y, layerDepth ); + glVertex3d( point3.x, point3.y, layerDepth ); + glEnd(); + } + else + { + // Insert two triangles for the mitered edge + glBegin( GL_TRIANGLE_FAN ); + glVertex3d( lastPoint.x, lastPoint.y, layerDepth ); + glVertex3d( point1.x, point1.y, layerDepth ); + glVertex3d( miterPoint.x, miterPoint.y, layerDepth ); + glVertex3d( point3.x, point3.y, layerDepth ); + glEnd(); + } + break; + } + } + } + + if( it == aPointList.end() - 1 ) + { + drawLineCap( actualPoint, lastPoint, layerDepth ); + } + + drawLineQuad( lastPoint, *it ); + lastPoint = *it; + lastStartEndVector = startEndVector; + } + + i++; + } + + lineCap = savedLineCap; +} + + +void OPENGL_GAL::DrawRectangle( VECTOR2D aStartPoint, VECTOR2D aEndPoint ) +{ + // Compute the diagonal points of the rectangle + VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y ); + VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y ); + + if( isUseShader ) + { + if( isFillEnabled ) + { + selectShader( 0 ); + glColor4d( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + glBegin( GL_QUADS ); + glNormal3d( 1.0, 0, 0); + glTexCoord2f(0.0, 0.0); + glVertex3d( aStartPoint.x, aStartPoint.y, layerDepth ); + glNormal3d( 1.0, 0, 0); + glTexCoord2f(1.0, 0.0); + glVertex3d( diagonalPointA.x, diagonalPointA.y, layerDepth ); + glNormal3d( 1.0, 0, 0); + glTexCoord2f(1.0, 1.0); + glVertex3d( aEndPoint.x, aEndPoint.y, layerDepth ); + glNormal3d( 1.0, 0, 0); + glTexCoord2f(0.0, 1.0); + glVertex3d( diagonalPointB.x, diagonalPointB.y, layerDepth ); + glEnd(); + } + if(isStrokeEnabled) + { + glBegin(GL_QUADS); + + drawRoundedSegment(aStartPoint, diagonalPointA, lineWidth, true, false ); + drawRoundedSegment(aEndPoint, diagonalPointA, lineWidth, true, false ); + drawRoundedSegment(aStartPoint, diagonalPointB, lineWidth, true, false ); + drawRoundedSegment(aEndPoint, diagonalPointB, lineWidth, true, false ); + + glEnd(); + } + + return; + } + + selectShader( -1 ); + + glPushMatrix(); + + glTranslated( 0, 0, layerDepth ); + + // Stroke the outline + if( isStrokeEnabled ) + { + glColor4d( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + std::deque pointList; + pointList.push_back( aStartPoint ); + pointList.push_back( diagonalPointA ); + pointList.push_back( aEndPoint ); + pointList.push_back( diagonalPointB ); + pointList.push_back( aStartPoint ); + DrawPolyline( pointList ); + } + + // Fill the rectangle + if( isFillEnabled ) + { + glColor4d( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + glBegin( GL_QUADS ); + glVertex2d( aStartPoint.x, aStartPoint.y ); + glVertex2d( diagonalPointA.x, diagonalPointA.y ); + glVertex2d( aEndPoint.x, aEndPoint.y ); + glVertex2d( diagonalPointB.x, diagonalPointB.y ); + glEnd(); + } + + glPopMatrix(); + + // Restore the stroke color + glColor4d( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); +} + + +void OPENGL_GAL::DrawCircle( VECTOR2D aCenterPoint, double aRadius ) +{ + // We need a minimum radius, else simply don't draw the circle + if( aRadius <= 0.0 ) + { + return; + } + + if( isUseShader ) + { + drawRoundedSegment( aCenterPoint, aCenterPoint, aRadius * 2.0, false, true ); + return; + } + + switch( m_drawMode ) + { + // Draw the middle of the circle (not anti-aliased) + case DRAW_MODE_NORMAL: + { + // Compute the factors for the unit circle + double outerScale = lineWidth / aRadius / 2; + double innerScale = -outerScale; + outerScale += 1.0; + innerScale += 1.0; + + if( isUseShader ) + { + innerScale *= 1.0 / cos( M_PI / CIRCLE_POINTS ); + } + + if( isStrokeEnabled ) + { + if( innerScale < outerScale ) + { + // Draw the outline + glColor4d( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + + glPushMatrix(); + + glTranslated( aCenterPoint.x, aCenterPoint.y, 0.0 ); + glScaled( aRadius, aRadius, 1.0 ); + + glBegin( GL_QUAD_STRIP ); + for( std::deque::const_iterator it = unitCirclePoints.begin(); + it != unitCirclePoints.end(); it++ ) + { + glVertex3d( it->x * innerScale, it->y * innerScale, layerDepth ); + glVertex3d( it->x * outerScale, it->y * outerScale, layerDepth ); + } + glEnd(); + + glPopMatrix(); + } + } + + // Filled circles are easy to draw by using the stored display list, scaling and translating + if( isFillEnabled ) + { + glColor4d( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + + glPushMatrix(); + + glTranslated( aCenterPoint.x, aCenterPoint.y, layerDepth ); + glScaled( aRadius, aRadius, 1.0 ); + + glBegin( GL_TRIANGLE_FAN ); + glVertex3d( 0, 0, 0 ); + glCallList( displayListCircle ); + glEnd(); + + glPopMatrix(); + } + + glColor4d( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + } + break; + + // Prepare / draw anti-aliased edges + case DRAW_MODE_PREPARE_EDGES: + case DRAW_MODE_DRAW_EDGES: + if( isUseShader ) + { + // Set the color + // Now we enable the shader program for the circle + // the shader requires the screen size as uniform argument + shaderList[0].Use(); + shaderList[0].SetParameter( 0, screenSize.x / 2 ); + shaderList[0].SetParameter( 1, screenSize.y / 2 ); + glBegin( GL_LINES ); + if( isStrokeEnabled ) + { + glColor4d( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + glVertex3d( aCenterPoint.x, aCenterPoint.y, layerDepth ); + glVertex3d( aRadius - lineWidth / 2, aRadius + lineWidth / 2, 0 ); + } + else + { + glColor4d( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + glVertex3d( aCenterPoint.x, aCenterPoint.y, layerDepth ); + glVertex3d( 0, aRadius, 0 ); + } + glEnd(); + shaderList[0].Deactivate(); + } + break; + default: + break; + } +} + + +// This method is used for round line caps +void OPENGL_GAL::drawSemiCircle( VECTOR2D aCenterPoint, double aRadius, double aAngle, + double aDepthOffset ) +{ + // XXX Depth seems to be buggy + glPushMatrix(); + glTranslated( aCenterPoint.x, aCenterPoint.y, aDepthOffset ); + glScaled( aRadius, aRadius, 1.0 ); + glRotated( aAngle * 360.0 / ( 2 * M_PI ), 0, 0, 1 ); + + glBegin( GL_TRIANGLE_FAN ); + glCallList( displayListSemiCircle ); + glEnd(); + + glPopMatrix(); +} + + +// FIXME Optimize +void OPENGL_GAL::DrawArc( VECTOR2D aCenterPoint, double aRadius, double aStartAngle, + double aEndAngle ) +{ + if( aRadius <= 0 ) + { + return; + } + + double outerScale = lineWidth / aRadius / 2; + double innerScale = -outerScale; + + outerScale += 1.0; + innerScale += 1.0; + + // Swap the angles, if start angle is greater than end angle + SWAP( aStartAngle, >, aEndAngle ); + + VECTOR2D startPoint( cos( aStartAngle ), sin( aStartAngle ) ); + VECTOR2D endPoint( cos( aEndAngle ), sin( aEndAngle ) ); + VECTOR2D startEndPoint = startPoint + endPoint; + VECTOR2D middlePoint = 0.5 * startEndPoint; + + + glPushMatrix(); + glTranslated( aCenterPoint.x, aCenterPoint.y, layerDepth ); + glScaled( aRadius, aRadius, 1.0 ); + + if( isStrokeEnabled ) + { + if( isUseShader ) + { + int n_points_s = (int) ( aRadius * worldScale ); + int n_points_a = (int) ( ( aEndAngle - aStartAngle ) / (double) ( 2.0 * M_PI / CIRCLE_POINTS )); + + if( n_points_s < 4 ) + n_points_s = 4; + + int n_points = std::min( n_points_s, n_points_a ); + + if( n_points > CIRCLE_POINTS ) + n_points = CIRCLE_POINTS; + + double alphaIncrement = ( aEndAngle - aStartAngle ) / n_points; + + double cosI = cos( alphaIncrement ); + double sinI = sin( alphaIncrement ); + + VECTOR2D p( cos( aStartAngle ), sin( aStartAngle ) ); + + glBegin( GL_QUADS ); + for( int i = 0; i < n_points; i++ ) + { + VECTOR2D p_next( p.x * cosI - p.y * sinI, p.x * sinI + p.y * cosI ); + + drawRoundedSegment( p, p_next, lineWidth / aRadius, true, false ); + p = p_next; + } + glEnd(); + } + else + { + glColor4d( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); + + glBegin( GL_QUAD_STRIP ); + + double alphaIncrement = 2 * M_PI / CIRCLE_POINTS; + + for( double alpha = aStartAngle; alpha < aEndAngle; alpha += alphaIncrement ) + { + glVertex2d( cos( alpha ) * innerScale, sin( alpha ) * innerScale ); + glVertex2d( cos( alpha ) * outerScale, sin( alpha ) * outerScale ); + } + + glVertex2d( cos( aEndAngle ) * innerScale, sin( aEndAngle ) * innerScale ); + glVertex2d( cos( aEndAngle ) * outerScale, sin( aEndAngle ) * outerScale ); + + glEnd(); + + if( lineCap == LINE_CAP_ROUND ) + { + drawSemiCircle( startPoint, lineWidth / aRadius / 2, aStartAngle + M_PI, 0 ); + drawSemiCircle( endPoint, lineWidth / aRadius / 2, aEndAngle, 0 ); + } + } + } + + if( isFillEnabled ) + { + glColor4d( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + + glBegin( GL_TRIANGLE_FAN ); + + glVertex2d( middlePoint.x, middlePoint.y ); + + double alphaIncrement = 2 * M_PI / CIRCLE_POINTS; + + for( double alpha = aStartAngle; alpha < aEndAngle; alpha += alphaIncrement ) + { + glVertex2d( cos( alpha ), sin( alpha ) ); + } + + glVertex2d( endPoint.x, endPoint.y ); + + glEnd(); + } + + glPopMatrix(); +} + + +struct OGLPOINT +{ + OGLPOINT() : + x( 0.0 ), y( 0.0 ), z( 0.0 ) + { + } + + OGLPOINT( const char* fastest ) + { + // do nothing for fastest speed, and keep inline + } + + OGLPOINT( const VECTOR2D& aPoint ) : + x( aPoint.x ), y( aPoint.y ), z( 0.0 ) + { + } + + OGLPOINT& operator=( const VECTOR2D& aPoint ) + { + x = aPoint.x; + y = aPoint.y; + z = 0.0; + return *this; + } + + GLdouble x; + GLdouble y; + GLdouble z; +}; + + +void OPENGL_GAL::DrawPolygon( const std::deque& aPointList ) +{ + // Any non convex polygon needs to be tesselated + // for this purpose the GLU standard functions are used + + GLUtesselator* tesselator = gluNewTess(); + + typedef std::vector OGLPOINTS; + + // Do only one heap allocation, can do because we know size in advance. + // std::vector is then fastest + OGLPOINTS vertexList( aPointList.size(), OGLPOINT( "fastest" ) ); + + InitTesselatorCallbacks( tesselator ); + + gluTessProperty( tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE ); + + glNormal3d( 0.0, 0.0, 1.0 ); + glColor4d( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + + glShadeModel( GL_FLAT ); + gluTessBeginPolygon( tesselator, NULL ); + gluTessBeginContour( tesselator ); + + // use operator=( const POINTS& ) + copy( aPointList.begin(), aPointList.end(), vertexList.begin() ); + + for( OGLPOINTS::iterator it = vertexList.begin(); it != vertexList.end(); it++ ) + { + it->z = layerDepth; + gluTessVertex( tesselator, &it->x, &it->x ); + } + + gluTessEndContour( tesselator ); + gluTessEndPolygon( tesselator ); + + gluDeleteTess( tesselator ); + + // vertexList destroyed here +} + + +void OPENGL_GAL::DrawCurve( VECTOR2D aStartPoint, VECTOR2D aControlPointA, + VECTOR2D aControlPointB, VECTOR2D aEndPoint ) +{ + // FIXME The drawing quality needs to be improved + // FIXME Perhaps choose a quad/triangle strip instead? + // FIXME Brute force method, use a better (recursive?) algorithm + + std::deque pointList; + + double t = 0.0; + double dt = 1.0 / (double)CURVE_POINTS; + + for( int i = 0; i <= CURVE_POINTS; i++ ) + { + double omt = 1.0 - t; + double omt2 = omt * omt; + double omt3 = omt * omt2; + double t2 = t * t; + double t3 = t * t2; + + VECTOR2D vertex = omt3 * aStartPoint + 3.0 * t * omt2 * aControlPointA + + 3.0 * t2 * omt * aControlPointB + t3 * aEndPoint; + + pointList.push_back( vertex ); + + t += dt; + } + + DrawPolyline( pointList ); +} + + +void OPENGL_GAL::SetStrokeColor( COLOR4D aColor ) +{ + if( strokeColor != aColor ) + { + isSetAttributes = true; + strokeColor = aColor; + + // This is the default drawing color + glColor4d( aColor.r, aColor.g, aColor.b, aColor.a ); + } +} + + +void OPENGL_GAL::SetFillColor( COLOR4D aColor ) +{ + if( fillColor != aColor ) + { + isSetAttributes = true; + fillColor = aColor; + } +} + + +void OPENGL_GAL::SetBackgroundColor( COLOR4D aColor ) +{ + if( backgroundColor != aColor ) + { + isSetAttributes = true; + backgroundColor = aColor; + } +} + + +void OPENGL_GAL::SetLineWidth( double aLineWidth ) +{ + if( lineWidth != aLineWidth ) + { + isSetAttributes = true; + lineWidth = aLineWidth; + } +} + + +void OPENGL_GAL::ClearScreen() +{ + // Clear screen + glClearColor( backgroundColor.r, backgroundColor.g, backgroundColor.b, backgroundColor.a ); + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); +} + + +void OPENGL_GAL::Transform( MATRIX3x3D aTransformation ) +{ + GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; + + matrixData[0] = aTransformation.m_data[0][0]; + matrixData[1] = aTransformation.m_data[1][0]; + matrixData[2] = aTransformation.m_data[2][0]; + matrixData[4] = aTransformation.m_data[0][1]; + matrixData[5] = aTransformation.m_data[1][1]; + matrixData[6] = aTransformation.m_data[2][1]; + matrixData[12] = aTransformation.m_data[0][2]; + matrixData[13] = aTransformation.m_data[1][2]; + matrixData[14] = aTransformation.m_data[2][2]; + + glMultMatrixd( matrixData ); +} + + +void OPENGL_GAL::Rotate( double aAngle ) +{ + glRotated( aAngle * ( 360 / ( 2 * M_PI ) ), 0, 0, 1 ); +} + + +void OPENGL_GAL::Translate( VECTOR2D aVector ) +{ + glTranslated( aVector.x, aVector.y, 0 ); +} + + +void OPENGL_GAL::Scale( VECTOR2D aScale ) +{ + // TODO: Check method + glScaled( aScale.x, aScale.y, 0 ); +} + + +void OPENGL_GAL::Flush() +{ + glFlush(); +} + + +void OPENGL_GAL::Save() +{ + glPushMatrix(); +} + + +void OPENGL_GAL::Restore() +{ + glPopMatrix(); +} + + +// TODO Error handling +int OPENGL_GAL::BeginGroup() +{ + isGroupStarted = true; + GLint displayList = glGenLists( 1 ); + glNewList( displayList, GL_COMPILE ); + displayListsGroup.push_back( displayList ); + + return (int) displayList; +} + + +void OPENGL_GAL::EndGroup() +{ + isGroupStarted = false; + glEndList(); + glCallList( displayListsGroup.back() ); +} + + +void OPENGL_GAL::DeleteGroup( int aGroupNumber ) +{ + std::deque::iterator it = displayListsGroup.begin(); + std::advance( it, aGroupNumber ); + + displayListsGroup.erase( it ); + + glDeleteLists( (GLint) aGroupNumber, 1 ); +} + + +void OPENGL_GAL::DrawGroup( int aGroupNumber ) +{ + glCallList( (GLint) aGroupNumber ); +} + + +void OPENGL_GAL::computeUnitArcs() +{ + displayListsArcs = glGenLists( CIRCLE_POINTS + 1 ); + + // Create an individual display list for each arc in with an angle [0 .. 2pi] + for( int j = 0; j < CIRCLE_POINTS + 1; j++ ) + { + glNewList( displayListsArcs + j, GL_COMPILE ); + + for( int i = 0; i < j; i++ ) + { + glVertex2d( cos( 2 * M_PI / CIRCLE_POINTS * i ), sin( 2 * M_PI / CIRCLE_POINTS * i ) ); + } + + glEndList(); + } +} + + +void OPENGL_GAL::computeUnitCircle() +{ + displayListCircle = glGenLists( 1 ); + glNewList( displayListCircle, GL_COMPILE ); + + // Compute the circle points for a given number of segments + // Insert in a display list and a vector + for( int i = 0; i < CIRCLE_POINTS + 1; i++ ) + { + double valueX = cos( 2 * M_PI / CIRCLE_POINTS * i ); + double valueY = sin( 2 * M_PI / CIRCLE_POINTS * i ); + glVertex3d( valueX, valueY, 0 ); + unitCirclePoints.push_back( VECTOR2D( valueX, valueY ) ); + } + + glEndList(); +} + + +void OPENGL_GAL::computeUnitSemiCircle() +{ + displayListSemiCircle = glGenLists( 1 ); + glNewList( displayListSemiCircle, GL_COMPILE ); + + for( int i = 0; i < CIRCLE_POINTS / 2 + 1; i++ ) + { + glVertex3d( cos( 2 * M_PI / CIRCLE_POINTS * i ), sin( 2 * M_PI / CIRCLE_POINTS * i ), 0 ); + } + + glEndList(); +} + + +void OPENGL_GAL::ComputeWorldScreenMatrix() +{ + ComputeWorldScale(); + + worldScreenMatrix.SetIdentity(); + + MATRIX3x3D translation; + translation.SetIdentity(); + translation.SetTranslation( 0.5 * screenSize ); + + MATRIX3x3D scale; + scale.SetIdentity(); + scale.SetScale( VECTOR2D( worldScale, worldScale ) ); + + MATRIX3x3D flip; + flip.SetIdentity(); + flip.SetScale( VECTOR2D( 1.0, 1.0 ) ); + + MATRIX3x3D lookat; + lookat.SetIdentity(); + lookat.SetTranslation( -lookAtPoint ); + + worldScreenMatrix = translation * flip * scale * lookat * worldScreenMatrix; +} + + +// ------------------------------------- +// Callback functions for the tesselator +// ------------------------------------- + +// Compare Redbook Chapter 11 + + +void CALLBACK VertexCallback( GLvoid* aVertexPtr ) +{ + GLdouble* vertex = (GLdouble*) aVertexPtr; + + glVertex3dv( vertex ); +} + + +void CALLBACK BeginCallback( GLenum aWhich ) +{ + glBegin( aWhich ); +} + + +void CALLBACK EndCallback() +{ + glEnd(); +} + + +void CALLBACK ErrorCallback( GLenum aErrorCode ) +{ + const GLubyte* estring; + + estring = gluErrorString( aErrorCode ); + wxLogError( wxT( "Tessellation Error: %s" ), (char*) estring ); +} + + +void InitTesselatorCallbacks( GLUtesselator* aTesselator ) +{ + gluTessCallback( aTesselator, GLU_TESS_VERTEX, ( void (CALLBACK*)() )VertexCallback ); + gluTessCallback( aTesselator, GLU_TESS_BEGIN, ( void (CALLBACK*)() )BeginCallback ); + gluTessCallback( aTesselator, GLU_TESS_END, ( void (CALLBACK*)() )EndCallback ); + gluTessCallback( aTesselator, GLU_TESS_ERROR, ( void (CALLBACK*)() )ErrorCallback ); +} + + +// --------------- +// Cursor handling +// --------------- + + +void OPENGL_GAL::initCursor( int aCursorSize ) +{ + cursorSize = aCursorSize; +} + + +VECTOR2D OPENGL_GAL::ComputeCursorToWorld( VECTOR2D aCursorPosition ) +{ + aCursorPosition.y = screenSize.y - aCursorPosition.y; + MATRIX3x3D inverseMatrix = worldScreenMatrix.Inverse(); + VECTOR2D cursorPositionWorld = inverseMatrix * aCursorPosition; + + return cursorPositionWorld; +} + + +void OPENGL_GAL::DrawCursor( VECTOR2D aCursorPosition ) +{ + SetCurrent( *glContext ); + + // Draw the cursor on the surface + VECTOR2D cursorPositionWorld = ComputeCursorToWorld( aCursorPosition ); + + cursorPositionWorld.x = round( cursorPositionWorld.x / gridSize.x ) * gridSize.x; + cursorPositionWorld.y = round( cursorPositionWorld.y / gridSize.y ) * gridSize.y; + + aCursorPosition = worldScreenMatrix * cursorPositionWorld; + + // Switch to the main frame buffer and blit the scene + glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + glLoadIdentity(); + + blitMainTexture( false ); + + glDisable( GL_TEXTURE_2D ); + glColor4d( cursorColor.r, cursorColor.g, cursorColor.b, cursorColor.a ); + + glBegin( GL_QUADS ); + + glVertex3f( (int) ( aCursorPosition.x - cursorSize / 2 ) + 1, + (int) ( aCursorPosition.y ), depthRange.x ); + + glVertex3f( (int) ( aCursorPosition.x + cursorSize / 2 ) + 1, + (int) ( aCursorPosition.y ), depthRange.x ); + + glVertex3f( (int) ( aCursorPosition.x + cursorSize / 2 ) + 1, + (int) ( aCursorPosition.y + 1 ), depthRange.x ); + + glVertex3f( (int) ( aCursorPosition.x - cursorSize / 2 ) + 1, + (int) ( aCursorPosition.y + 1), depthRange.x ); + + glVertex3f( (int) ( aCursorPosition.x ), + (int) ( aCursorPosition.y - cursorSize / 2 ) + 1, depthRange.x ); + + glVertex3f( (int) ( aCursorPosition.x ), + (int) ( aCursorPosition.y + cursorSize / 2 ) + 1, depthRange.x ); + + glVertex3f( (int) ( aCursorPosition.x ) + 1, + (int) ( aCursorPosition.y + cursorSize / 2 ) + 1, depthRange.x ); + + glVertex3f( (int) ( aCursorPosition.x ) + 1, + (int) ( aCursorPosition.y - cursorSize / 2 ) + 1, depthRange.x ); + glEnd(); + + // Blit the current screen contents + SwapBuffers(); +} + + +void OPENGL_GAL::DrawGridLine( VECTOR2D aStartPoint, VECTOR2D aEndPoint ) +{ + // We check, if we got a horizontal or a vertical grid line and compute the offset + VECTOR2D perpendicularVector; + + if( aStartPoint.x == aEndPoint.x ) + { + perpendicularVector = VECTOR2D( 0.5 * lineWidth, 0 ); + } + else + { + perpendicularVector = VECTOR2D( 0, 0.5 * lineWidth ); + } + + // Now we compute the edge points of the quad + VECTOR2D point1 = aStartPoint + perpendicularVector; + VECTOR2D point2 = aStartPoint - perpendicularVector; + VECTOR2D point3 = aEndPoint + perpendicularVector; + VECTOR2D point4 = aEndPoint - perpendicularVector; + + if( isUseShader ) + selectShader( -1 ); + + // Set color + glColor4d( gridColor.r, gridColor.g, gridColor.b, gridColor.a ); + + // Draw the quad for the grid line + glBegin( GL_QUADS ); + double gridDepth = depthRange.y * 0.75; + glVertex3d( point1.x, point1.y, gridDepth ); + glVertex3d( point2.x, point2.y, gridDepth ); + glVertex3d( point4.x, point4.y, gridDepth ); + glVertex3d( point3.x, point3.y, gridDepth ); + glEnd(); +} + + +bool OPENGL_GAL::Show( bool aShow ) +{ + bool s = wxGLCanvas::Show( aShow ); + + if( aShow ) + wxGLCanvas::Raise(); + + return s; +} diff --git a/common/gal/opengl/shader.cpp b/common/gal/opengl/shader.cpp new file mode 100644 index 0000000000..e38fbe1995 --- /dev/null +++ b/common/gal/opengl/shader.cpp @@ -0,0 +1,217 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Graphics Abstraction Layer (GAL) for OpenGL + * + * Shader class + * + * 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 + */ + +#include +#include + +#include + +#include + +using namespace KiGfx; + +SHADER::SHADER() +{ + isProgramCreated = false; + isShaderLinked = false; + maximumVertices = 4; + geomInputType = GL_LINES; + geomOutputType = GL_LINES; +} + + +SHADER::~SHADER() +{ + if( isProgramCreated ) + { + // Delete the shaders and the program + for( std::deque::iterator it = shaderNumbers.begin(); it != shaderNumbers.end(); + it++ ) + { + glDeleteShader( *it ); + } + + glDeleteProgram( programNumber ); + } +} + + +void SHADER::ProgramInfo( GLuint aProgram ) +{ + GLint glInfoLogLength = 0; + GLint writtenChars = 0; + + // Get the length of the info string + glGetProgramiv( aProgram, GL_INFO_LOG_LENGTH, &glInfoLogLength ); + + // Print the information + if( glInfoLogLength > 2 ) + { + GLchar* glInfoLog = new GLchar[glInfoLogLength]; + glGetProgramInfoLog( aProgram, glInfoLogLength, &writtenChars, glInfoLog ); + + wxLogInfo( wxString::FromUTF8( (char*) glInfoLog ) ); + + delete glInfoLog; + } +} + + +std::string SHADER::ReadSource( std::string aShaderSourceName ) +{ + // Open the shader source for reading + std::ifstream inputFile( aShaderSourceName.c_str(), std::ifstream::in ); + std::string shaderSource; + + if( !inputFile ) + { + wxLogError( wxString::FromUTF8( "Can't read the shader source: " ) + + wxString( aShaderSourceName.c_str(), wxConvUTF8 ) ); + exit( 1 ); + } + + std::string shaderSourceLine; + + // Read all lines from the text file + while( getline( inputFile, shaderSourceLine ) ) + { + shaderSource += shaderSourceLine; + shaderSource += "\n"; + } + + return shaderSource; +} + + +void SHADER::AddSource( std::string aShaderSourceName, ShaderType aShaderType ) +{ + if( isShaderLinked ) + { + wxLogError( wxString::FromUTF8( "Shader is already linked!" ) ); + } + + // Create the program + if( !isProgramCreated ) + { + programNumber = glCreateProgram(); + isProgramCreated = true; + } + + // Load shader sources + std::string shaderSource = ReadSource( aShaderSourceName ); + + // Create a shader + GLuint shaderNumber = glCreateShader( aShaderType ); + shaderNumbers.push_back( shaderNumber ); + + // Get the program info + ProgramInfo( programNumber ); + + // Copy to char array + char* source = new char[shaderSource.size() + 1]; + strcpy( source, shaderSource.c_str() ); + const char** source_ = (const char**) ( &source ); + + // Attach the source + glShaderSource( shaderNumber, 1, source_, NULL ); + ProgramInfo( programNumber ); + + // Compile and attach shader to the program + glCompileShader( shaderNumber ); + glAttachShader( programNumber, shaderNumber ); + ProgramInfo( programNumber ); + + // Special handling for the geometry shader + if( aShaderType == SHADER_TYPE_GEOMETRY ) + { + glProgramParameteriEXT( programNumber, GL_GEOMETRY_VERTICES_OUT_EXT, maximumVertices ); + glProgramParameteriEXT( programNumber, GL_GEOMETRY_INPUT_TYPE_EXT, geomInputType ); + glProgramParameteriEXT( programNumber, GL_GEOMETRY_OUTPUT_TYPE_EXT, geomOutputType ); + } + + // Delete the allocated char array + delete[] source; +} + + +void SHADER::ConfigureGeometryShader( GLuint maxVertices, GLuint geometryInputType, + GLuint geometryOutputType ) +{ + maximumVertices = maxVertices; + geomInputType = geometryInputType; + geomOutputType = geometryOutputType; +} + + +void SHADER::Link() +{ + // Shader linking + glLinkProgram( programNumber ); + ProgramInfo( programNumber ); + + // Check the Link state + GLint linkStatus = 0; + glGetObjectParameterivARB( programNumber, GL_OBJECT_LINK_STATUS_ARB, &linkStatus ); + + if( !linkStatus ) + { + wxLogError( wxString::FromUTF8( "Can't link the shaders!" ) ); + exit( 1 ); + } + + isShaderLinked = true; +} + + +void SHADER::Use() +{ + glUseProgram( programNumber ); +} + + +void SHADER::Deactivate() +{ + glUseProgram( 0 ); +} + + +void SHADER::AddParameter( std::string aParameterName ) +{ + GLint location = glGetUniformLocation( programNumber, aParameterName.c_str() ); + + if( location != -1 ) + { + parameterLocation.push_back( location ); + } +} + + +void SHADER::SetParameter( int parameterNumber, float value ) +{ + glUniform1f( parameterLocation[parameterNumber], value ); +} diff --git a/common/gal/opengl/shader/circle.frag b/common/gal/opengl/shader/circle.frag new file mode 100644 index 0000000000..142952c878 --- /dev/null +++ b/common/gal/opengl/shader/circle.frag @@ -0,0 +1,60 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Fragment shader + * + * 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 + */ + +// This shader requires GLSL 1.2 +#version 120 + +// Input variables +flat varying vec4 center_; +flat varying vec2 radius_; +flat varying vec4 colorA_; +flat varying vec4 colorB_; + +void main( void ) +{ + // Compute the distance from the circle edge + float distA = distance( center_, gl_FragCoord ) - radius_.y; + float distB = radius_.x - distance( center_, gl_FragCoord ); + + // Limit the range to [ 0 .. 1 ] + if( distA < 0 ) distA = 0; + if( distA > 1 ) distA = 1; + if( distB < 0 ) distB = 0; + if( distB > 1 ) distB = 1; + + // Points with a larger distance from the edge are set deeper + gl_FragDepth = gl_FragCoord.z + distA * 0.001 + distB * 0.001; + + // Compute the color + vec4 color; + color.r = colorA_.r; + color.g = colorA_.g; + color.b = colorA_.b; + color.a = colorA_.a * ( 1 - distA ) * ( 1 - distB ); + + // Now output the edge fragment color + gl_FragColor = color; +} diff --git a/common/gal/opengl/shader/circle.geom b/common/gal/opengl/shader/circle.geom new file mode 100644 index 0000000000..e908e458b6 --- /dev/null +++ b/common/gal/opengl/shader/circle.geom @@ -0,0 +1,115 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Geometry shader + * + * 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 + */ + +// This shader requires GLSL 1.2 +#version 120 +#extension GL_EXT_geometry_shader4: enable +#extension GL_EXT_gpu_shader4: enable + +uniform float viewPortX2; +uniform float viewPortY2; + +flat varying vec4 center_; +flat varying vec2 radius_; +flat varying vec4 colorA_; + +const float PI = 3.141592654; +const float EPSILON = 0.01; +const float smallestValue = 1.175494351e-38; +const int SEGMENTS = 16; + +const float PIXEL_EXTEND = 1.5; + +void main() +{ + vec4 center = gl_PositionIn[0]; + vec4 radius = gl_PositionIn[1]; + + center_ = gl_ModelViewProjectionMatrix * center; + + // Compute the outer and inner radius in screen coordinates + // This could be further optimized + radius_.x = ( gl_ModelViewProjectionMatrix * vec4(radius.x, 0, 0, 1) ).x; + radius_.x = abs( radius_.x - (gl_ModelViewProjectionMatrix * vec4(0, 0, 0, 1) ).x ) * viewPortX2; + radius_.y = ( gl_ModelViewProjectionMatrix * vec4(radius.y, 0, 0, 1) ).x; + radius_.y = abs( radius_.y - (gl_ModelViewProjectionMatrix * vec4(0, 0, 0, 1) ).x ) * viewPortX2; + + // Compute the center point in screen coordinates + center_.x = center_.x * viewPortX2 + viewPortX2; + center_.y = center_.y * viewPortY2 + viewPortY2; + + // Compute the extend value, first make sure that the outline is inside the triangles and second add + // a margin for one pixel for smooth edges + float extendInner = 1.0; + float extendOuter = 0; + if( radius_.y > smallestValue ) + { + extendOuter += PIXEL_EXTEND / radius_.y; + } + extendOuter += 1.0 / cos( PI / SEGMENTS ); + + colorA_ = gl_FrontColorIn[0]; + + // Create a quad strip for the outer circle edge + for( float alpha = 0, inc = 2 * PI / SEGMENTS, limit = 2 * PI + EPSILON; + alpha < limit; alpha += inc ) + { + gl_Position = gl_ModelViewProjectionMatrix * + vec4( center.x + extendInner * radius.y * cos( alpha ), + center.y + extendInner * radius.y * sin( alpha ), center.zw ); + EmitVertex(); + gl_Position = gl_ModelViewProjectionMatrix * + vec4( center.x + extendOuter * radius.y * cos( alpha ), + center.y + extendOuter * radius.y * sin( alpha ), center.zw ); + EmitVertex(); + } + EndPrimitive(); + + if( radius.x > 0 ) + { + extendInner = cos( PI / SEGMENTS ) - PIXEL_EXTEND / radius_.x; + if( extendInner < 0.0 ) + { + extendInner = 0; + } + extendOuter = 1.0 / cos( PI / SEGMENTS); + + // Create a quad strip for the inner circle edge + for( float alpha = 0, inc = 2 * PI / SEGMENTS, limit = 2 * PI + EPSILON; + alpha < limit; alpha += inc ) + { + gl_Position = gl_ModelViewProjectionMatrix * + vec4( center.x + extendOuter * radius.x * cos( alpha ), + center.y + extendOuter * radius.x * sin( alpha ), center.zw ); + EmitVertex(); + gl_Position = gl_ModelViewProjectionMatrix * + vec4( center.x + extendInner * radius.x * cos( alpha ), + center.y + extendInner * radius.x * sin( alpha ), center.zw ); + EmitVertex(); + } + EndPrimitive(); + } +} diff --git a/common/gal/opengl/shader/circle.vert b/common/gal/opengl/shader/circle.vert new file mode 100644 index 0000000000..2e16c5d08f --- /dev/null +++ b/common/gal/opengl/shader/circle.vert @@ -0,0 +1,35 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Vertex shader + * + * 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 + */ + +// This shader requires GLSL 1.2 +#version 120 + +void main() +{ + // Simple pass-through + gl_Position = gl_Vertex; + gl_FrontColor = gl_Color; +} diff --git a/common/gal/opengl/shader/line.frag b/common/gal/opengl/shader/line.frag new file mode 100644 index 0000000000..63d0f0bf2c --- /dev/null +++ b/common/gal/opengl/shader/line.frag @@ -0,0 +1,37 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Fragment shader + * + * 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 + */ + +#version 120 +#extension GL_EXT_gpu_shader4: enable + +varying float dist; + +void main() +{ + float d = dist; + gl_FragDepth = gl_FragCoord.z + d * 0.001; + gl_FragColor = vec4( gl_Color.rgb, gl_Color.a * ( 1 - d ) ); +} diff --git a/common/gal/opengl/shader/line.geom b/common/gal/opengl/shader/line.geom new file mode 100644 index 0000000000..8000a791cd --- /dev/null +++ b/common/gal/opengl/shader/line.geom @@ -0,0 +1,123 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Geometry shader + * + * 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 + */ + +#version 120 +#extension GL_EXT_geometry_shader4: enable +#extension GL_EXT_gpu_shader4: enable + +uniform float viewPortX2; +uniform float viewPortY2; +varying float dist; + +void main() +{ + // Compute the transformed start and end points + vec2 startPoint = gl_PositionIn[0].xy; + vec2 endPoint = gl_PositionIn[1].xy; + float lineWidth = gl_PositionIn[1].z; + + // Compute vector start -> end + vec2 startEndVector = endPoint.xy - startPoint.xy; + float lineLength = distance( startPoint, endPoint ); + float scale = 0.0; + + if( lineLength > 0.0 ) + { + scale = 0.5 * lineWidth / lineLength; + } + else + { + scale = 0.0; + } + + // Compute the edge points of the line + vec2 perpendicularVector = scale * vec2( -startEndVector.y, startEndVector.x ); + vec2 point1 = startPoint + perpendicularVector; + vec2 point2 = startPoint - perpendicularVector; + vec2 point3 = endPoint + perpendicularVector; + vec2 point4 = endPoint - perpendicularVector; + + vec4 point1T = gl_ModelViewProjectionMatrix * vec4( point1, gl_PositionIn[0].zw ); + vec4 point2T = gl_ModelViewProjectionMatrix * vec4( point2, gl_PositionIn[0].zw ); + vec4 point3T = gl_ModelViewProjectionMatrix * vec4( point3, gl_PositionIn[0].zw ); + vec4 point4T = gl_ModelViewProjectionMatrix * vec4( point4, gl_PositionIn[0].zw ); + + // Construct the quad for the middle + gl_FrontColor = gl_FrontColorIn[0]; + dist = 0; + gl_Position = point1T; + EmitVertex(); + dist = 0; + gl_Position = point2T; + EmitVertex(); + dist = 0; + gl_Position = point3T; + EmitVertex(); + dist = 0; + gl_Position = point4T; + EmitVertex(); + + EndPrimitive(); + + // Compute the perpendicular vector with 1 pixel width + vec2 v = point1T.xy - point3T.xy; + vec4 onePix = 0.5 * vec4( -v.y, v.x, 0, 0 ); + onePix *= 1.0 / sqrt( dot( onePix, onePix ) ); + onePix.x *= 1.0 / viewPortX2; + onePix.y *= 1.0 / viewPortY2; + + gl_FrontColor = gl_FrontColorIn[0]; + + dist = 1; + gl_Position = point1T + onePix; + EmitVertex(); + dist = 1; + gl_Position = point3T + onePix; + EmitVertex(); + dist = 0; + gl_Position = point1T; + EmitVertex(); + dist = 0; + gl_Position = point3T; + EmitVertex(); + + EndPrimitive(); + + dist = 1; + gl_Position = point2T - onePix; + EmitVertex(); + dist = 1; + gl_Position = point4T - onePix; + EmitVertex(); + dist = 0; + gl_Position = point2T; + EmitVertex(); + dist = 0; + gl_Position = point4T; + EmitVertex(); + + EndPrimitive(); +} diff --git a/common/gal/opengl/shader/line.vert b/common/gal/opengl/shader/line.vert new file mode 100644 index 0000000000..2e16c5d08f --- /dev/null +++ b/common/gal/opengl/shader/line.vert @@ -0,0 +1,35 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Vertex shader + * + * 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 + */ + +// This shader requires GLSL 1.2 +#version 120 + +void main() +{ + // Simple pass-through + gl_Position = gl_Vertex; + gl_FrontColor = gl_Color; +} diff --git a/common/gal/opengl/shader/round.frag b/common/gal/opengl/shader/round.frag new file mode 100644 index 0000000000..3d615db8c0 --- /dev/null +++ b/common/gal/opengl/shader/round.frag @@ -0,0 +1,41 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2013 Tomasz Wlostowski + * + * Fragment shader + * + * 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 + */ + +#version 120 + +varying float aspect; + +void main() +{ + vec2 v = abs( gl_TexCoord[0].xy - vec2( 0.5, 0.5 ) ) * 2.0 - vec2( aspect, 0.0 ); + vec2 d = vec2( v.x / ( 1.0 - aspect ), v.y ); + + if( v.x <= 0.0 || (dot( d, d ) < 1.0 ) ) + gl_FragColor = gl_Color; + else + discard; + + // gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); +} diff --git a/common/gal/opengl/shader/round.vert b/common/gal/opengl/shader/round.vert new file mode 100644 index 0000000000..21410a6063 --- /dev/null +++ b/common/gal/opengl/shader/round.vert @@ -0,0 +1,37 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2013 Tomasz Wlostowski + * + * Vertex shader + * + * 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 + */ + +#version 120 + +varying float aspect; + +void main() +{ + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_FrontColor = gl_Color; + gl_TexCoord[0] = gl_MultiTexCoord0; + + aspect = gl_Normal.x; +} diff --git a/common/gal/stroke_font.cpp b/common/gal/stroke_font.cpp new file mode 100644 index 0000000000..a82021dc30 --- /dev/null +++ b/common/gal/stroke_font.cpp @@ -0,0 +1,276 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Stroke font class + * + * 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 + */ + +#include +#include + +using namespace KiGfx; + +STROKE_FONT::STROKE_FONT( GAL* aGal ) : + m_gal( aGal ), + m_bold( false ), + m_italic( false ), + m_mirrored( false ) +{ + // Default values + m_scaleFactor = 1.0 / 21.0; + m_glyphSize = VECTOR2D( 10.0, 10.0 ); + m_verticalJustify = GR_TEXT_VJUSTIFY_BOTTOM; + m_horizontalJustify = GR_TEXT_HJUSTIFY_LEFT; +} + + +STROKE_FONT::~STROKE_FONT() +{ +} + + +bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ) +{ + m_glyphs.clear(); + m_glyphBoundingBoxes.clear(); + + for( int j = 0; j < aNewStrokeFontSize; j++ ) + { + Glyph glyph; + double glyphStartX; + double glyphEndX; + VECTOR2D glyphBoundingX; + + std::deque pointList; + + int i = 0; + + while( aNewStrokeFont[j][i] ) + { + VECTOR2D point; + char coordinate[2]; + + for( int k = 0; k < 2; k++ ) + { + coordinate[k] = aNewStrokeFont[j][i + k]; + } + + if( i < 2 ) + { + // The first two values contain the width of the char + glyphStartX = coordinate[0] - 'R'; + glyphEndX = coordinate[1] - 'R'; + glyphBoundingX = VECTOR2D( 0, glyphEndX - glyphStartX ); + } + else if( ( coordinate[0] == ' ' ) && ( coordinate[1] == 'R' ) ) + { + // Raise pen + if( pointList.size() > 0 ) + glyph.push_back( pointList ); + + pointList.clear(); + } + else + { + // Every coordinate description of the Hershey format has an offset, + // it has to be subtracted + point.x = (double) ( coordinate[0] - 'R' ) - glyphStartX; + point.y = (double) ( coordinate[1] - 'R' ) - 11.0; + pointList.push_back( point ); + } + + i += 2; + } + + if( pointList.size() > 0 ) + glyph.push_back( pointList ); + + m_glyphs.push_back( glyph ); + + // Compute the bounding box of the glyph + m_glyphBoundingBoxes.push_back( computeBoundingBox( glyph, glyphBoundingX ) ); + } + + return true; +} + + +void STROKE_FONT::LoadAttributes( const EDA_TEXT* aText ) +{ + SetGlyphSize( VECTOR2D( aText->GetSize() ) ); + SetHorizontalJustify( aText->GetHorizJustify() ); + SetVerticalJustify( aText->GetVertJustify() ); + SetBold( aText->IsBold() ); + SetItalic( aText->IsItalic() ); + SetMirrored( aText->IsMirrored() ); +} + + +BOX2D STROKE_FONT::computeBoundingBox( Glyph aGlyph, VECTOR2D aGlyphBoundingX ) +{ + BOX2D boundingBox; + + std::deque boundingPoints; + + boundingPoints.push_back( VECTOR2D( aGlyphBoundingX.x, 0 ) ); + boundingPoints.push_back( VECTOR2D( aGlyphBoundingX.y, 0 ) ); + + for( Glyph::iterator pointListIt = aGlyph.begin(); pointListIt != aGlyph.end(); ++pointListIt ) + { + for( std::deque::iterator pointIt = pointListIt->begin(); + pointIt != pointListIt->end(); ++pointIt ) + { + boundingPoints.push_back( VECTOR2D( aGlyphBoundingX.x, pointIt->y ) ); + } + } + + boundingBox.Compute( boundingPoints ); + + return boundingBox; +} + + +void STROKE_FONT::Draw( std::string aText, VECTOR2D aPosition, double aRotationAngle ) +{ + // Compute the text size + VECTOR2D textsize = computeTextSize( aText ); + + // Context needs to be saved before any transformations + m_gal->Save(); + + m_gal->Translate( aPosition ); + m_gal->Rotate( -aRotationAngle ); + + // Adjust the text position to the given alignment + switch( m_horizontalJustify ) + { + case GR_TEXT_HJUSTIFY_CENTER: + m_gal->Translate( VECTOR2D( -textsize.x / 2, 0 ) ); + break; + + case GR_TEXT_HJUSTIFY_RIGHT: + m_gal->Translate( VECTOR2D( -textsize.x, 0 ) ); + break; + + case GR_TEXT_HJUSTIFY_LEFT: + break; + + default: + break; + } + + switch( m_verticalJustify ) + { + case GR_TEXT_VJUSTIFY_CENTER: + m_gal->Translate( VECTOR2D( 0, textsize.y / 2 ) ); + break; + + case GR_TEXT_VJUSTIFY_TOP: + m_gal->Translate( VECTOR2D( 0, textsize.y ) ); + break; + + case GR_TEXT_VJUSTIFY_BOTTOM: + break; + + default: + break; + } + + double xOffset, glyphSizeX; + + if( m_mirrored ) + { + // In case of mirrored text invert the X scale of points and their X direction + // (m_glyphSize.x) and start drawing from the position where text normally should end + // (textsize.x) + xOffset = textsize.x; + glyphSizeX = -m_glyphSize.x; + } + else + { + xOffset = 0.0; + glyphSizeX = m_glyphSize.x; + } + double scaleY = m_scaleFactor * m_glyphSize.y; + double scaleX = m_scaleFactor * glyphSizeX; + + if( m_bold ) + { + m_gal->SetLineWidth( m_gal->GetLineWidth() * 1.3 ); + } + + for( std::string::iterator chIt = aText.begin(); chIt != aText.end(); chIt++ ) + { + GlyphList::iterator glyphIt = m_glyphs.begin(); + std::deque::iterator bbIt = m_glyphBoundingBoxes.begin(); + + advance( glyphIt, (int) ( *chIt ) - (int) ' ' ); + advance( bbIt, (int) ( *chIt ) - (int) ' ' ); + + Glyph glyph = *glyphIt; + + for( Glyph::iterator pointListIt = glyph.begin(); pointListIt != glyph.end(); + pointListIt++ ) + { + std::deque pointListScaled; + + for( std::deque::iterator pointIt = pointListIt->begin(); + pointIt != pointListIt->end(); pointIt++ ) + { + VECTOR2D pointPos( pointIt->x * scaleX + xOffset, pointIt->y * scaleY ); + + if( m_italic ) + { + // FIXME should be done other way - referring to the lowest Y value of point + // because now italic fonts are translated a bit + pointPos.x += pointPos.y * 0.1; + } + + pointListScaled.push_back( pointPos ); + } + + m_gal->DrawPolyline( pointListScaled ); + } + + xOffset += m_scaleFactor * glyphSizeX * + ( bbIt->GetEnd().x - bbIt->GetOrigin().x ); + } + + m_gal->Restore(); +} + + +VECTOR2D STROKE_FONT::computeTextSize( std::string aText ) +{ + VECTOR2D result = VECTOR2D( 0.0, 0.0 ); + + for( std::string::iterator chIt = aText.begin(); chIt != aText.end(); chIt++ ) + { + std::deque::iterator bbIt = m_glyphBoundingBoxes.begin(); + advance( bbIt, (int) ( *chIt ) - (int) ' ' ); + result.x += m_scaleFactor * m_glyphSize.x * ( bbIt->GetEnd().x - bbIt->GetOrigin().x ); + } + + result.y = m_glyphSize.y; + + return result; +} diff --git a/common/painter.cpp b/common/painter.cpp new file mode 100644 index 0000000000..46c8a15d20 --- /dev/null +++ b/common/painter.cpp @@ -0,0 +1,82 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * @author Maciej Suminski + * + * 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 + */ + +#include +#include +#include +#include +#include + +using namespace KiGfx; + +RENDER_SETTINGS::RENDER_SETTINGS() +{ + // Set the default initial values + m_selectionBorderColor = COLOR4D( 1.0, 1.0, 1.0, 1.0 ); + m_netLabelColor = COLOR4D( 1.0, 1.0, 1.0, 0.7 ); + + m_highlightFactor = 0.5; + m_selectFactor = 0.5; + m_layerOpacity = 0.8; + m_highlightEnabled = false; + m_hiContrastEnabled = false; + m_hiContrastFactor = 0.2; + + // Store the predefined colors used in KiCad in format used by GAL + for( int i = 0; i < NBCOLOR; i++ ) + { + m_legacyColorMap[ColorRefs[i].m_Numcolor] = COLOR4D( (double) ColorRefs[i].m_Red / 255.0, + (double) ColorRefs[i].m_Green / 255.0, + (double) ColorRefs[i].m_Blue / 255.0, + m_layerOpacity ); + } +} + + +RENDER_SETTINGS::~RENDER_SETTINGS() +{ +} + + +void RENDER_SETTINGS::Update() +{ + m_hiContrastColor = COLOR4D( m_hiContrastFactor, m_hiContrastFactor, m_highlightFactor, + m_layerOpacity ); +} + + +PAINTER::PAINTER( GAL* aGal ) : + m_gal( aGal ), m_settings( NULL ) +{ + m_stroke_font = new STROKE_FONT( aGal ); + m_stroke_font->LoadNewStrokeFont( newstroke_font, newstroke_font_bufsize ); +} + + +PAINTER::~PAINTER() +{ + delete m_stroke_font; +} diff --git a/common/profile.h b/common/profile.h new file mode 100644 index 0000000000..d224152a89 --- /dev/null +++ b/common/profile.h @@ -0,0 +1,159 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * 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 profile.h: + * @brief Simple profiling functions for measuring code execution time. Currently only Linux is + * supported. + */ + +#ifndef __PROFILE_H +#define __PROFILE_H + +#ifdef __linux__ + #include +#endif + +#include + +/** + * Function rdtsc + * Returns processor's time-stamp counter. Main purpose is precise time measuring of code + * execution time. + * @return unsigned long long - Value of time-stamp counter. + */ +#if defined(__i386__) +static __inline__ unsigned long long rdtsc() +{ + unsigned long long int x; + __asm__ volatile ( ".byte 0x0f, 0x31" : "=A" ( x ) ); + + return x; +} + + +#elif defined(__x86_64__) +static __inline__ unsigned long long rdtsc() +{ + unsigned hi, lo; + __asm__ __volatile__ ( "rdtsc" : "=a" ( lo ), "=d" ( hi ) ); + + return ( (unsigned long long) lo ) | ( ( (unsigned long long) hi ) << 32 ); +} + + +#elif defined(__powerpc__) +static __inline__ unsigned long long rdtsc() +{ + unsigned long long int result = 0; + unsigned long int upper, lower, tmp; + __asm__ volatile ( + "0: \n" + "\tmftbu %0 \n" + "\tmftb %1 \n" + "\tmftbu %2 \n" + "\tcmpw %2,%0 \n" + "\tbne 0b \n" + : "=r" ( upper ), "=r" ( lower ), "=r" ( tmp ) + ); + + result = upper; + result = result << 32; + result = result | lower; + + return result; +} + + +#endif /* __powerpc__ */ + +// Fixme: OS X version +/** + * Function get_tics + * Returns the number of milliseconds that have elapsed since the system was started. + * @return uint64_t Number of milliseconds. + */ +static inline uint64_t get_tics() +{ +#if defined(__linux__) + struct timezone tz = { 0, 0 }; + struct timeval tv; + gettimeofday( &tv, &tz ); + + return (uint64_t) tv.tv_sec * 1000000ULL + (uint64_t) tv.tv_usec; +#elif defined _WIN32 || defined _WIN64 + // TODO to be tested + return GetTickCount(); +#else + return 0; +#endif +} + + +/** + * Structure for storing data related to profiling counters. + */ +struct prof_counter +{ + uint64_t value; /// Stored timer value + bool use_rdtsc; /// Method of time measuring (rdtsc or tics) +}; + +/** + * Function prof_start + * Begins code execution time counting for a given profiling counter. + * @param cnt is the counter which should be started. + * @param use_rdtsc tells if processor's time-stamp counter should be used for time counting. + * Otherwise is system tics method will be used. + */ +static inline void prof_start( prof_counter* cnt, bool use_rdtsc ) +{ + cnt->use_rdtsc = use_rdtsc; + + if( use_rdtsc ) + { + cnt->value = rdtsc(); + } + else + { + cnt->value = get_tics(); + } +} + + +/** + * Function prof_stop + * Ends code execution time counting for a given profiling counter. + * @param cnt is the counter which should be stopped. + */ +static inline void prof_end( prof_counter* cnt ) +{ + if( cnt->use_rdtsc ) + cnt->value = rdtsc() - cnt->value; + else + cnt->value = get_tics() - cnt->value; +} + + +#endif diff --git a/common/view/view.cpp b/common/view/view.cpp new file mode 100644 index 0000000000..0e78e23eb7 --- /dev/null +++ b/common/view/view.cpp @@ -0,0 +1,486 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * 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 + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace KiGfx; + +const unsigned int VIEW::VIEW_MAX_LAYERS = 64; + +void VIEW::AddLayer( int aLayer, bool aDisplayOnly ) +{ + if( m_layers.find( aLayer ) == m_layers.end() ) + { + m_layers[aLayer] = VIEW_LAYER(); + m_layers[aLayer].id = aLayer; + m_layers[aLayer].items = new VIEW_RTREE(); + m_layers[aLayer].renderingOrder = aLayer; + m_layers[aLayer].enabled = true; + m_layers[aLayer].isDirty = false; + m_layers[aLayer].displayOnly = aDisplayOnly; + } + + sortLayers(); +} + + +void VIEW::Add( VIEW_ITEM* aItem ) +{ + int layers[VIEW_MAX_LAYERS], layers_count; + + aItem->ViewGetLayers( layers, layers_count ); + + for( int i = 0; i < layers_count; i++ ) + { + VIEW_LAYER* l = &m_layers[layers[i]]; + l->items->Insert( aItem ); + l->dirtyExtents.Merge( aItem->ViewBBox() ); + } + + if( m_dynamic ) + aItem->viewAssign( this ); +} + + +void VIEW::Remove( VIEW_ITEM* aItem ) +{ + if( m_dynamic ) + aItem->m_view = NULL; + +// fixme: this is so sloooow! + for( LayerMapIter i = m_layers.begin(); i != m_layers.end(); ++i ) + { + VIEW_LAYER* l = & ( ( *i ).second ); + l->items->Remove( aItem ); + } +} + + +// stupid C++... python lamda would do this in one line +template +struct queryVisitor +{ + typedef typename Container::value_type item_type; + + queryVisitor( Container& aCont, int aLayer ) : + m_cont( aCont ), m_layer( aLayer ) + { + } + + void operator()( VIEW_ITEM* aItem ) + { + if( aItem->ViewIsVisible() ) + m_cont.push_back( VIEW::LayerItemPair( aItem, m_layer ) ); + } + + Container& m_cont; + int m_layer; +}; + + +int VIEW::Query( const BOX2I& aRect, std::vector& aResult ) +{ + if( m_orderedLayers.empty() ) + return 0; + + std::vector::reverse_iterator i; + + // execute queries in reverse direction, so that items that are on the top of + // the rendering stack are returned first. + for( i = m_orderedLayers.rbegin(); i != m_orderedLayers.rend(); ++i ) + { + // ignore layers that do not contain actual items (i.e. the selection box, menus, floats) + if( ( *i )->displayOnly ) + continue; + + queryVisitor > visitor( aResult, ( *i )->id ); + ( *i )->items->Query( aRect, visitor ); + } + + return aResult.size(); +} + + +VIEW::VIEW( bool aIsDynamic, bool aUseGroups ) : + m_scale ( 1.0 ), + m_painter( NULL ), + m_gal( NULL ), + m_dynamic( aIsDynamic ), + m_useGroups( aUseGroups ) +{ +} + + +VIEW::~VIEW() +{ +} + + +VECTOR2D VIEW::ToWorld( const VECTOR2D& aCoord, bool aAbsolute ) const +{ + MATRIX3x3D matrix = m_gal->GetWorldScreenMatrix().Inverse(); + + if( aAbsolute ) + { + return VECTOR2D( matrix * aCoord ); + } + else + { + return VECTOR2D( matrix.GetScale().x * aCoord.x, matrix.GetScale().y * aCoord.y ); + } +} + + +VECTOR2D VIEW::ToScreen( const VECTOR2D& aCoord, bool aAbsolute ) const +{ + MATRIX3x3D matrix = m_gal->GetWorldScreenMatrix(); + + if( aAbsolute ) + { + return VECTOR2D( matrix * aCoord ); + } + else + { + return VECTOR2D( matrix.GetScale().x * aCoord.x, matrix.GetScale().y * aCoord.y ); + } +} + + +double VIEW::ToScreen( double aCoord, bool aAbsolute ) const +{ + VECTOR2D t( aCoord, 0 ); + + return ToScreen( t, aAbsolute ).x; +} + + +void VIEW::CopySettings( const VIEW* aOtherView ) +{ + // FIXME +} + + +void VIEW::SetGAL( GAL* aGal ) +{ + m_gal = aGal; + + // force the new GAL to display the current viewport. + SetCenter( m_center ); + SetScale( m_scale ); +} + + +void VIEW::SetPainter( PAINTER* aPainter ) +{ + m_painter = aPainter; +} + + +BOX2D VIEW::GetViewport() const +{ + BOX2D rect; + VECTOR2D screenSize = m_gal->GetScreenPixelSize(); + + rect.SetOrigin( ToWorld( VECTOR2D( 0, 0 ) ) ); + rect.SetEnd( ToWorld( screenSize ) ); + + return rect.Normalize(); +} + + +void VIEW::SetViewport( const BOX2D& aViewport, bool aKeepAspect ) +{ + VECTOR2D ssize = ToWorld( m_gal->GetScreenPixelSize(), false ); + VECTOR2D centre = aViewport.Centre(); + VECTOR2D vsize = aViewport.GetSize(); + double zoom = 1.0 / std::min( fabs( vsize.x / ssize.x ), fabs( vsize.y / ssize.y ) ); + + SetCenter( centre ); + SetScale( GetScale() * zoom ); +} + + +void VIEW::SetMirror( bool aMirrorX, bool aMirrorY ) +{ + // FIXME +} + + +void VIEW::SetScale( double aScale ) +{ + SetScale( aScale, m_center ); +} + + +void VIEW::SetScale( double aScale, const VECTOR2D& aAnchor ) +{ + VECTOR2D a = ToScreen( aAnchor ); + + m_gal->SetZoomFactor( aScale ); + m_gal->ComputeWorldScreenMatrix(); + + VECTOR2D delta = ToWorld( a ) - aAnchor; + + SetCenter( m_center - delta ); + m_scale = aScale; +} + + +void VIEW::SetCenter( const VECTOR2D& aCenter ) +{ + m_center = aCenter; + m_gal->SetLookAtPoint( m_center ); + m_gal->ComputeWorldScreenMatrix(); +} + + +void VIEW::SetLayerVisible( int aLayer, bool aVisible ) +{ + // FIXME +} + + +void VIEW::sortLayers() +{ + int n = 0; + + m_orderedLayers.resize( m_layers.size() ); + + for( LayerMapIter i = m_layers.begin(); i != m_layers.end(); ++i ) + m_orderedLayers[n++] = &i->second; + + sort( m_orderedLayers.begin(), m_orderedLayers.end(), compareRenderingOrder ); +} + + +void VIEW::SetLayerOrder( int aLayer, int aRenderingOrder ) +{ + m_layers[aLayer].renderingOrder = aRenderingOrder; + sortLayers(); +} + + +struct VIEW::drawItem +{ + drawItem( VIEW* aView, int aCurrentLayer ) : + count( 0 ), countCached( 0 ), currentLayer( aCurrentLayer ), time( 0 ), view( aView ) + { + } + + void operator()( VIEW_ITEM* aItem ) + { + BOX2I tmp; + uint64_t ts = rdtsc(); + GAL* gal = view->GetGAL(); + + if( view->m_useGroups ) + { + int group = aItem->m_cachedGroup; + + if( group >= 0 && aItem->ViewIsVisible() ) + { + gal->DrawGroup( group ); + countCached++; + } + else + { + group = gal->BeginGroup(); + aItem->m_cachedGroup = group; + aItem->ViewDraw( 0, gal, tmp ); + gal->EndGroup(); + gal->DrawGroup( group ); + } + } + else if( aItem->ViewIsVisible() ) + { + if( !( view->m_painter + && view->m_painter->Draw( static_cast( aItem ), currentLayer ) ) ) + { + // Fallback, if there is no painter or painter does not know how to draw aItem + aItem->ViewDraw( currentLayer, gal, tmp ); + } + } + + time += rdtsc() - ts; + count++; + } + + int count; + int countCached; + int currentLayer; + uint64_t time; + VIEW* view; +}; + +void VIEW::redrawRect( const BOX2I& aRect ) +{ + int totalItems = 0, totalCached = 0; + uint64_t totalDrawTime = 0; + prof_counter totalCycles, totalRealTime; + + prof_start( &totalRealTime, false ); + prof_start( &totalCycles, true ); + + BOOST_FOREACH( VIEW_LAYER* l, m_orderedLayers ) + { + drawItem drawFunc( this, l->id ); + + m_gal->SetLayerDepth( (double) l->renderingOrder ); + l->items->Query( aRect, drawFunc ); + l->isDirty = false; + + totalItems += drawFunc.count; + totalDrawTime += drawFunc.time; + totalCached += drawFunc.countCached; + } + + prof_end( &totalCycles ); + prof_end( &totalRealTime ); + + wxLogDebug( "Redraw::items %d (%d cached), %.1f ms/frame (%.0f FPS), draw/geometry ratio: %.1f%%", + totalItems, totalCached, (double) totalRealTime.value / 1000.0, + 1000000.0 / (double) totalRealTime.value, + (double) totalDrawTime / (double) totalCycles.value * 100.0 ); +} + + +struct VIEW::unlinkItem +{ + void operator()( VIEW_ITEM* aItem ) + { + aItem->m_view = NULL; + } +}; + + +void VIEW::Clear() +{ + BOX2I r; + + r.SetMaximum(); + + for( LayerMapIter i = m_layers.begin(); i != m_layers.end(); ++i ) + { + VIEW_LAYER* l = &( ( *i ).second ); + unlinkItem v; + if( m_dynamic ) + l->items->Query( r, v ); + + l->items->RemoveAll(); + }; +} + + +void VIEW::Redraw() +{ + VECTOR2D screenSize = m_gal->GetScreenPixelSize(); + BOX2I rect( ToWorld( VECTOR2D( 0, 0 ) ), + ToWorld( screenSize ) - ToWorld( VECTOR2D( 0, 0 ) ) ); + + rect.Normalize(); + redrawRect( rect ); +} + + +VECTOR2D VIEW::GetScreenPixelSize() const +{ + return m_gal->GetScreenPixelSize(); +} + + +void VIEW::invalidateItem( VIEW_ITEM* aItem, int aUpdateFlags ) +{ + int layer_indices[VIEW_MAX_LAYERS], layer_count; + + aItem->ViewGetLayers( layer_indices, layer_count ); + + for( int i = 0; i < layer_count; i++ ) + { + VIEW_LAYER* l = &m_layers[layer_indices[i]]; + + l->dirtyExtents = + l->isDirty ? aItem->ViewBBox() : l->dirtyExtents.Merge( aItem->ViewBBox() ); + + if( aUpdateFlags & VIEW_ITEM::GEOMETRY ) + { + l->items->Remove( aItem ); + l->items->Insert( aItem ); /* reinsert */ + + if( m_useGroups ) + aItem->m_cachedGroup = -1; + } + } + + if( m_useGroups && aItem->m_cachedGroup >= 0 ) + { + m_gal->DeleteGroup( aItem->m_cachedGroup ); + aItem->m_cachedGroup = -1; + } +} + + +struct VIEW::clearItemCache +{ + clearItemCache( VIEW* aView ) : + view( aView ) + { + } + + void operator()( VIEW_ITEM* aItem ) + { + if( aItem->m_cachedGroup >= 0 ) + { + view->GetGAL()->DeleteGroup( aItem->m_cachedGroup ); + aItem->m_cachedGroup = -1; + } + } + + VIEW* view; +}; + + +void VIEW::clearGroupCache() +{ + BOX2I r; + + r.SetMaximum(); + + for( LayerMapIter i = m_layers.begin(); i != m_layers.end(); ++i ) + { + VIEW_LAYER* l = & ( ( *i ).second ); + clearItemCache visitor( this ); + l->items->Query( r, visitor ); + }; +} diff --git a/common/view/view_item.cpp b/common/view/view_item.cpp new file mode 100644 index 0000000000..dce0120882 --- /dev/null +++ b/common/view/view_item.cpp @@ -0,0 +1,68 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * 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 + */ + +#include + +#include +#include + +using namespace KiGfx; + +void VIEW_ITEM::ViewSetVisible( bool aIsVisible ) +{ + bool update = false; + + if( m_viewVisible != aIsVisible ) + { + update = true; + } + + m_viewVisible = aIsVisible; + + // update only if the visibility has really changed + if( update ) + { + ViewUpdate( APPEARANCE ); + } +} + + +void VIEW_ITEM::ViewUpdate( int aUpdateFlags, bool aForceImmediateRedraw ) +{ + m_view->invalidateItem( this, aUpdateFlags ); + + if( aForceImmediateRedraw ) + { + m_view->Redraw(); + } +} + + +void VIEW_ITEM::ViewRelease() +{ + if( m_view && m_view->IsDynamic() ) + { + m_view->Remove( this ); + } +} diff --git a/common/view/wx_view_controls.cpp b/common/view/wx_view_controls.cpp new file mode 100644 index 0000000000..67b5cf603d --- /dev/null +++ b/common/view/wx_view_controls.cpp @@ -0,0 +1,137 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * 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 + */ + +#include +#include + +#include +#include + +using namespace KiGfx; + +WX_VIEW_CONTROLS::WX_VIEW_CONTROLS( VIEW* aView, wxWindow* aParentPanel ) : + VIEW_CONTROLS( aView ), + m_autoPanMargin( 0.1 ), + m_autoPanSpeed( 0.15 ), + m_autoPanCornerRatio( 0.1 ), + m_parentPanel( aParentPanel ) +{ + m_parentPanel->Connect( wxEVT_MOTION, wxMouseEventHandler( + WX_VIEW_CONTROLS::onMotion ), NULL, this ); + m_parentPanel->Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( + WX_VIEW_CONTROLS::onWheel ), NULL, this ); + m_parentPanel->Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( + WX_VIEW_CONTROLS::onButton ), NULL, this ); + m_parentPanel->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( + WX_VIEW_CONTROLS::onButton ), NULL, this ); +} + + +void WX_VIEW_CONTROLS::onMotion( wxMouseEvent& event ) +{ + VECTOR2D mousePoint( event.GetX(), event.GetY() ); + + if( event.Dragging() ) + { + if( m_isDragPanning ) + { + VECTOR2D d = m_dragStartPoint - mousePoint; + VECTOR2D delta = m_view->ToWorld( d, false ); + + m_view->SetCenter( m_lookStartPoint + delta ); + m_parentPanel->Refresh(); + } + else + { + event.Skip(); + } + } +} + + +void WX_VIEW_CONTROLS::onWheel( wxMouseEvent& event ) +{ + const double wheelPanSpeed = 0.001; + + if( event.ControlDown() ) + { + wxLongLong timeStamp = wxGetLocalTimeMillis(); + double timeDiff = timeStamp.ToDouble() - m_timeStamp.ToDouble(); + + m_timeStamp = timeStamp; + double zoomScale; + + // Set scaling speed depending on scroll wheel event interval + if( timeDiff < 500 && timeDiff > 0 ) + { + zoomScale = ( event.GetWheelRotation() > 0.0 ) ? 2.05 - timeDiff / 500 : + 1.0 / ( 2.05 - timeDiff / 500 ); + } + else + { + zoomScale = ( event.GetWheelRotation() > 0.0 ) ? 1.05 : 0.95; + } + + + VECTOR2D anchor = m_view->ToWorld( VECTOR2D( event.GetX(), event.GetY() ) ); + m_view->SetScale( m_view->GetScale() * zoomScale, anchor ); + m_parentPanel->Refresh(); + } + else + { + VECTOR2D scrollVec = m_view->ToWorld( m_view->GetScreenPixelSize() * + ( (double) event.GetWheelRotation() * wheelPanSpeed ), false ); + double scrollSpeed; + + if( abs( scrollVec.x ) > abs( scrollVec.y ) ) + scrollSpeed = scrollVec.x; + else + scrollSpeed = scrollVec.y; + + VECTOR2D t = m_view->GetScreenPixelSize(); + VECTOR2D delta( event.ShiftDown() ? scrollSpeed : 0.0, + !event.ShiftDown() ? scrollSpeed : 0.0 ); + + m_view->SetCenter( m_view->GetCenter() + delta ); + m_parentPanel->Refresh(); + } +} + + +void WX_VIEW_CONTROLS::onButton( wxMouseEvent& event ) +{ + if( event.RightDown() ) + { + m_isDragPanning = true; + m_dragStartPoint = VECTOR2D( event.GetX(), event.GetY() ); + m_lookStartPoint = m_view->GetCenter(); // ookAtPoint(); + } + else if( event.RightUp() ) + { + m_isDragPanning = false; + } + + event.Skip(); +} diff --git a/common/zoom.cpp b/common/zoom.cpp index 2c25c7a19f..6d3dac3ee9 100644 --- a/common/zoom.cpp +++ b/common/zoom.cpp @@ -39,7 +39,11 @@ #include #include #include - +#ifdef KICAD_GAL +#include +#include +#include +#endif void EDA_DRAW_FRAME::RedrawScreen( const wxPoint& aCenterPoint, bool aWarpPointer ) @@ -82,7 +86,10 @@ void EDA_DRAW_FRAME::Zoom_Automatique( bool aWarpPointer ) if( screen->m_FirstRedraw ) screen->SetCrossHairPosition( screen->GetScrollCenterPosition() ); - RedrawScreen( screen->GetScrollCenterPosition(), aWarpPointer ); + if( !m_galCanvasActive ) + RedrawScreen( screen->GetScrollCenterPosition(), aWarpPointer ); + else + m_canvas->Hide(); } @@ -160,6 +167,36 @@ void EDA_DRAW_FRAME::OnZoom( wxCommandEvent& event ) m_canvas->Refresh(); break; +#ifdef KICAD_GAL + // Switch canvas between standard and GAL-based + case ID_SWITCH_CANVAS: + { + UseGalCanvas( !m_galCanvasActive ); + + KiGfx::VIEW* view = m_galCanvas->GetView(); + KiGfx::GAL* gal = m_galCanvas->GetGAL(); + double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor(); + + // Display the same view after canvas switching + if( m_galCanvasActive ) + { + double zoom = 1 / ( zoomFactor * m_canvas->GetZoom() ); + view->SetScale( zoom ); + + view->SetCenter( VECTOR2D( m_canvas->GetScreenCenterLogicalPosition() ) ); + } + else + { + double zoom = 1 / ( zoomFactor * view->GetScale() ); + m_canvas->SetZoom( zoom ); + + VECTOR2D center = view->GetCenter(); + RedrawScreen( wxPoint( center.x, center.y ), false ); + } + } + break; +#endif + case ID_POPUP_ZOOM_CENTER: center = screen->GetCrossHairPosition(); RedrawScreen( center, true ); diff --git a/cvpcb/CMakeLists.txt b/cvpcb/CMakeLists.txt index f154c169d4..c7922c58d6 100644 --- a/cvpcb/CMakeLists.txt +++ b/cvpcb/CMakeLists.txt @@ -104,6 +104,13 @@ target_link_libraries(cvpcb ${GDI_PLUS_LIBRARIES} ) +if(KICAD_GAL) +target_link_libraries(cvpcb + gal + ${GLEW_LIBRARIES} + ) +endif(KICAD_GAL) + ### # Add cvpcb as install target ### diff --git a/include/base_struct.h b/include/base_struct.h index c258b17d47..487fea0eb9 100644 --- a/include/base_struct.h +++ b/include/base_struct.h @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -389,7 +390,11 @@ public: * is a base class for most all the KiCad significant classes, used in * schematics and boards. */ +#ifdef KICAD_GAL +class EDA_ITEM : public KiGfx::VIEW_ITEM +#else class EDA_ITEM +#endif { private: @@ -740,6 +745,12 @@ public: */ virtual EDA_ITEM& operator=( const EDA_ITEM& aItem ); + /// @copydoc VIEW_ITEM::ViewBBox() + virtual const BOX2I ViewBBox() const; + + /// @copydoc VIEW_ITEM::ViewGetLayers() + virtual void ViewGetLayers( int aLayers[], int& aCount ) const; + #if defined(DEBUG) /** diff --git a/include/class_board_item.h b/include/class_board_item.h index 2c64957d48..a0077a19c6 100644 --- a/include/class_board_item.h +++ b/include/class_board_item.h @@ -248,6 +248,9 @@ public: static std::string FormatInternalUnits( const wxPoint& aPoint ); static std::string FormatInternalUnits( const wxSize& aSize ); + + /// @copydoc VIEW_ITEM::ViewGetLayers() + virtual void ViewGetLayers( int aLayers[], int& aCount ) const; }; #endif /* BOARD_ITEM_STRUCT_H */ diff --git a/include/class_drawpanel.h b/include/class_drawpanel.h index 0e3fe4cf22..dcca64cb88 100644 --- a/include/class_drawpanel.h +++ b/include/class_drawpanel.h @@ -258,6 +258,9 @@ public: void OnCharHook( wxKeyEvent& event ); void OnPan( wxCommandEvent& event ); +#ifdef KICAD_GAL + void OnSize( wxSizeEvent& event ); +#endif void EraseScreen( wxDC* DC ); void OnScrollWin( wxCommandEvent& event ); diff --git a/include/class_drawpanel_gal.h b/include/class_drawpanel_gal.h new file mode 100644 index 0000000000..206d88a5ff --- /dev/null +++ b/include/class_drawpanel_gal.h @@ -0,0 +1,99 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * 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 class_drawpanel_gal.h: + * @brief EDA_DRAW_PANEL_GAL class definition. + */ + +#ifndef PANELGAL_WXSTRUCT_H +#define PANELGAL_WXSTRUCT_H + +#include +#include + +#include + +class BOARD; + +namespace KiGfx +{ +class GAL; +class VIEW; +class WX_VIEW_CONTROLS; +class PAINTER; +}; + + +class EDA_DRAW_PANEL_GAL : public wxWindow +{ +public: + enum GalType { + GAL_TYPE_OPENGL, ///< OpenGL implementation + GAL_TYPE_CAIRO, ///< Cairo implementation + GAL_TYPE_WXDC ///< WXDC implementation + }; + + EDA_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWindowId, const wxPoint& aPosition, + const wxSize& aSize, GalType aGalType = GAL_TYPE_OPENGL ); + ~EDA_DRAW_PANEL_GAL(); + + /** + * Function SwitchBackend + * Switches method of rendering graphics. + * @param aGalType is a type of rendering engine that you want to use. + */ + void SwitchBackend( GalType aGalType, bool aUseShaders = false ); + + /** + * Function GetGAL + * Returns a pointer to the GAL instance used in the panel. + * @return KiGfx::GAL* Instance of GAL. + */ + KiGfx::GAL* GetGAL() { return m_gal; } + + void SetView( KiGfx::VIEW* aView ) { m_view = aView; } + KiGfx::VIEW* GetView() const { return m_view; } + +protected: + void onPaint( wxEvent& event ); + void onSize( wxSizeEvent& aEvent ); + void onMotion( wxMouseEvent& event ); + void onButton( wxMouseEvent& event ); + void onEraseBackground( wxEvent& event ); + + KiGfx::GAL* m_gal; ///< Interface for drawing objects on a 2D-surface + KiGfx::VIEW* m_view; ///< Stores view settings (scale, center, etc.) + ///< and items to be drawn + KiGfx::PAINTER* m_painter; ///< Contains information about how to draw items + ///< using GAL + KiGfx::WX_VIEW_CONTROLS* m_viewControls; ///< Control for VIEW (moving, zooming, etc.) + + VECTOR2D m_screenSize; ///< Stores current screen size + wxWindow* m_parentFrame; ///< Pointer to the parent frame + + std::string m_galShaderPath; ///< Path to shader files, used in OpenGL mode +}; + +#endif diff --git a/include/gal/cairo/cairo_gal.h b/include/gal/cairo/cairo_gal.h new file mode 100644 index 0000000000..002622296d --- /dev/null +++ b/include/gal/cairo/cairo_gal.h @@ -0,0 +1,411 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * CairoGal - Graphics Abstraction Layer for Cairo + * + * 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 + */ + +#ifndef CAIROGAL_H_ +#define CAIROGAL_H_ + +#include +#include + +#include + +#include + + +#if defined(__WXMSW__) +#define SCREEN_DEPTH 24 +#else +#if wxCHECK_VERSION( 2, 9, 0 ) +#define SCREEN_DEPTH wxBITMAP_SCREEN_DEPTH +#else +#define SCREEN_DEPTH 32 +#endif +#endif + +#define EXCEPTION_ZERO_CLIENT_RECTANGLE 0 +#define EXCEPTION_ZERO_CONTEXT 1 + +/** + * @brief Class CAIRO_GAL is the cairo implementation of the graphics abstraction layer. + * + * Quote from Wikipedia: + * " Cairo is a software library used to provide a vector graphics-based, device-independent + * API for software developers. It is designed to provide primitives for 2-dimensional + * drawing across a number of different backends. " + *
+ * Cairo offers also backends for Postscript and PDF surfaces. So it can be used for printing + * of KiCad graphics surfaces as well. + * + */ +namespace KiGfx +{ +class CAIRO_GAL : public GAL, public wxWindow +{ +public: + /** + * Constructor CAIRO_GAL + * + * @param aParent is the wxWidgets immediate wxWindow parent of this object. + * + * @param aMouseListener is the wxEvtHandler that should receive the mouse events, + * this can be can be any wxWindow, but is often a wxFrame container. + * + * @param aPaintListener is the wxEvtHandler that should receive the paint + * event. This can be any wxWindow, but is often a derived instance + * of this class or a containing wxFrame. The "paint event" here is + * a wxCommandEvent holding EVT_GAL_REDRAW, as sent by PostPaint(). + * + * @param aName is the name of this window for use by wxWindow::FindWindowByName() + */ + CAIRO_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener = NULL, + wxEvtHandler* aPaintListener = NULL, const wxString& aName = wxT("CairoCanvas") ); + + virtual ~CAIRO_GAL(); + + // --------------- + // Drawing methods + // --------------- + + /// @copydoc GAL::BeginDrawing() + virtual void BeginDrawing() throw (int); + + /// @copydoc GAL::EndDrawing() + virtual void EndDrawing(); + + /// @copydoc GAL::DrawLine() + virtual void DrawLine( VECTOR2D aStartPoint, VECTOR2D aEndPoint ); + + /// @copydoc GAL::DrawPolyline() + virtual void DrawPolyline( std::deque& aPointList ); + + /// @copydoc GAL::DrawCircle() + virtual void DrawCircle( VECTOR2D aCenterPoint, double aRadius ); + + /// @copydoc GAL::DrawArc() + virtual void + DrawArc( VECTOR2D aCenterPoint, double aRadius, double aStartAngle, double aEndAngle ); + + /// @copydoc GAL::DrawRectangle() + virtual void DrawRectangle( VECTOR2D aStartPoint, VECTOR2D aEndPoint ); + + /// @copydoc GAL::DrawPolygon() + virtual void DrawPolygon( const std::deque& aPointList ); + + /// @copydoc GAL::DrawCurve() + virtual void DrawCurve( VECTOR2D startPoint, VECTOR2D controlPointA, VECTOR2D controlPointB, + VECTOR2D endPoint ); + + // -------------- + // Screen methods + // -------------- + + /// @brief Resizes the canvas. + virtual void ResizeScreen ( int aWidth, int aHeight ); + + /// @brief Shows/hides the GAL canvas + virtual bool Show( bool aShow ); + + /// @copydoc GAL::Flush() + virtual void Flush(); + + /// @copydoc GAL::ClearScreen() + virtual void ClearScreen(); + + // ----------------- + // Attribute setting + // ----------------- + + /// @copydoc GAL::SetIsFill() + virtual void SetIsFill( bool aIsFillEnabled ); + + /// @copydoc GAL::SetIsStroke() + virtual void SetIsStroke( bool aIsStrokeEnabled ); + + /// @copydoc GAL::SetFillColor() + virtual void SetFillColor( COLOR4D aColor ); + + /// @copydoc GAL::SetStrokeColor() + virtual void SetStrokeColor( COLOR4D aColor ); + + /// @copydoc GAL::GetStrokeColor() + COLOR4D GetStrokeColor(); + + /// @copydoc GAL::SetBackgroundColor() + virtual void SetBackgroundColor( COLOR4D aColor ); + + /// @copydoc GAL::SetLineCap() + virtual void SetLineCap( LineCap aLineCap ); + + /// @copydoc GAL::SetLineJoin() + virtual void SetLineJoin( LineJoin aLineJoin ); + + /// @copydoc GAL::SetLineWidth() + virtual void SetLineWidth( double aLineWidth ); + + /// @copydoc GAL::GetLineWidth() + double GetLineWidth(); + + /// @copydoc GAL::SetLayerDepth() + virtual void SetLayerDepth( double aLayerDepth ) + { + super::SetLayerDepth( aLayerDepth ); + } + + // -------------- + // Transformation + // -------------- + + /// @copydoc GAL::Transform() + virtual void Transform( MATRIX3x3D aTransformation ); + + /// @copydoc GAL::Rotate() + virtual void Rotate( double aAngle ); + + /// @copydoc GAL::Translate() + virtual void Translate( VECTOR2D aTranslation ); + + /// @copydoc GAL::Scale() + virtual void Scale( VECTOR2D aScale ); + + /// @copydoc GAL::Save() + virtual void Save(); + + /// @copydoc GAL::Restore() + virtual void Restore(); + + // -------------------------------------------- + // Group methods + // --------------------------------------------- + + /// @copydoc GAL::BeginGroup() + virtual int BeginGroup(); + + /// @copydoc GAL::EndGroup() + virtual void EndGroup(); + + /// @copydoc GAL::DrawGroup() + virtual void DrawGroup( int aGroupNumber ); + + /// @copydoc GAL::DeleteGroup() + virtual void DeleteGroup( int aGroupNumber ); + + // -------------------------------------------------------- + // Handling the world <-> screen transformation + // -------------------------------------------------------- + + /// @copydoc GAL::ComputeWorldScreenMatrix() + virtual void ComputeWorldScreenMatrix(); + + /// @copydoc GAL::GetWorldScreenMatrix() + MATRIX3x3D GetWorldScreenMatrix(); + + /// @copydoc GAL::SetWorldScreenMatrix() + void SetWorldScreenMatrix( MATRIX3x3D aMatrix ); + + /// @copydoc GAL::SetWorldUnitLength() + void SetWorldUnitLength( double aWorldUnitLength ); + + /// @copydoc GAL::SetScreenDPI() + void SetScreenDPI( double aScreenDPI ); + + /// @copydoc GAL::SetLookAtPoint() + void SetLookAtPoint( VECTOR2D aPoint ); + + /// @copydoc GAL::GetLookAtPoint() + VECTOR2D GetLookAtPoint(); + + /// @copydoc GAL::SetZoomFactor() + void SetZoomFactor( double aZoomFactor ); + + /// @copydoc GAL::GetZoomFactor() + double GetZoomFactor(); + + /// @copydoc GAL::SaveScreen() + virtual void SaveScreen(); + + /// @copydoc GAL::RestoreScreen() + virtual void RestoreScreen(); + + // ------- + // Cursor + // ------- + + /// @copydoc GAL::ComputeCursorToWorld() + virtual VECTOR2D ComputeCursorToWorld( VECTOR2D aCursorPosition ); + + /// @copydoc GAL::SetIsCursorEnabled() + void SetIsCursorEnabled( bool aIsCursorEnabled ); + + /// @copydoc GAL::DrawCursor() + virtual void DrawCursor( VECTOR2D aCursorPosition ); + + /** + * Function PostPaint + * posts an event to m_paint_listener. A post is used so that the actual drawing + * function can use a device context type that is not specific to the wxEVT_PAINT event. + */ + void PostPaint() + { + if( paintListener ) + { + wxCommandEvent redrawEvent( EVT_GAL_REDRAW ); + wxPostEvent( paintListener, redrawEvent ); + } + } + + void SetMouseListener( wxEvtHandler* aMouseListener ) + { + mouseListener = aMouseListener; + } + + void SetPaintListener( wxEvtHandler* aPaintListener ) + { + paintListener = aPaintListener; + } + +protected: + virtual void DrawGridLine(VECTOR2D aStartPoint, VECTOR2D aEndPoint); + +private: + /// Super class definition + typedef GAL super; + + // Variables related to wxWidgets + wxWindow* parentWindow; ///< Parent window + wxEvtHandler* mouseListener; ///< Mouse listener + wxEvtHandler* paintListener; ///< Paint listener + wxRect clientRectangle; ///< Area definition of the surface + unsigned int bufferSize; ///< Size of buffers cairoOutput, bitmapBuffers + unsigned char* wxOutput; ///< wxImage comaptible buffer + + // Cursor variables + std::deque savedCursorPixels; ///< Saved pixels of the cursor + bool isDeleteSavedPixels; ///< True, if the saved pixels can be discarded + wxPoint savedCursorPosition; ///< The last cursor position + wxBitmap* cursorPixels; ///< Cursor pixels + wxBitmap* cursorPixelsSaved; ///< Saved cursor pixels + int cursorSize; ///< Cursor size + + // Variables for the grouping function + int actualGroupIndex; ///< The index of the actual group + bool isGrouping; ///< Is grouping enabled ? + bool isElementAdded; ///< Was an graphic element added ? + std::deque pathList; ///< List of stored paths + + /// Maximum number of arguments for one command + static const int MAX_CAIRO_ARGUMENTS = 6; + + /// Definitions for the command recorder + enum GraphicsCommand + { + CMD_SET_FILL, ///< Enable/disable filling + CMD_SET_STROKE, ///< Enable/disable stroking + CMD_SET_FILLCOLOR, ///< Set the fill color + CMD_SET_STROKECOLOR, ///< Set the stroke color + CMD_SET_LINE_WIDTH, ///< Set the line width + CMD_SET_LINE_CAP, ///< Set the line cap style + CMD_SET_LINE_JOIN, ///< Set the line join style + CMD_STROKE_PATH, ///< Set the stroke path + CMD_FILL_PATH, ///< Set the fill path + CMD_TRANSFORM, ///< Transform the actual context + CMD_ROTATE, ///< Rotate the context + CMD_TRANSLATE, ///< Translate the context + CMD_SCALE, ///< Scale the context + CMD_SAVE, ///< Save the transformation matrix + CMD_RESTORE, ///< Restore the transformation matrix + CMD_CALL_GROUP ///< Call a group + }; + + /// Type definition for an graphics group element + typedef struct + { + GraphicsCommand command; ///< Command to execute + double arguments[MAX_CAIRO_ARGUMENTS]; ///< Arguments for Cairo commands + bool boolArgument; ///< A bool argument + int intArgument; ///< An int argument + cairo_path_t* cairoPath; ///< Pointer to a Cairo path + } GroupElement; + + typedef std::deque Group; ///< A graphic group type definition + std::deque groups; ///< List of graphic groups + + // Variables related to Cairo <-> wxWidgets + cairo_matrix_t cairoWorldScreenMatrix; ///< Cairo world to screen transformation matrix + cairo_t* cairoImage; ///< Cairo image + cairo_surface_t* cairoSurface; ///< Cairo surface + unsigned int* bitmapBuffer; ///< Storage of the cairo image + unsigned int* bitmapBufferBackup; ///< Backup storage of the cairo image + wxBitmap* wxBitmap_; ///< Pointer to the wxWidgets bitmap + int stride; ///< Stride value for Cairo + // wxClientDC* clientDC; ///< Pointer to the clientDC + int screenSizeY; ///< Vertical size of the actual surface + + // Mapping between Cairo and GAL line attributes + std::map lineCapMap; ///< Line cap style mapping + std::map lineJoinMap; ///< Line join style mapping + + // Methods + void storePath(); ///< Store the actual path + + // Event handlers + /** + * @brief Paint event handler. + * + * @param aEvent is the paint event. + */ + void onPaint( wxPaintEvent& aEvent ); + void onEraseBackground( wxEraseEvent& aEvent ); + + /** + * @brief Window resizing event handler. + * + * @param aEvent is the resizing event. + */ + void onSize( wxSizeEvent& aEvent ); + + /** + * @brief Mouse event handler, forwards the event to the child. + * + * @param aEvent is the mouse event to be forwarded. + */ + void skipMouseEvent( wxMouseEvent& aEvent ); + + /** + * @brief Initialize the cursor. + * + * @param aCursorSize is the size of the cursor. + */ + void initCursor( int aCursorSize ); + + /// Allocate the bitmaps for drawing + void allocateBitmaps(); + + /// Allocate the bitmaps for drawing + void deleteBitmaps(); +}; +} // namespace KiGfx + +#endif // CAIROGAL_H_ diff --git a/include/gal/definitions.h b/include/gal/definitions.h new file mode 100644 index 0000000000..b6a66c584c --- /dev/null +++ b/include/gal/definitions.h @@ -0,0 +1,34 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Macro definitions + * + * 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 + */ + +#ifndef DEFINITIONS_H_ +#define DEFINITIONS_H_ + +/// Swap the variables if a condition is met. +#define SWAP( varA, condition, varB ) if( varA condition varB ) { double tmp = varA; varA = varB; \ + varB = tmp; } + +#endif /* DEFINITIONS_H_ */ diff --git a/include/gal/graphics_abstraction_layer.h b/include/gal/graphics_abstraction_layer.h new file mode 100644 index 0000000000..85f686d165 --- /dev/null +++ b/include/gal/graphics_abstraction_layer.h @@ -0,0 +1,694 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Graphics Abstraction Layer (GAL) - base class + * + * 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 + */ + +#ifndef GRAPHICSABSTRACTIONLAYER_H_ +#define GRAPHICSABSTRACTIONLAYER_H_ + +#include +#include + +#include + +#include +#include + + +namespace KiGfx +{ +// Event declaration +extern const wxEventType EVT_GAL_REDRAW; + +/** + * LineCap: Type definition of the line end point style + */ +enum LineCap +{ + LINE_CAP_BUTT, ///< Stop line at the end point + LINE_CAP_ROUND, ///< Draw a circle at the end point + LINE_CAP_SQUARED ///< Draw a square at the end point +}; + +/** + * LineJoin: Type definition of the line joint style + */ +enum LineJoin +{ + LINE_JOIN_MITER, ///< Use sharp corners + LINE_JOIN_ROUND, ///< Insert a circle at the joints + LINE_JOIN_BEVEL ///< Diagonal corner +}; + +/** + * GridStyle: Type definition of the grid style + */ +enum GridStyle +{ + GRID_STYLE_LINES, ///< Use lines for the grid + GRID_STYLE_DOTS ///< Use dots for the grid +}; + +/** + * @brief Class GAL is the abstract interface for drawing on a 2D-surface. + * + * The functions are optimized for drawing shapes of an EDA-program such as KiCad. Most methods + * are abstract and need to be implemented by a lower layer, for example by a cairo or OpenGL implementation. + *
+ * Almost all methods use world coordinates as arguments. The board design is defined in world space units; + * for drawing purposes these are transformed to screen units with this layer. So zooming is handled here as well. + * + */ +class GAL +{ +public: + // Constructor / Destructor + GAL(); + virtual ~GAL(); + + // --------------- + // Drawing methods + // --------------- + + /// @brief Begin the drawing, needs to be called for every new frame. + virtual void BeginDrawing() = 0; + + /// @brief End the drawing, needs to be called for every new frame. + virtual void EndDrawing() = 0; + + /** + * @brief Draw a line. + * + * Start and end points are defined as 2D-Vectors. + * + * @param aStartPoint is the start point of the line. + * @param aEndPoint is the end point of the line. + */ + virtual void DrawLine( VECTOR2D aStartPoint, VECTOR2D aEndPoint ) = 0; + + /** + * @brief Draw a polyline + * + * @param aPointList is a list of 2D-Vectors containing the polyline points. + */ + virtual void DrawPolyline( std::deque& aPointList ) = 0; + + /** + * @brief Draw a circle using world coordinates. + * + * @param aCenterPoint is the center point of the circle. + * @param aRadius is the radius of the circle. + */ + virtual void DrawCircle( VECTOR2D aCenterPoint, double aRadius ) = 0; + + /** + * @brief Draw an arc. + * + * @param aCenterPoint is the center point of the arc. + * @param aRadius is the arc radius. + * @param aStartAngle is the start angle of the arc. + * @param aEndAngle is the end angle of the arc. + */ + virtual void + DrawArc( VECTOR2D aCenterPoint, double aRadius, double aStartAngle, double aEndAngle ) = 0; + + /** + * @brief Draw a rectangle. + * + * @param aStartPoint is the start point of the rectangle. + * @param aEndPoint is the end point of the rectangle. + */ + virtual void DrawRectangle( VECTOR2D aStartPoint, VECTOR2D aEndPoint ) = 0; + + /** + * @brief Draw a polygon. + * + * @param aPointList is the list of the polygon points. + */ + virtual void DrawPolygon( const std::deque& aPointList ) = 0; + + /** + * @brief Draw a cubic bezier spline. + * + * @param startPoint is the start point of the spline. + * @param controlPointA is the first control point. + * @param controlPointB is the second control point. + * @param endPoint is the end point of the spline. + */ + virtual void DrawCurve( VECTOR2D startPoint, VECTOR2D controlPointA, + VECTOR2D controlPointB, VECTOR2D endPoint ) = 0; + + // -------------- + // Screen methods + // -------------- + + /// @brief Resizes the canvas. + virtual void ResizeScreen( int aWidth, int aHeight ) = 0; + + /// @brief Shows/hides the GAL canvas + virtual bool Show( bool aShow ) = 0; + + /// @brief Returns GAL canvas size in pixels + VECTOR2D GetScreenPixelSize() const + { + return screenSize; + } + + /// @brief Force all remaining objects to be drawn. + virtual void Flush() = 0; + + /// @brief Clear the screen. + virtual void ClearScreen() = 0; + + // ----------------- + // Attribute setting + // ----------------- + + /** + * @brief Enable/disable fill. + * + * @param aIsFillEnabled is true, when the graphics objects should be filled, else false. + */ + inline virtual void SetIsFill( bool aIsFillEnabled ) + { + isFillEnabled = aIsFillEnabled; + } + + /** + * @brief Enable/disable stroked outlines. + * + * @param aIsStrokeEnabled is true, if the outline of an object should be stroked. + */ + inline virtual void SetIsStroke( bool aIsStrokeEnabled ) + { + isStrokeEnabled = aIsStrokeEnabled; + } + + /** + * @brief Set the fill color. + * + * @param aColor is the color for filling. + */ + inline virtual void SetFillColor( COLOR4D aColor ) + { + fillColor = aColor; + } + + /** + * @brief Set the stroke color. + * + * @param aColor is the color for stroking the outline. + */ + inline virtual void SetStrokeColor( COLOR4D aColor ) + { + strokeColor = aColor; + } + + /** + * @brief Get the stroke color. + * + * @return the color for stroking the outline. + */ + inline COLOR4D GetStrokeColor() + { + return strokeColor; + } + + /** + * @brief Set the background color. + * + * @param aColor is the color for background filling. + */ + virtual void SetBackgroundColor( COLOR4D aColor ) = 0; + + /** + * @brief Set the style of the line caps. + * + * @param aLineCap is the line cap style. + */ + inline virtual void SetLineCap( LineCap aLineCap ) + { + lineCap = aLineCap; + } + + /** + * @brief Set the line join style. + * + * @param aLineJoin is the line join style. + */ + inline virtual void SetLineJoin( LineJoin aLineJoin ) + { + lineJoin = aLineJoin; + } + + /** + * @brief Set the line width. + * + * @param aLineWidth is the line width. + */ + inline virtual void SetLineWidth( double aLineWidth ) + { + lineWidth = aLineWidth; + } + + /** + * @brief Get the line width. + * + * @return the actual line width. + */ + inline double GetLineWidth() + { + return lineWidth; + } + + /** + * @brief Set the depth of the layer (position on the z-axis) + * + * @param aLayerDepth the layer depth for the objects. + */ + inline virtual void SetLayerDepth( double aLayerDepth ) + { + layerDepth = aLayerDepth; + } + + // -------------- + // Transformation + // -------------- + + /** + * @brief Transform the context. + * + * @param aTransformation is the ransformation matrix. + */ + virtual void Transform( MATRIX3x3D aTransformation ) = 0; + + /** + * @brief Rotate the context. + * + * @param aAngle is the rotation angle in radians. + */ + virtual void Rotate( double aAngle ) = 0; + + /** + * @brief Translate the context. + * + * @param aTranslation is the translation vector. + */ + virtual void Translate( VECTOR2D aTranslation ) = 0; + + /** + * @brief Scale the context. + * + * @param aScale is the scale factor for the x- and y-axis. + */ + virtual void Scale( VECTOR2D aScale ) = 0; + + /// @brief Save the context. + virtual void Save() = 0; + + /// @brief Restore the context. + virtual void Restore() = 0; + + // -------------------------------------------- + // Group methods + // --------------------------------------------- + + /** + * @brief Begin a group. + * + * A group is a collection of graphic items. + * Hierarchical groups are possible, attributes and transformations can be used. + * + * @return the number of the group. + */ + virtual int BeginGroup() = 0; + + /// @brief End the group. + virtual void EndGroup() = 0; + + /** + * @brief Draw the stored group. + * + * @param aGroupNumber is the group number. + */ + virtual void DrawGroup( int aGroupNumber ) = 0; + + /** + * @brief Delete the group from the memory. + * + * @param aGroupNumber is the group number. + */ + virtual void DeleteGroup( int aGroupNumber ) = 0; + + // -------------------------------------------------------- + // Handling the world <-> screen transformation + // -------------------------------------------------------- + + /// @brief Compute the world <-> screen transformation matrix + virtual void ComputeWorldScreenMatrix() = 0; + + /** + * @brief Get the world <-> screen transformation matrix. + * + * @return the transformation matrix. + */ + MATRIX3x3D GetWorldScreenMatrix() + { + return worldScreenMatrix; + } + + /** + * @brief Set the world <-> screen transformation matrix. + * + * @param aMatrix is the 3x3 world <-> screen transformation matrix. + */ + inline void SetWorldScreenMatrix( const MATRIX3x3D& aMatrix ) + { + worldScreenMatrix = aMatrix; + } + + /** + * @brief Set the unit length. + * + * This defines the length [inch] per one integer. For instance a value 0.001 means + * that the coordinate [1000, 1000] corresponds with a point at (1 inch, 1 inch) or + * 1 mil resolution per integer. + * + * @param aWorldUnitLength is the world Unit length. + */ + inline void SetWorldUnitLength( double aWorldUnitLength ) + { + worldUnitLength = aWorldUnitLength; + } + + /** + * @brief Set the dots per inch of the screen. + * + * This value depends on the user screen, it should be configurable by the application. + * For instance a typical notebook with HD+ resolution (1600x900) has 106 DPI. + * + * @param aScreenDPI are the screen DPI. + */ + inline void SetScreenDPI( double aScreenDPI ) + { + screenDPI = aScreenDPI; + } + + /** + * @brief Set the Point in world space to look at. + * + * This point corresponds with the center of the actual drawing area. + * + * @param aPoint is the look at point (center of the actual drawing area). + */ + inline void SetLookAtPoint( const VECTOR2D& aPoint ) + { + lookAtPoint = aPoint; + } + + /** + * @brief Get the look at point. + * + * @return the look at point. + */ + inline VECTOR2D GetLookAtPoint() + { + return lookAtPoint; + } + + /** + * @brief Set the zoom factor of the scene. + * + * @param aZoomFactor is the zoom factor. + */ + inline void SetZoomFactor( double aZoomFactor ) + { + zoomFactor = aZoomFactor; + } + + /** + * @brief Get the zoom factor + * + * @return the zoom factor. + */ + inline double GetZoomFactor() + { + return zoomFactor; + } + + /** + * @brief Set the range of the layer depth. + * + * Usually required for the OpenGL implementation, any object outside this range is not drawn. + * + * @param aDepthRange is the depth range where component x is the near clipping plane and y + * is the far clipping plane. + */ + inline void SetDepthRange( VECTOR2D aDepthRange ) + { + depthRange = aDepthRange; + } + + /** + * @brief Get the world scale. + * + * @return the actual world scale factor. + */ + inline double GetWorldScale() + { + return worldScale; + } + + /** + * @brief Save the screen contents. + */ + virtual void SaveScreen() = 0; + + /** + * @brief Save the screen contents. + */ + virtual void RestoreScreen() = 0; + + // ------------- + // Grid methods + // ------------- + + /** + * @brief Set the origin point for the grid. + * + * @param aGridOrigin is a vector containing the grid origin point, in world coordinates. + */ + inline void SetGridOrigin( const VECTOR2D& aGridOrigin ) + { + gridOrigin = aGridOrigin; + } + + /** + * @brief Sets the screen size of the grid origin marker + * + * @param aSize is the radius of the origin marker, in pixels. + */ + inline void SetGridOriginMarkerSize( int aSize ) + { + gridOriginMarkerSize = aSize; + } + + /** + * @brief Set the threshold for grid drawing. + * + * @param aThreshold is the minimum grid cell size (in pixels) for which the grid is drawn. + */ + inline void SetGridDrawThreshold( int aThreshold ) + { + gridDrawThreshold = aThreshold; + } + + /** + * @brief Set the grid size. + * + * @param aGridSize is a vector containing the grid size in x- and y direction. + */ + inline void SetGridSize( const VECTOR2D& aGridSize ) + { + gridSize = aGridSize; + } + + /** + * @brief Set the grid color. + * + * @param aGridColor is the grid color, it should have a low alpha value for the best effect. + */ + inline void SetGridColor( COLOR4D aGridColor ) + { + gridColor = aGridColor; + } + + /** + * @brief Draw every tick line wider. + * + * @param aInterval increase the width of every aInterval line, if 0 do not use this feature. + */ + inline void SetCoarseGrid( int aInterval ) + { + gridTick = aInterval; + } + + /** + * @brief Get the grid line width. + * + * @return the grid line width + */ + inline double GetGridLineWidth() + { + return gridLineWidth; + } + + /** + * @brief Set the grid line width. + * + * @param aGridLineWidth is the rid line width. + */ + inline void SetGridLineWidth( double aGridLineWidth ) + { + gridLineWidth = aGridLineWidth; + } + + /// @brief Draw the grid + void DrawGrid(); + + // TODO Not yet implemented + // virtual void SetGridStyle(GridStyle gridStyle); + + // ------- + // Cursor + // ------- + + /** + * @brief Compute the cursor position in world coordinates from given screen coordinates. + * + * @param aCursorPosition is the cursor position in screen coordinates. + * @return the cursor position in world coordinates. + */ + virtual VECTOR2D ComputeCursorToWorld( VECTOR2D aCursorPosition ) = 0; + + /** + * @brief Enable/Disable cursor. + * + * @param aIsCursorEnabled is true if the cursor should be enabled, else false. + */ + inline void SetIsCursorEnabled( bool aIsCursorEnabled ) + { + isCursorEnabled = aIsCursorEnabled; + } + + /** + * @brief Set the cursor color. + * + * @param aCursorColor is the color of the cursor. + */ + inline void SetCursorColor( COLOR4D aCursorColor ) + { + cursorColor = aCursorColor; + } + + /** + * @brief Draw the cursor. + * + * @param aCursorPosition is the cursor position in screen coordinates. + */ + virtual void DrawCursor( VECTOR2D aCursorPosition ) = 0; + + void AdvanceDepth() + { + layerDepth -= 0.1; // fixme: there should be a minimum step + } + + void PushDepth() + { + depthStack.push( layerDepth ); + } + + void PopDepth() + { + layerDepth = depthStack.top(); + depthStack.pop(); + } + +protected: + std::stack depthStack; ///< Stored depth values + VECTOR2D screenSize; ///< Screen size in screen coordinates + + double worldUnitLength; ///< The unit length of the world coordinates [inch] + double screenDPI; ///< The dots per inch of the screen + VECTOR2D lookAtPoint; ///< Point to be looked at in world space + + double zoomFactor; ///< The zoom factor + MATRIX3x3D worldScreenMatrix; ///< World transformation + double worldScale; ///< The scale factor world->screen + + double lineWidth; ///< The line width + LineCap lineCap; ///< Line end style + LineJoin lineJoin; ///< Style of the line joints + + bool isFillEnabled; ///< Is filling of graphic objects enabled ? + bool isStrokeEnabled; ///< Are the outlines stroked ? + bool isSetAttributes; ///< True, if the attributes have been set + + COLOR4D backgroundColor; ///< The background color + COLOR4D fillColor; ///< The fill color + COLOR4D strokeColor; ///< The color of the outlines + + double layerDepth; ///< The actual layer depth + VECTOR2D depthRange; ///< Range of the depth + + VECTOR2D gridSize; ///< The grid size + VECTOR2D gridOrigin; ///< The grid origin + COLOR4D gridColor; ///< Color of the grid + int gridTick; ///< Every tick line gets the double width + double gridLineWidth; ///< Line width of the grid + int gridDrawThreshold; ///< Minimum screen size of the grid (pixels) + ///< below which the grid is not drawn + int gridOriginMarkerSize; ///< Grid origin indicator size (pixels) + + bool isCursorEnabled; ///< Is the cursor enabled? + VECTOR2D cursorPosition; ///< The cursor position + COLOR4D cursorColor; ///< Cursor color + + /// Compute the scaling factor for the world->screen matrix + inline void ComputeWorldScale() + { + worldScale = screenDPI * worldUnitLength * zoomFactor; + } + + /** + * @brief Draw a grid line (usually a simplified line function). + * + * @param aStartPoint is the start point of the line. + * @param aEndPoint is the end point of the line. + */ + virtual void DrawGridLine( VECTOR2D aStartPoint, VECTOR2D aEndPoint ) = 0; +}; +} // namespace KiGfx + +#endif /* GRAPHICSABSTRACTIONLAYER_H_ */ diff --git a/include/gal/opengl/opengl_gal.h b/include/gal/opengl/opengl_gal.h new file mode 100644 index 0000000000..4606698e35 --- /dev/null +++ b/include/gal/opengl/opengl_gal.h @@ -0,0 +1,547 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Graphics Abstraction Layer (GAL) for OpenGL + * + * 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 + */ + +#ifndef OPENGLGAL_H_ +#define OPENGLGAL_H_ + +// GAL imports +#include +#include + +// wxWidgets imports +#include +#include + +// STL imports +#include +#include +#include +#include + +#include +#include +#include + + +#if defined(DEBUG) +#define D(x) x +#else +#define D(x) +#endif + +namespace KiGfx +{ +class SHADER; + +/** + * @brief Class OpenGL_GAL is the OpenGL implementation of the Graphics Abstraction Layer. + * + * This is a direct OpenGL-implementation and uses low-level graphics primitives like triangles + * and quads. The purpuse is to provide a fast graphics interface, that takes advantage of modern + * graphics card GPUs. All methods here benefit thus from the hardware acceleration. + */ +class OPENGL_GAL : public GAL, public wxGLCanvas +{ +public: + + /// Current drawing mode + enum DrawMode { + DRAW_MODE_NORMAL, ///< Normal drawing mode + DRAW_MODE_PREPARE_EDGES, ///< Prepare the object edges + DRAW_MODE_DRAW_EDGES ///< Draw anti-aliased object edges + }; + + /** + * @brief Constructor OPENGL_GAL + * + * @param aParent is the wxWidgets immediate wxWindow parent of this object. + * + * @param aMouseListener is the wxEvtHandler that should receive the mouse events, + * this can be can be any wxWindow, but is often a wxFrame container. + * + * @param aPaintListener is the wxEvtHandler that should receive the paint + * event. This can be any wxWindow, but is often a derived instance + * of this class or a containing wxFrame. The "paint event" here is + * a wxCommandEvent holding EVT_GAL_REDRAW, as sent by PostPaint(). + * + * @param aName is the name of this window for use by wxWindow::FindWindowByName() + * + * @param isUseShaders is a flag, that indicates, if shaders should be used + * for higher quality rendering. + * + */ + OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener = NULL, + wxEvtHandler* aPaintListener = NULL, bool isUseShaders = false, + const wxString& aName = wxT("GLCanvas") ); + + virtual ~OPENGL_GAL(); + + // --------------- + // Drawing methods + // --------------- + + /// @copydoc GAL::BeginDrawing() + virtual void BeginDrawing(); + + /// @copydoc GAL::EndDrawing() + virtual void EndDrawing(); + + /// @copydoc GAL::DrawLine() + virtual void DrawLine( VECTOR2D aStartPoint, VECTOR2D aEndPoint ); + + /// @copydoc GAL::DrawPolyline() + virtual void DrawPolyline( std::deque& aPointList ); + + /// @copydoc GAL::DrawCircle() + virtual void DrawCircle( VECTOR2D aCenterPoint, double aRadius ); + + /// @copydoc GAL::DrawArc() + virtual void + DrawArc( VECTOR2D aCenterPoint, double aRadius, double aStartAngle, double aEndAngle ); + + /// @copydoc GAL::DrawRectangle() + virtual void DrawRectangle( VECTOR2D aStartPoint, VECTOR2D aEndPoint ); + + /// @copydoc GAL::DrawPolygon() + virtual void DrawPolygon( const std::deque& aPointList ); + + /// @copydoc GAL::DrawCurve() + virtual void DrawCurve( VECTOR2D startPoint, VECTOR2D controlPointA, VECTOR2D controlPointB, + VECTOR2D endPoint ); + + // -------------- + // Screen methods + // -------------- + +/// @brief Resizes the canvas. + virtual void ResizeScreen ( int aWidth, int aHeight ); + + /// @brief Shows/hides the GAL canvas + virtual bool Show( bool aShow ); + + /// @copydoc GAL::Flush() + virtual void Flush(); + + /// @copydoc GAL::ClearScreen() + virtual void ClearScreen(); + + // ----------------- + // Attribute setting + // ----------------- + + /// @copydoc GAL::SetIsFill() + virtual void SetIsFill( bool aIsFillEnabled ) + { + isFillEnabled = aIsFillEnabled; + } + + /// @copydoc GAL::SetIsStroke() + virtual void SetIsStroke( bool aIsStrokeEnabled ) + { + isStrokeEnabled = aIsStrokeEnabled; + } + + /// @copydoc GAL::SetFillColor() + virtual void SetFillColor( COLOR4D aColor ); + + /// @copydoc GAL::SetStrokeColor() + virtual void SetStrokeColor( COLOR4D aColor ); + + /// @copydoc GAL::GetStrokeColor() + COLOR4D GetStrokeColor(); + + /// @copydoc GAL::SetBackgroundColor() + virtual void SetBackgroundColor( COLOR4D aColor ); + + /// @copydoc GAL::SetLineCap() + virtual void SetLineCap( LineCap aLineCap ) + { + lineCap = aLineCap; + } + + /// @copydoc GAL::SetLineJoin() + virtual void SetLineJoin( LineJoin aLineJoin ) + { + lineJoin = aLineJoin; + } + + /// @copydoc GAL::SetLineWidth() + virtual void SetLineWidth( double aLineWidth ); + + /// @copydoc GAL::GetLineWidth() + double GetLineWidth(); + + /// @copydoc GAL::SetLayerDepth() + virtual void SetLayerDepth( double aLayerDepth ){ + super::SetLayerDepth( aLayerDepth ); + } + + // -------------- + // Transformation + // -------------- + + /// @copydoc GAL::Transform() + virtual void Transform( MATRIX3x3D aTransformation ); + + /// @copydoc GAL::Rotate() + virtual void Rotate( double aAngle ); + + /// @copydoc GAL::Translate() + virtual void Translate( VECTOR2D aTranslation ); + + /// @copydoc GAL::Scale() + virtual void Scale( VECTOR2D aScale ); + + /// @copydoc GAL::Save() + virtual void Save(); + + /// @copydoc GAL::Restore() + virtual void Restore(); + + // -------------------------------------------- + // Group methods + // --------------------------------------------- + + /// @copydoc GAL::BeginGroup() + virtual int BeginGroup(); + + /// @copydoc GAL::EndGroup() + virtual void EndGroup(); + + /// @copydoc GAL::DrawGroup() + virtual void DrawGroup( int aGroupNumber ); + + /// @copydoc GAL::DeleteGroup() + virtual void DeleteGroup( int aGroupNumber ); + + // -------------------------------------------------------- + // Handling the world <-> screen transformation + // -------------------------------------------------------- + + /// @copydoc GAL::ComputeWorldScreenMatrix() + virtual void ComputeWorldScreenMatrix(); + + /// @copydoc GAL::GetWorldScreenMatrix() + MATRIX3x3D GetWorldScreenMatrix(); + + /// @copydoc GAL::SetWorldScreenMatrix() + void SetWorldScreenMatrix( MATRIX3x3D aMatrix ); + + /// @copydoc GAL::SetWorldUnitLength() + void SetWorldUnitLength( double aWorldUnitLength ); + + /// @copydoc GAL::SetScreenDPI() + void SetScreenDPI( double aScreenDPI ); + + /// @copydoc GAL::SetLookAtPoint() + void SetLookAtPoint( VECTOR2D aPoint ); + + /// @copydoc GAL::GetLookAtPoint() + VECTOR2D GetLookAtPoint(); + + /// @copydoc GAL::SetZoomFactor() + void SetZoomFactor( double aZoomFactor ); + + /// @copydoc GAL::GetZoomFactor() + double GetZoomFactor(); + + /// @copydoc GAL::SaveScreen() + virtual void SaveScreen(); + + /// @copydoc GAL::RestoreScreen() + virtual void RestoreScreen(); + + // ------- + // Cursor + // ------- + + /// @copydoc GAL::ComputeCursorToWorld() + virtual VECTOR2D ComputeCursorToWorld( VECTOR2D aCursorPosition ); + + /// @copydoc GAL::SetIsCursorEnabled() + void SetIsCursorEnabled( bool aIsCursorEnabled ); + + /// @copydoc GAL::DrawCursor() + virtual void DrawCursor( VECTOR2D aCursorPosition ); + + /** + * @brief Function PostPaint + * posts an event to m_paint_listener. A post is used so that the actual drawing + * function can use a device context type that is not specific to the wxEVT_PAINT event. + */ + void PostPaint() + { + if( paintListener ) + { + wxCommandEvent redrawEvent( EVT_GAL_REDRAW ); + wxPostEvent( paintListener, redrawEvent ); + } + } + + void SetMouseListener( wxEvtHandler* aMouseListener ) + { + mouseListener = aMouseListener; + } + + void SetPaintListener( wxEvtHandler* aPaintListener ) + { + paintListener = aPaintListener; + } + + // Special methods for OpenGL only + void SetDrawMode( DrawMode aDrawMode ) + { + m_drawMode = aDrawMode; + + switch( aDrawMode ) + { + case DRAW_MODE_NORMAL: + glColorMask( true, true, true, true ); + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LESS ); + break; + + case DRAW_MODE_PREPARE_EDGES: + // We just manipulate the Z-buffer in this mode + glColorMask( false, false, false, false ); + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LESS ); + // Shift the depth of the edge points a very small value deeper + // this way we prevent that overlapping edge points are not drawn twice + // and brighter, if we have used transparency. + glTranslated( 0, 0, (depthRange.y - depthRange.x) * DEPTH_ADJUST_FACTOR ); + break; + + case DRAW_MODE_DRAW_EDGES: + glColorMask( true, true, true, true ); + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LESS ); + // Restore the shifted position + glTranslated( 0, 0, -(depthRange.y - depthRange.x) * DEPTH_ADJUST_FACTOR ); + break; + + default: + break; + } + } + + void SetShaderPath( const std::string& aPath ) + { + shaderPath = aPath; + } + + /** + * @brief Get the current drawing mode. + * + * @return the current drawing mode. + */ + DrawMode GetDrawMode() + { + return m_drawMode; + } + +protected: + virtual void DrawGridLine( VECTOR2D aStartPoint, VECTOR2D aEndPoint ); + +private: + /// Super class definition + typedef GAL super; + + DrawMode m_drawMode; ///< Current drawing mode + + static const int CIRCLE_POINTS = 64; ///< The number of points for circle approximation + static const int CURVE_POINTS = 32; ///< The number of points for curve approximation + static const int SHADER_NUMBER = 2; ///< Number of the used shaders + static const double MITER_LIMIT = 1.5; ///< Limit for mitered edges ( * lineWidth ) + + /// This factor is used to for correct merging of antialiased edges, + /// a very small value is required + static const double DEPTH_ADJUST_FACTOR = ( 1.0 / (1 << 23) ); + + wxClientDC* clientDC; ///< Drawing context + wxGLContext* glContext; ///< OpenGL context of wxWidgets + wxWindow* parentWindow; ///< Parent window + wxEvtHandler* mouseListener; + wxEvtHandler* paintListener; + + // Display lists + GLuint displayListsArcs; ///< Arc display list + GLuint displayListCircle; ///< Circle display list + GLuint displayListSemiCircle; ///< Semi circle display list + std::deque displayListsGroup; ///< List of display lists used for groups + + double curvePoints[12]; ///< Coefficients for curves + std::deque unitCirclePoints; ///< List of the points on a unit circle + + // Polygon tesselation + GLUtesselator* tesselator; ///< Pointer to the tesselator + + // Shader + std::deque shaderList; ///< List of the shaders + + // Cursor + int cursorSize; ///< Size of the cursor in pixels + GLubyte* cursorShape; ///< Cursor pixel storage + GLubyte* cursorSave; ///< Saved cursor pixels + bool isDeleteSavedPixels; ///< Flag for deleting saved pixels + VECTOR2D savedCursorPosition; ///< Last saved cursor position + + // Frame buffer + GLuint frameBuffer; ///< Main FBO handle + GLuint depthBuffer; ///< Depth buffer handle + GLuint texture; ///< Main texture handle + GLuint frameBufferBackup; ///< Backup FBO handle + GLuint depthBufferBackup; ///< Backup depth buffer handle + GLuint textureBackup; ///< Backup texture handle + + // Internal flags + bool isCreated; + bool isGlewInitialized; ///< Is GLEW initialized? + bool isFrameBufferInitialized; ///< Are the frame buffers initialized? + bool isShaderInitialized; ///< Was the shader initialized? + bool isShaderEnabled; ///< Are the shaders enabled? + bool isUseShader; ///< Should the shaders be used? + bool isGroupStarted; ///< Was a group started? + int currentShader; ///< ID of the shader currently in use + std::string shaderPath; + + /** + * @brief Draw a semi circle (used for line caps). + * + * @param aCenterPoint is the center point. + * @param aRadius is the radius of the semi-circle. + * @param aAngle is the angle of the semi-circle. + * @param ADepthOffset is the relative depth of the semi-circle. + * + */ + void drawSemiCircle( VECTOR2D aCenterPoint, double aRadius, double aAngle, + double aDepthOffset ); + + /// Compute the points of a unit circle. + void computeUnitCircle(); + + /// Compute the points of a unit semi circle. + void computeUnitSemiCircle(); + + /// Compute the points of a unit arc. + void computeUnitArcs(); + + // Event handling + /** + * @brief This is the window creation event handler. + * + * @param aEvent is the window creation event. + */ + void onCreate( wxWindowCreateEvent& aEvent ); + + /** + * @brief This is the OnPaint event handler. + * + * @param aEvent is the OnPaint event. + */ + void onPaint( wxPaintEvent& aEvent ); + + /** + * @brief Window resizing event handler. + * + * @param aEvent is the window resizing event. + */ + void onSize( wxSizeEvent& aEvent ); + + /** + * @brief Skip the mouse event to the parent. + * + * @param aEvent is the mouse event. + */ + void skipMouseEvent( wxMouseEvent& aEvent ); + + /// Initialize GLEW. + void initGlew(); + + /** + * @brief Initialize the cursor. + * + * @param aCursorSize is the cursor size in pixels (screen coordinates). + */ + void initCursor( int aCursorSize ); + + /** + * @brief Blit the main texture to the screen. + * + * @param aIsClearFrameBuffer if true, the frame buffer is cleared as well. + * + */ + void blitMainTexture( bool aIsClearFrameBuffer ); + + /// @brief Initialize the frame buffers for main contents and backup storage. + void initFrameBuffers(); + + /** + * @brief Generate a frame buffer for the screen contents. + * + * @param aFrameBuffer is the pointer to the frame buffer handle. + * @param aDepthBuffer is the pointer to the depth buffer handle. + * @param aTexture is the pointer to the texture handle. + */ + void generateFrameBuffer( GLuint* aFrameBuffer, GLuint* aDepthBuffer, GLuint* aTexture ); + + /** + * @brief Delete the frame buffer for the screen contents. + * + * @param aFrameBuffer is the pointer to the frame buffer handle. + * @param aDepthBuffer is the pointer to the depth buffer handle. + * @param aTexture is the pointer to the texture handle. + */ + void deleteFrameBuffer( GLuint* aFrameBuffer, GLuint* aDepthBuffer, GLuint* aTexture ); + + /** + * @brief Draw a quad for the line. + * + * @param aStartPoint is the start point of the line. + * @param aEndPoint is the end point of the line. + */ + inline void drawLineQuad( VECTOR2D aStartPoint, VECTOR2D aEndPoint ); + + /** + * @brief Draw the line cap + * + * @param aStartPoint is the start point of the line. + * @param aEndPoint is the end point of the line. + * @param aDepthOffset is the relative depth of the line cap. + */ + inline void drawLineCap( VECTOR2D aStartPoint, VECTOR2D aEndPoint, double aDepthOffset ); + + inline void selectShader( int aIndex ); + + /// @copydoc GAL::DrawRoundedSegment() + void drawRoundedSegment( VECTOR2D aStartPoint, VECTOR2D aEndPoint, double aWidth, + bool aStroke = false, bool aGlBegin = false ); + + +}; +} // namespace KiGfx + +#endif // OPENGLGAL_H_ diff --git a/include/gal/opengl/shader.h b/include/gal/opengl/shader.h new file mode 100644 index 0000000000..97852c27d4 --- /dev/null +++ b/include/gal/opengl/shader.h @@ -0,0 +1,153 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Graphics Abstraction Layer (GAL) for OpenGL + * + * Shader class + * + * 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 + */ + +#ifndef SHADER_H_ +#define SHADER_H_ + +#include + +#include +#include + +namespace KiGfx +{ +class OPENGL_GAL; + +/// Type definition for the shader +enum ShaderType +{ + SHADER_TYPE_VERTEX = GL_VERTEX_SHADER, ///< Vertex shader + SHADER_TYPE_FRAGMENT = GL_FRAGMENT_SHADER, ///< Fragment shader + SHADER_TYPE_GEOMETRY = GL_GEOMETRY_SHADER ///< Geometry shader +}; + +/** + * @brief Class SHADER provides the access to the OpenGL shaders. + * + * The purpose of this class is advanced drawing with OpenGL. One example is using the pixel + * shader for drawing exact circles or for anti-aliasing. This class supports vertex, geometry + * and fragment shaders. + *
+ * Make sure that the hardware supports these features. This can be identified with the "GLEW" + * library. + */ +class SHADER +{ +public: + + /** + * @brief Constructor + */ + SHADER(); + + /** + * @brief Destructor + */ + virtual ~SHADER(); + + /** + * @brief Add a shader and compile the shader sources. + * + * @param aShaderSourceName is the shader source file name. + * @param aShaderType is the type of the shader. + */ + void AddSource( std::string aShaderSourceName, ShaderType aShaderType ); + + /** + * Link the shaders. + */ + void Link(); + + /** + * Use the shader. + */ + void Use(); + + /** + * @brief Deactivate the shader and use the default OpenGL program. + */ + void Deactivate(); + + /** + * @brief Configure the geometry shader - has to be done before linking! + * + * @param maxVertices is the maximum of vertices to be generated. + * @param geometryInputType is the input type [e.g. GL_LINES, GL_TRIANGLES, GL_QUADS etc.] + * @param geometryOutputType is the output type [e.g. GL_LINES, GL_TRIANGLES, GL_QUADS etc.] + */ + void ConfigureGeometryShader( GLuint maxVertices, GLuint geometryInputType, + GLuint geometryOutputType ); + + /** + * @brief Add a parameter to the parameter queue. + * + * To communicate with the shader use this function to set up the names for the uniform + * variables. These are queued in a list and can be assigned with the SetParameter(..) + * method using the queue position. + * + * @param aParameterName is the name of the parameter. + */ + void AddParameter( std::string aParameterName ); + + /** + * @brief Set a parameter of the shader. + * + * @param aParameterNumber is the number of the parameter. + * @param aValue is the value of the parameter. + */ + void SetParameter( int aParameterNumber, float aValue ); + +private: + + /** + * @brief Get the shader program information. + * + * @param aProgram is the program number. + */ + void ProgramInfo( GLuint aProgram ); + + /** + * @brief Read the shader source file + * + * @param aShaderSourceName is the shader source file name. + * @return the source as string + */ + std::string ReadSource( std::string aShaderSourceName ); + + std::deque shaderNumbers; ///< Shader number list + GLuint programNumber; ///< Shader program number + bool isProgramCreated; ///< Flag for program creation + bool isShaderLinked; ///< Is the shader linked? + GLuint maximumVertices; ///< The maximum of vertices to be generated + GLuint geomInputType; ///< Input type [e.g. GL_LINES, GL_TRIANGLES, GL_QUADS etc.] + GLuint geomOutputType; ///< Output type [e.g. GL_LINES, GL_TRIANGLES, GL_QUADS etc.] + std::deque parameterLocation; ///< Location of the parameter +}; +} // namespace KiGfx + +#endif /* SHADER_H_ */ diff --git a/include/gal/stroke_font.h b/include/gal/stroke_font.h new file mode 100644 index 0000000000..a94aace854 --- /dev/null +++ b/include/gal/stroke_font.h @@ -0,0 +1,185 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors. + * + * Stroke font class + * + * 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 + */ + +#ifndef STROKE_FONT_H_ +#define STROKE_FONT_H_ + +#include +#include + +#include + +#include + +namespace KiGfx +{ +class GAL; + +typedef std::deque< std::deque > Glyph; +typedef std::deque GlyphList; + +/** + * @brief Class STROKE_FONT implements stroke font drawing. + * + * A stroke font is composed of lines. + */ +class STROKE_FONT +{ +public: + /// Constructor + STROKE_FONT( GAL* aGal ); + + /// Destructor + ~STROKE_FONT(); + + // TODO Load font from a text file + + /** + * @brief Load the new stroke font. + * + * @param aNewStrokeFont is the pointer to the font data. + * @param aNewStrokeFontSize is the size of the font data. + * @return True, if the font was successfully loaded, else false. + */ + bool LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ); + + /** + * @brief Load attributes of a given text, in order to be used during drawing. + * + * @param aText is the text string. + */ + void LoadAttributes( const EDA_TEXT* aText ); + + /** + * @brief Draw a string. + * + * @param aText is the text to be drawn. + * @param aPosition is the text position in world coordinates. + * @param aRotationAngle is the text rotation angle. + */ + void Draw( std::string aText, VECTOR2D aPosition, double aRotationAngle ); + + /** + * @brief Set the scale factor of the font for the glyph size. + * + * @param aScaleFactor is the scale factor of the font. + */ + inline void SetScaleFactor( const double aScaleFactor ) + { + m_scaleFactor = aScaleFactor; + } + + /** + * @brief Set the glyph size. + * + * @param aGlyphSize is the glyph size. + */ + inline void SetGlyphSize( const VECTOR2D aGlyphSize ) + { + m_glyphSize = aGlyphSize; + } + + /** + * @brief Set a bold property of current font. + * + * @param aBold tells if the font should be bold or not. + */ + inline void SetBold( const bool aBold ) + { + m_bold = aBold; + } + + /** + * @brief Set an italic property of current font. + * + * @param aItalic tells if the font should be italic or not. + */ + inline void SetItalic( const bool aItalic ) + { + m_italic = aItalic; + } + + /** + * @brief Set a mirrored property of text. + * + * @param aMirrored tells if the text should be mirrored or not. + */ + inline void SetMirrored( const bool aMirrored ) + { + m_mirrored = aMirrored; + } + + /** + * @brief Set the horizontal justify for text drawing. + * + * @param aHorizontalJustify is the horizontal justify value. + */ + inline void SetHorizontalJustify( const EDA_TEXT_HJUSTIFY_T aHorizontalJustify ) + { + m_horizontalJustify = aHorizontalJustify; + } + + /** + * @brief Set the vertical justify for text drawing. + * + * @param aVerticalJustify is the vertical justify value. + */ + inline void SetVerticalJustify( const EDA_TEXT_VJUSTIFY_T aVerticalJustify ) + { + m_verticalJustify = aVerticalJustify; + } + +private: + GAL* m_gal; ///< Pointer to the GAL + + GlyphList m_glyphs; ///< Glyph list + std::deque m_glyphBoundingBoxes; ///< Bounding boxes of the glyphs + double m_scaleFactor; ///< Scale factor for the glyph + VECTOR2D m_glyphSize; ///< Size of the glyphs + EDA_TEXT_HJUSTIFY_T m_horizontalJustify; ///< Horizontal justification + EDA_TEXT_VJUSTIFY_T m_verticalJustify; ///< Vertical justification + bool m_bold, m_italic, m_mirrored; ///< Properties of text + + /** + * @brief Compute the bounding box of a given glyph. + * + * @param aGlyph is the glyph. + * @param aGlyphBoundingX is the x-component of the bounding box size. + * @return is the complete bounding box size. + */ + BOX2D computeBoundingBox( Glyph aGlyph, VECTOR2D aGlyphBoundingX ); + + /** + * @brief Compute the size of a given text. + * + * @param aText is the text string. + * @return is the text size. + */ + VECTOR2D computeTextSize( const std::string aText ); +}; +} // namespace KiGfx + +#endif /* STROKE_FONT_H_ */ diff --git a/include/id.h b/include/id.h index 4d517474af..eb9eec91b0 100644 --- a/include/id.h +++ b/include/id.h @@ -213,6 +213,7 @@ enum main_id ID_ZOOM_OUT, ID_ZOOM_PAGE, ID_ZOOM_REDRAW, + ID_SWITCH_CANVAS, /* Panning command event IDs. */ ID_PAN_UP, diff --git a/include/layers_id_colors_and_visibility.h b/include/layers_id_colors_and_visibility.h index 6b7fd7ef38..77f2e87f0c 100644 --- a/include/layers_id_colors_and_visibility.h +++ b/include/layers_id_colors_and_visibility.h @@ -143,9 +143,19 @@ enum PCB_VISIBLE MOD_VALUES_VISIBLE, ///< show modules values (when texts are visibles) MOD_REFERENCES_VISIBLE, ///< show modules references (when texts are visibles) + TRACKS_VISIBLE, + PADS_VISIBLE, + VIA_HOLES_VISIBLE, + PAD_HOLES_VISIBLE, + END_PCB_VISIBLE_LIST // sentinel }; +/// macro for obtaining layer number for specific item (eg. pad or text) +#define ITEM_GAL_LAYER(layer) (LAYER_COUNT + layer) + +/// number of *all* layers including PCB and item layers +#define TOTAL_LAYER_COUNT (LAYER_COUNT + END_PCB_VISIBLE_LIST) /** * Function IsValidLayerIndex diff --git a/include/painter.h b/include/painter.h new file mode 100644 index 0000000000..2c4412408f --- /dev/null +++ b/include/painter.h @@ -0,0 +1,216 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * @author Maciej Suminski + * + * 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 + */ + +#ifndef __CLASS_PAINTER_H +#define __CLASS_PAINTER_H + +#include +#include + +#include +#include + + +class EDA_ITEM; +class COLORS_DESIGN_SETTINGS; + +namespace KiGfx +{ +class GAL; +class STROKE_FONT; + +/** + * Class RENDER_SETTINGS + * Contains all the knowledge about how graphical objects are drawn on + * any output surface/device. This includes: + * - color/transparency settings + * - highlighting and high contrast mode control + * - drawing quality control (sketch/outline mode) + * The class acts as an interface between the PAINTER object and the GUI (i.e. Layers/Items + * widget or display options dialog). + * + * Todo: properties/introspection + */ +class RENDER_SETTINGS +{ +public: + + RENDER_SETTINGS(); + virtual ~RENDER_SETTINGS(); + + /** + * Function Update + * Precalculates extra colors for layers (eg. highlighted, darkened and any needed version + * of base colors). + */ + virtual void Update(); + + /** + * Function ImportLegacyColors + * Loads a list of color settings for layers. + * @param aSettings is a list of color settings. + */ + virtual void ImportLegacyColors( COLORS_DESIGN_SETTINGS* aSettings ) = 0; + + /** + * Function SetActiveLayer + * Sets the specified layer as active - it means that it can be drawn in a specific mode + * (eg. highlighted, so it differs from other layers). + * @param aLayerId is a layer number that should be displayed in a specific mode. + */ + void SetActiveLayer( int aLayerId ) + { + m_activeLayer = aLayerId; + } + + /** + * Function SetHighlight + * Turns on/off highlighting - it may be done for the active layer or the specified net. + * @param aEnabled tells if highlighting should be enabled. + * @param aNetCode is optional and if specified, turns on higlighting only for the net with + * number given as the parameter. + */ + void SetHighlight( bool aEnabled, int aNetcode = -1 ) + { + m_highlightEnabled = aEnabled; + + if( aNetcode > 0 ) + m_highlightNetcode = aNetcode; + } + + /** + * Function SetHighContrast + * Turns on/off high contrast display mode. + * @param aEnabled determines if high contrast display mode should be enabled or not. + */ + void SetHighContrast( bool aEnabled ) + { + m_hiContrastEnabled = aEnabled; + } + +protected: + + int m_activeLayer; /// Stores active layer number + + /// Parameters for display modes + bool m_hiContrastEnabled; /// High contrast display mode on/off + COLOR4D m_hiContrastColor; /// Color used for high contrast display mode + float m_hiContrastFactor; /// Factor used for computing high contrast color + + bool m_highlightEnabled; /// Highlight display mode on/off + int m_highlightNetcode; /// Net number that is displayed in highlight + /// -1 means that there is no specific net, and whole active + /// layer is highlighted + float m_highlightFactor; /// Factor used for computing hightlight color + + COLOR4D m_selectionBorderColor; /// Color of selection box border + COLOR4D m_netLabelColor; /// Color of net labels + + float m_selectFactor; /// Specifies how color of selected items is changed + float m_layerOpacity; /// Determines opacity of all layers, so every can be seen + /// at the same time + + /// Map of colors that were usually used for display + std::map m_legacyColorMap; +}; + + +/** + * Class PAINTER + * contains all the knowledge about how to draw graphical object onto + * any particular output device. + * This knowledge is held outside the individual graphical objects so that + * alternative output devices may be used, and so that the graphical objects + * themselves to not contain drawing routines. Drawing routines in the objects + * cause problems with usages of the objects as simple container objects in + * DLL/DSOs. + * PAINTER is an abstract layer, because every module (pcbnew, eeschema, etc.) + * has to draw different kinds of objects. + */ +class PAINTER +{ +public: + + /* + * Constructor PAINTER( GAL* ) + * initializes this object for painting on any of the polymorphic + * GRAPHICS_ABSTRACTION_LAYER* derivatives. + * + * @param aGal is a pointer to a polymorphic GAL device on which + * to draw (i.e. Cairo, OpenGL, wxDC) + * No ownership is given to this PAINTER of aGal. + */ + PAINTER( GAL* aGal ); + virtual ~PAINTER(); + + /** + * Function ApplySettings + * Loads colors and display modes settings that are going to be used when drawing items. + * @param aSettings are settings to be applied. + */ + virtual void ApplySettings( RENDER_SETTINGS* aSettings ) + { + if( m_settings ) + delete m_settings; + + m_settings = aSettings; + } + + /** + * Function Draw + * Takes an instance of EDA_ITEM and passes it to a function that know how to draw the item. + * @param aItem is an item to be drawn. + * @param aLayer tells which layer is currently rendered so that draw functions + * may know what to draw (eg. for pads there are separate layers for holes, because they + * have other dimensions then the pad itself. + */ + virtual bool Draw( const EDA_ITEM* aItem, int aLayer ) = 0; + +protected: + + /** + * Function getLayerColor + * is used for obtaining color that should be used for specific layer/net + * combination using stored color settings. + * @param aLayer is the layer number that is being drawn. + * @param aNetCode is a number of the net that is being drawn. + */ + virtual const COLOR4D& getLayerColor( int aLayer, int aNetCode ) const = 0; + + /// Instance of graphic abstraction layer that gives an interface to call + /// commands used to draw (eg. DrawLine, DrawCircle, etc.) + GAL* m_gal; + + /// Instance of object that stores information about how to draw texts (including different + /// font display modes [bold/italic/mirror]). + STROKE_FONT* m_stroke_font; + + /// Colors and display modes settings that are going to be used when drawing items. + RENDER_SETTINGS* m_settings; +}; +} // namespace KiGfx + +#endif /* __CLASS_PAINTER_H */ diff --git a/include/view/view.h b/include/view/view.h new file mode 100644 index 0000000000..ef89d8ee6a --- /dev/null +++ b/include/view/view.h @@ -0,0 +1,361 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * 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 + */ + +#ifndef __VIEW_H +#define __VIEW_H + +#include +#include + +#include + +namespace KiGfx +{ +class PAINTER; +class GAL; +class VIEW_ITEM; +class VIEW_RTREE; + +/** + * Class VIEW. + * Holds a (potentially large) number of VIEW_ITEMs and renders them on a graphics device + * provided by the GAL. VIEWs can exist in two flavors: + * - dynamic - where items can be added, removed or changed anytime, intended for the main + * editing panel. Each VIEW_ITEM can be added to a single dynamic view. + * - static - where items are added once at the startup and are not linked with the VIEW. + * Foreseen for preview windows and printing. + * Items in a view are grouped in layers (not to be confused with Kicad's PCB layers). Each layer is + * identified by an integer number. Visibility and rendering order can be set individually for each + * of the layers. Future versions of the VIEW will also allow to assign different layers to different + * rendering targets, which will be composited at the final stage by the GAL. + * The VIEW class also provides fast methods for finding all visible objects that are within a given + * rectangular area, useful for object selection/hit testing. + */ +class VIEW +{ +public: + friend class VIEW_ITEM; + + typedef std::pair LayerItemPair; + + /** + * Constructor. + * @param aIsDynamic: decides whether we are creating a static or a dynamic VIEW. + * @param aUseGroups: fixme + */ + VIEW( bool aIsDynamic = true, bool aUseGroups = false ); + + ~VIEW(); + + /** + * Function Add() + * Adds a VIEW_ITEM to the view. + * @param aItem: item to be added. No ownership is given + */ + void Add( VIEW_ITEM* aItem ); + + /** + * Function Remove() + * Removes a VIEW_ITEM from the view. + * @param aItem: item to be removed. Caller must dispose the removed item if necessary + */ + void Remove( VIEW_ITEM* aItem ); + + /** Function Query() + * Finds all visible items that touch or are within the rectangle aRect. + * @param aRect area to search for items + * @param aResult result of the search, containing VIEW_ITEMs associated with their layers. + * Sorted according to the rendering order (items that are on top of the rendering stack as first). + * @return Number of found items. + */ + int Query( const BOX2I& aRect, std::vector& aResult ); + + /** + * Function CopySettings() + * Copies layers and visibility settings from another view. + * @param aOtherView: view from which settings will be copied. + */ + void CopySettings( const VIEW* aOtherView ); + + /* + * Convenience wrappers for adding multiple items + * template void AddItems( const T& aItems ); + * template void RemoveItems( const T& aItems ); + */ + + /** + * Function SetGAL() + * Assigns a rendering device for the VIEW. + * @param aGal: pointer to the GAL output device + */ + void SetGAL( GAL* aGal ); + + /** + * Function GetGAL() + * Returns the GAL this view is using to draw graphical primitives. + * @return Pointer to the currently used GAL instance. + */ + GAL* GetGAL() const { return m_gal; } + + /** + * Function SetPainter() + * Sets the painter object used by the view for drawing VIEW_ITEMS. + */ + void SetPainter( PAINTER* aPainter ); + + /** + * Function GetPainter() + * Returns the painter object used by the view for drawing VIEW_ITEMS. + * @return Pointer to the currently used Painter instance. + */ + PAINTER* GetPainter() const { return m_painter; }; + + /** + * Function SetViewport() + * Sets the visible area of the VIEW. + * @param aViewport: desired visible area, in world space coordinates. + * @param aKeepProportions: when true, the X/Y size proportions are kept. + */ + void SetViewport( const BOX2D& aViewport, bool aKeepProportions = true ); + + /** + * Function GetViewport() + * Returns the current viewport visible area rectangle. + * @return Current viewport rectangle + */ + BOX2D GetViewport() const; + + /** + * Function SetMirror() + * Controls the mirroring of the VIEW. + * @param aMirrorX: when true, the X axis is mirrored + * @param aMirrorY: when true, the Y axis is mirrored. + */ + void SetMirror( bool aMirrorX, bool aMirrorY ); + + /** + * Function SetScale() + * Sets the scaling factor. Scale = 1 corresponds to the real world size of the objects + * (depending on correct GAL unit length & DPI settings). + * @param aScale: the scalefactor + */ + void SetScale( double aScale ); + + /** + * Function SetScale() + * Sets the scaling factor, zooming around a given anchor point. + * (depending on correct GAL unit length & DPI settings). + * @param aScale: the scale factor + */ + void SetScale( double aScale, const VECTOR2D& aAnchor ); + + /** + * Function GetScale() + * @return Current scalefactor of this VIEW + */ + double GetScale() const { return m_scale; } + + /** + * Function SetCenter() + * Sets the center point of the VIEW (i.e. the point in world space that will be drawn in the middle + * of the screen). + * @param aCenter: the new center point, in world space coordinates. + */ + void SetCenter( const VECTOR2D& aCenter ); + + /** + * Function GetCenter() + * Returns the center point of this VIEW (in world space coordinates) + * @return center point of the view + */ + const VECTOR2D& GetCenter() const { return m_center; } + + /** + * Function ToWorld() + * Converts a screen space point/vector to a point/vector in world space coordinates. + * @param aCoord: the point/vector to be converted + * @param aAbsolute: when true, aCoord is treated as a point, otherwise - as a direction (vector) + */ + VECTOR2D ToWorld( const VECTOR2D& aCoord, bool aAbsolute = true ) const; + + /** + * Function ToScreen() + * Converts a world space point/vector to a point/vector in screen space coordinates. + * @param aCoord: the point/vector to be converted + * @param aAbsolute: when true, aCoord is treated as a point, otherwise - as a direction (vector) + */ + VECTOR2D ToScreen( const VECTOR2D& aCoord, bool aAbsolute = true ) const; + + /** + * Function ToScreen() + * Converts a world space coordinate to a coordinate in screen space coordinates. + * @param aCoord: the coordinate to be transformed. + * @param aAbsolute: when true, aCoord is treated as a point, otherwise - as a direction (vector) + */ + double ToScreen( double aCoord, bool aAbsolute = true ) const; + + + /** + * Function GetScreenPixelSize() + * Returns the size of the our rendering area, in pixels. + * @return viewport screen size + */ + VECTOR2D GetScreenPixelSize() const; + + /** + * Function AddLayer() + * Adds a new layer to the view. + * @param aLayer: unique ID of the layer to be added. + * @param aDisplayOnly: layer is display-only (example: selection boxes, floating hints/menus). + * Objects belonging to this layer are not taken into account by Query() method. + */ + void AddLayer( int aLayer, bool aDisplayOnly = false ); + + + /** + * Function ClearLayer() + * Removes all items from a given layer. + * @param aLayer: ID of the layer to be cleared + */ + void ClearLayer( int aLayer ); + + /** + * Function Clear() + * Removes all items from the view. + */ + void Clear(); + + /** + * Function SetLayerVisible() + * Controls the visibility of a particular layer. + * @param aLayer: the layer to show/hide. When ALL_LAYERS constant is given, all layers' + * visibility is updated + * @param aVisible: the obivous + */ + void SetLayerVisible( int aLayer, bool aVisible = true ); + + /** + * Function SetLayerOrder() + * Sets rendering order of a particular layer. + * @param aLayer: the layer + * @param aRenderingOrder: arbitrary number denoting the rendering order. + * Lower values are rendered first. + */ + void SetLayerOrder( int aLayer, int aRenderingOrder ); + + /** + * Function Redraw() + * Immediately redraws the whole view. + */ + void Redraw(); + + /** + * Function PartialRedraw() + * Redraws only the parts of the view that have been affected by items + * for which ViewUpdate() function has been called since last redraw. + */ + void PartialRedraw(); + + /** + * Function IsDynamic() + * Tells if the VIEW is dynamic (ie. can be changed, for example displaying PCBs in a window) + * or static (that cannot be modified, eg. displaying image/PDF). + */ + bool IsDynamic() const { return m_dynamic; } + + static const unsigned int VIEW_MAX_LAYERS; ///* maximum number of layers that may be shown + +private: + + struct VIEW_LAYER + { + bool enabled; ///* is the layer to be rendered? + bool isDirty; ///* does it contain any dirty items (updated since last redraw) + bool displayOnly; ///* is the layer display only? + VIEW_RTREE* items; ///* R-tree indexing all items on this layer. + std::vector dirtyItems; ///* set of dirty items collected since last redraw + int renderingOrder; ///* rendering order of this layer + int id; ///* layer ID + BOX2I extents; ///* sum of bboxes of all items on the layer + BOX2I dirtyExtents; ///* sum of bboxes of all dirty items on the layer + }; + + // Convenience typedefs + typedef boost::unordered_map LayerMap; + typedef LayerMap::iterator LayerMapIter; + typedef std::vector LayerOrder; + typedef std::vector::iterator LayerOrderIter; + + // Function objects that need to access VIEW/VIEW_ITEM private/protected members + struct clearItemCache; + struct unlinkItem; + struct drawItem; + + ///* Redraws contents within rect aRect + void redrawRect( const BOX2I& aRect ); + + ///* Manages dirty flags & redraw queueing when updating an item. Called internally + /// via VIEW_ITEM::ViewUpdate() + void invalidateItem( VIEW_ITEM* aItem, int aUpdateFlags ); + + ///* Sorts m_orderedLayers when layer rendering order has changed + void sortLayers(); + + ///* Clears cached GAL display lists + void clearGroupCache(); + + /// Determines rendering order of layers. Used in display order sorting function. + static bool compareRenderingOrder( VIEW_LAYER* i, VIEW_LAYER* j ) + { + return i->renderingOrder > j->renderingOrder; + }; + + /// Contains set of possible displayed layers and its properties + LayerMap m_layers; + + /// Sorted list of pointers to members of m_layers. + LayerOrder m_orderedLayers; + + /// Center point of the VIEW (the point at which we are looking at) + VECTOR2D m_center; + + /// Scale of displayed VIEW_ITEMs + double m_scale; + + /// PAINTER contains information how do draw items + PAINTER* m_painter; + + /// Gives interface to PAINTER, that is used to draw items + GAL* m_gal; + + /// Dynamic VIEW (eg. display PCB in window) allows changes once it is built, + /// static (eg. image/PDF) - does not. + bool m_dynamic; + + /// Determines whether to use cached groups of objects for displaying. + bool m_useGroups; +}; +} // namespace KiGfx + +#endif diff --git a/include/view/view_controls.h b/include/view/view_controls.h new file mode 100644 index 0000000000..695530d73e --- /dev/null +++ b/include/view/view_controls.h @@ -0,0 +1,117 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * 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 view_controls.h + * @brief VIEW_CONTROLS class definition. + */ + +#ifndef __VIEW_CONTROLS_H +#define __VIEW_CONTROLS_H + +#include + +namespace KiGfx +{ +class VIEW; + +/** + * Class VIEW_CONTROLS + * is an interface for classes handling user events controlling the view behaviour + * (such as zooming, panning, mouse grab, etc.) + */ +class VIEW_CONTROLS +{ +public: + /** + * Possible modes for panning (JUMP means that view is updated less often, resulting in + * not so responsive user interface). + */ + enum PanMode { + SMOOTH = 1, + JUMP = 2 + }; + + VIEW_CONTROLS( VIEW* aView ) : m_view( aView ) {}; + virtual ~VIEW_CONTROLS() {}; + + /** + * Function Activate + * Determines if all view related events (mouse wheel, right click panning, etc.), should be + * handled or not. If not - they can be processed by the legacy view. + * @param aEnabled tells if events should be handled. + */ + virtual void Activate( bool aEnabled ) {}; + + /** + * Function SetGrabMouse + * Turns on/off mouse grabbing. When the mouse is grabbed, it cannot go outside the VIEW. + * @param aEnabled tells if mouse should be grabbed or not. + */ + virtual void SetGrabMouse( bool aEnabled ) {}; + + /** + * Function SetGrabMouse + * Turns on/off auto panning (this feature is used when there is a tool active (eg. drawing a + * track) and user moves mouse to the VIEW edge - then the view can be translated or not). + * @param aEnabled tells if the autopanning should be active. + */ + virtual void SetAutoPan( bool aEnabled ) {} + + /** + * Function SetPanSpeed + * Sets speed of panning. + * @param aSpeed is a new speed for panning. + */ + virtual void SetPanSpeed( float aSpeed ) {}; + + /** + * Function SetPanMode + * Enables specified mode for panning. + * @param aMode is a new mode used for VIEW panning. + */ + virtual void SetPanMode( PanMode aMode ) {}; + + /** + * Function SetZoomSpeed + * Determines how much zoom factor should be affected on one zoom event (eg. mouse wheel). + * @param aSpeed is a new zooming speed. + */ + virtual void SetZoomSpeed( float aSpeed ) {}; + + /** + * Function AnimatedZoom + * // TODO + */ + virtual void AnimatedZoom( const BOX2I& aExtents ) {}; + +protected: + /// Pointer to controlled VIEW. + VIEW* m_view; +}; +} // namespace KiGfx + +#endif diff --git a/include/view/view_item.h b/include/view/view_item.h new file mode 100644 index 0000000000..36643c0a92 --- /dev/null +++ b/include/view/view_item.h @@ -0,0 +1,180 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * 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 view_item.h + * @brief VIEW_ITEM class definition. + */ + +#ifndef __VIEW_ITEM_H +#define __VIEW_ITEM_H + +#include + +namespace KiGfx +{ +// Forward declarations +class GAL; +class PAINTER; +class VIEW; + +/** + * Class VIEW_ITEM - + * is an abstract base class for deriving all objects that can be added to a VIEW. + * It's role is to: + * - communicte geometry, appearance and visibility updates to the associated dynamic VIEW, + * - provide a bounding box for redraw area calculation, + * - (optional) draw the object using the GAL API functions for PAINTER-less implementations. + * VIEW_ITEM objects are never owned by a VIEW. A single VIEW_ITEM can belong to any number of + * static VIEWs, but only one dynamic VIEW due to storage of only one VIEW reference. + */ +class VIEW_ITEM +{ +public: + + /** + * Enum ViewUpdateFlags. + * Defines the how severely the shape/appearance of the item has been changed: + * - APPEARANCE: shape or layer set of the item have not been affected, + * only colors or visibility. + * - GEOMETRY: shape or layer set of the item have changed, VIEW may need to reindex it. + * - ALL: all flags above */ + + enum ViewUpdateFlags { + APPEARANCE = 0x1, + GEOMETRY = 0x2, + ALL = 0xff + }; + + VIEW_ITEM() : m_view( NULL ), m_viewVisible( true ), m_cachedGroup( -1 ) {} + + /** + * Destructor. For dynamic views, removes the item from the view. + */ + virtual ~VIEW_ITEM() + { + ViewRelease(); + }; + + /** + * Function ViewBBox() + * returns the bounding box of the item covering all its layers. + * @return BOX2I - the current bounding box + */ + virtual const BOX2I ViewBBox() const = 0; + + /** + * Function ViewDraw() + * Draws the parts of the object belonging to layer aLayer. + * viewDraw() is an alternative way for drawing objects if + * if there is no PAINTER assigned for the view or if the PAINTER + * doesn't know how to paint this particular implementation of + * VIEW_ITEM. The preferred way of drawing is to design an + * appropriate PAINTER object, the method below is intended only + * for quick hacks and debugging purposes. + * + * @param aLayer: current drawing layer + * @param aGal: pointer to the GAL device we are drawing on + * @param aVisibleArea: area (in world space coordinates) that is relevant for drawing. For + * example, when drawing a bitmap, one can clip the blitting area to aVisibleArea, reducing + * drawing time. + */ + virtual void ViewDraw( int aLayer, GAL* aGal, const BOX2I& aVisibleArea ) const { }; + + /** + * Function ViewGetLayers() + * Returns the all the layers within the VIEW the object is painted on. For instance, a D_PAD + * spans one or more copper layers and a few technical layers. ViewDraw() or PAINTER::Draw() is + * repeatedly called for each of the layers returned by ViewGetLayers(), depending on the + * rendering order. + * @param aLayers[]: output layer index array + * @param aCount: number of layer indices in aLayers[] + */ + + virtual void ViewGetLayers( int aLayers[], int& aCount ) const = 0; + + /** + * Function ViewSetVisible() + * Sets the item visibility. + * + * @param aIsVisible: whether the item is visible (on all layers), or not. + */ + void ViewSetVisible( bool aIsVisible = true ); + + + /** + * Function ViewIsVisible() + * Returns if the item is visible (or not). + * + * @return when true, the item is visible (i.e. to be displayed, not visible in the + * *current* viewport) + */ + bool ViewIsVisible() const + { + return m_viewVisible; + } + + /** + * Function ViewUpdate() + * For dynamic VIEWs, informs the associated VIEW that the graphical representation of + * this item has changed. For static views calling has no effect. + * + * @param aUpdateFlags: how much the object has changed + * @param aForceImmediateRedraw: when true, the VIEW is redrawn immediately, + * otherwise, it will be redrawn upon next call of VIEW::Update() + */ + virtual void ViewUpdate( int aUpdateFlags = ALL, bool aForceImmediateRedraw = false ); + + /** + * Function ViewRelease() + * Releases the item from an associated dynamic VIEW. For static views calling has no effect. + */ + void ViewRelease(); + +protected: + friend class VIEW; + + /** + * Function viewAssign() + * Assigns the item to a given dynamic VIEW. Called internally by the VIEW. + * + * @param aView[]: dynamic VIEW instance the item is being added to. + */ + + void viewAssign( VIEW* aView ) + { + // release the item from a previously assigned dynamic view (if there is any) + ViewRelease(); + m_view = aView; + m_cachedGroup = -1; + } + + VIEW* m_view; ///* Current dynamic view the item is assigned to. + bool m_viewVisible; ///* Are we visible in the current dynamic VIEW. +private: + int m_cachedGroup; ///* Index of cached GAL display list corresponding to the item +}; +} // namespace KiGfx + +#endif diff --git a/include/view/wx_view_controls.h b/include/view/wx_view_controls.h new file mode 100644 index 0000000000..0848f0c10b --- /dev/null +++ b/include/view/wx_view_controls.h @@ -0,0 +1,85 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Torsten Hueter, torstenhtr gmx.de + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * + * 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 wx_view_controls.h + * @brief WX_VIEW_CONTROLS class definition. + */ + +#ifndef __WX_VIEW_CONTROLS_H +#define __WX_VIEW_CONTROLS_H + +#include +#include + +#include + +class EDA_DRAW_PANEL_GAL; + +namespace KiGfx +{ +/** + * Class WX_VIEW_CONTROLS + * is a specific implementation of class VIEW_CONTROLS for wxWidgets library. + */ +class WX_VIEW_CONTROLS : public VIEW_CONTROLS, public wxEvtHandler +{ +public: + + WX_VIEW_CONTROLS( VIEW* aView, wxWindow* aParentPanel ); + ~WX_VIEW_CONTROLS() {}; + + void onWheel( wxMouseEvent& event ); + void onMotion( wxMouseEvent& event ); + void onButton( wxMouseEvent& event ); + +private: + + /// Options for WX_VIEW_CONTROLS + bool m_isDragPanning; + bool m_isAutoPanning; + bool m_autoPanEnabled; + bool m_needRedraw; + + /// Distance from cursor to VIEW edge when panning is active. + double m_autoPanMargin; + /// How fast is panning when in auto mode. + double m_autoPanSpeed; + /// TODO + double m_autoPanCornerRatio; + + /// Panel that is affected by VIEW_CONTROLS + wxWindow* m_parentPanel; + + /// Stores information about point where event started. + VECTOR2D m_dragStartPoint; + VECTOR2D m_lookStartPoint; + + /// Used for determining time intervals between events. + wxLongLong m_timeStamp; +}; +} // namespace KiGfx + +#endif diff --git a/include/wxstruct.h b/include/wxstruct.h index a359981191..20407a702a 100644 --- a/include/wxstruct.h +++ b/include/wxstruct.h @@ -68,6 +68,7 @@ class EDA_ITEM; class EDA_RECT; class EDA_DRAW_PANEL; +class EDA_DRAW_PANEL_GAL; class EDA_MSG_PANEL; class BASE_SCREEN; class PARAM_CFG_BASE; @@ -391,11 +392,15 @@ protected: EDA_HOTKEY_CONFIG* m_HotkeysZoomAndGridList; int m_LastGridSizeId; bool m_DrawGrid; // hide/Show grid + bool m_galCanvasActive; // whether to use new GAL engine EDA_COLOR_T m_GridColor; // Grid color /// The area to draw on. EDA_DRAW_PANEL* m_canvas; + /// New type of area (GAL-based) to draw on. + EDA_DRAW_PANEL_GAL* m_galCanvas; + /// Tool ID of previously active draw tool bar button. int m_lastDrawToolId; @@ -904,6 +909,30 @@ public: */ wxString LengthDoubleToString( double aValue, bool aConvertToMils = false ) const; + /** + * Function UseGalCanvas + * used to switch between standard and GAL-based canvas. + * + * @param aEnable True for GAL-based canvas, false for standard canvas. + */ + void UseGalCanvas( bool aEnable ); + + /** + * Function IsNewCanvasActive + * is used to check which canvas (GAL-based or standard) is currently in use. + * + * @return True for GAL-based canvas, false for standard canvas. + */ + bool IsGalCanvasActive() { return m_galCanvasActive; } + + /** + * Function GetCalCanvas + * returns a pointer to GAL-based canvas of given EDA draw frame. + * + * @return Pointer to GAL-based canvas. + */ + EDA_DRAW_PANEL_GAL* GetGalCanvas() { return m_galCanvas; } + DECLARE_EVENT_TABLE() }; diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index ba68add0c8..d65bd84a56 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -426,6 +426,13 @@ target_link_libraries(pcbnew ${PCBNEW_EXTRA_LIBS} ) +if(KICAD_GAL) +target_link_libraries(pcbnew + gal + ${GLEW_LIBRARIES} + ) +endif(KICAD_GAL) + ### # Add pcbnew as install target ### diff --git a/pcbnew/basepcbframe.cpp b/pcbnew/basepcbframe.cpp index b68f1cc42a..2cd72ee990 100644 --- a/pcbnew/basepcbframe.cpp +++ b/pcbnew/basepcbframe.cpp @@ -42,10 +42,18 @@ #include #include #include +#include +#include +#include #include #include +#include +#include #include +#ifdef KICAD_GAL +#include +#endif // Configuration entry names. @@ -77,6 +85,27 @@ BEGIN_EVENT_TABLE( PCB_BASE_FRAME, EDA_DRAW_FRAME ) END_EVENT_TABLE() +/// Rendering order of layers on GAL-based canvas (lower index in the array +/// means that layer is displayed closer to the user, ie. on the top). +const int m_galLayerOrder[] = +{ + DRAW_N, COMMENT_N, ECO1_N, ECO2_N, EDGE_N, + UNUSED_LAYER_29, UNUSED_LAYER_30, UNUSED_LAYER_31, + ITEM_GAL_LAYER( MOD_TEXT_FR_VISIBLE ), + SILKSCREEN_N_FRONT, SOLDERPASTE_N_FRONT, ADHESIVE_N_FRONT, SOLDERMASK_N_FRONT, + + ITEM_GAL_LAYER( VIA_HOLES_VISIBLE ), ITEM_GAL_LAYER( PAD_HOLES_VISIBLE ), + ITEM_GAL_LAYER( VIAS_VISIBLE ), ITEM_GAL_LAYER( PADS_VISIBLE ), + + LAYER_N_FRONT, LAYER_N_15, LAYER_N_14, LAYER_N_13, LAYER_N_12, LAYER_N_11, + LAYER_N_10, LAYER_N_9, LAYER_N_8, LAYER_N_7, LAYER_N_6, LAYER_N_5, LAYER_N_4, + LAYER_N_3, LAYER_N_2, LAYER_N_BACK, + + SOLDERMASK_N_BACK, ADHESIVE_N_BACK, SOLDERPASTE_N_BACK, SILKSCREEN_N_BACK, + ITEM_GAL_LAYER( MOD_TEXT_BK_VISIBLE ) +}; + + PCB_BASE_FRAME::PCB_BASE_FRAME( wxWindow* aParent, ID_DRAWFRAME_TYPE aFrameType, const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize, @@ -101,6 +130,12 @@ PCB_BASE_FRAME::PCB_BASE_FRAME( wxWindow* aParent, ID_DRAWFRAME_TYPE aFrameType, m_FastGrid1 = 0; m_FastGrid2 = 0; +#ifdef KICAD_GAL + m_galCanvas = new EDA_DRAW_PANEL_GAL( this, -1, wxPoint( 0, 0 ), m_FrameSize, + EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL ); + m_galCanvas->Hide(); +#endif /* KICAD_GAL */ + m_auxiliaryToolBar = NULL; } @@ -110,6 +145,9 @@ PCB_BASE_FRAME::~PCB_BASE_FRAME() delete m_Collector; delete m_Pcb; // is already NULL for FOOTPRINT_EDIT_FRAME +#ifdef KICAD_GAL + delete m_galCanvas; +#endif /* KICAD_GAL */ } @@ -117,6 +155,78 @@ void PCB_BASE_FRAME::SetBoard( BOARD* aBoard ) { delete m_Pcb; m_Pcb = aBoard; + +#ifdef KICAD_GAL + if( m_galCanvas ) + { + KiGfx::VIEW* view = m_galCanvas->GetView(); + view->Clear(); + + // All of PCB drawing elements should be added to the VIEW + // in order to be displayed + + // Load zones + for( int i = 0; i < m_Pcb->GetAreaCount(); ++i ) + { + view->Add( (KiGfx::VIEW_ITEM*) ( m_Pcb->GetArea( i ) ) ); + } + + // Load drawings + for( BOARD_ITEM* drawing = m_Pcb->m_Drawings; drawing; drawing = drawing->Next() ) + { + view->Add( drawing ); + } + + // Load tracks + for( TRACK* track = m_Pcb->m_Track; track; track = track->Next() ) + { + view->Add( track ); + } + + // Load modules and its additional elements + for( MODULE* module = m_Pcb->m_Modules; module; module = module->Next() ) + { + // Load module's pads + for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() ) + { + view->Add( pad ); + } + + // Load module's drawing (mostly silkscreen) + for( BOARD_ITEM* drawing = module->GraphicalItems().GetFirst(); drawing; + drawing = drawing->Next() ) + { + view->Add( drawing ); + } + + // Load module's texts (name and value) + view->Add( &module->Reference() ); + view->Add( &module->Value() ); + } + + // Segzones (equivalent of ZONE_CONTAINER for legacy boards) + for( SEGZONE* zone = m_Pcb->m_Zone; zone; zone = zone->Next() ) + { + view->Add( zone ); + } + + // Apply layer coloring scheme + if( view->GetPainter() ) + { + KiGfx::PCB_RENDER_SETTINGS* colorSettings = new KiGfx::PCB_RENDER_SETTINGS(); + + // Load layers' colors from PCB data + colorSettings->ImportLegacyColors( m_Pcb->GetColorsSettings() ); + view->GetPainter()->ApplySettings( colorSettings ); + } + + // Set rendering order of layers (check m_galLayerOrder to see the order) + for( unsigned int i = 0; i < sizeof( m_galLayerOrder ) / sizeof( int ); ++i ) + { + view->SetLayerOrder( m_galLayerOrder[i], i ); + } + } +#endif } diff --git a/pcbnew/class_board_item.cpp b/pcbnew/class_board_item.cpp index 9b2bb8a972..b79e7f6760 100644 --- a/pcbnew/class_board_item.cpp +++ b/pcbnew/class_board_item.cpp @@ -132,3 +132,11 @@ std::string BOARD_ITEM::FormatInternalUnits( const wxSize& aSize ) { return FormatInternalUnits( aSize.GetWidth() ) + " " + FormatInternalUnits( aSize.GetHeight() ); } + + +void BOARD_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const +{ + // Basic fallback + aCount = 1; + aLayers[0] = m_Layer; +} diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index a2762e1b5c..6ed89c4efd 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -850,6 +850,24 @@ EDA_ITEM* D_PAD::Clone() const } +void D_PAD::ViewGetLayers( int aLayers[], int& aCount ) const +{ + if( m_Attribute == PAD_SMD || m_Attribute == PAD_CONN) + { + // Single layer pad (smd) without hole + aLayers[0] = GetParent()->GetLayer(); + aCount = 1; + } + else + { + // Multi layer pad with hole - pad is shown on one common layer, hole on the other + aLayers[0] = ITEM_GAL_LAYER( PADS_VISIBLE ); + aLayers[1] = ITEM_GAL_LAYER( PAD_HOLES_VISIBLE ); + aCount = 2; + } +} + + #if defined(DEBUG) void D_PAD::Show( int nestLevel, std::ostream& os ) const diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index 2a58543ec6..529a49577a 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -395,6 +395,9 @@ public: EDA_ITEM* Clone() const; + /// @copydoc VIEW_ITEM::ViewGetLayers() + virtual void ViewGetLayers( int aLayers[], int& aCount ) const; + #if defined(DEBUG) void Show( int nestLevel, std::ostream& os ) const; // overload #endif diff --git a/pcbnew/class_text_mod.cpp b/pcbnew/class_text_mod.cpp index 419a779faf..56de7e3c5f 100644 --- a/pcbnew/class_text_mod.cpp +++ b/pcbnew/class_text_mod.cpp @@ -488,6 +488,23 @@ EDA_ITEM* TEXTE_MODULE::Clone() const } +void TEXTE_MODULE::ViewGetLayers( int aLayers[], int& aCount ) const +{ + switch( GetParent()->GetLayer() ) + { + case LAYER_N_BACK: + aLayers[0] = ITEM_GAL_LAYER( MOD_TEXT_BK_VISIBLE ); // how about SILKSCREEN_N_BACK? + break; + + case LAYER_N_FRONT: + aLayers[0] = ITEM_GAL_LAYER( MOD_TEXT_FR_VISIBLE ); // how about SILKSCREEN_N_FRONT? + break; + } + + aCount = 1; +} + + #if defined(DEBUG) void TEXTE_MODULE::Show( int nestLevel, std::ostream& os ) const diff --git a/pcbnew/class_text_mod.h b/pcbnew/class_text_mod.h index 5042b27875..ba0e7b2087 100644 --- a/pcbnew/class_text_mod.h +++ b/pcbnew/class_text_mod.h @@ -156,6 +156,9 @@ public: EDA_ITEM* Clone() const; + /// @copydoc VIEW_ITEM::ViewGetLayers() + virtual void ViewGetLayers( int aLayers[], int& aCount ) const; + #if defined(DEBUG) void Show( int nestLevel, std::ostream& os ) const; // overload #endif diff --git a/pcbnew/class_track.cpp b/pcbnew/class_track.cpp index dc80db4d1c..9992b13edc 100644 --- a/pcbnew/class_track.cpp +++ b/pcbnew/class_track.cpp @@ -958,6 +958,25 @@ void SEGVIA::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode, } +void SEGVIA::ViewGetLayers( int aLayers[], int& aCount ) const +{ + /*int top_layer, bottom_layer; + ReturnLayerPair( &top_layer, &bottom_layer ); + + // We can add vias to all layers they belong, but then they are drawn this many times + aCount = 0; + for( int i = bottom_layer; i <= top_layer; ++i ) + { + aLayers[aCount++] = i; + }*/ + + // Just show it on common via & via holes layers + aLayers[0] = ITEM_GAL_LAYER( VIAS_VISIBLE ); + aLayers[1] = ITEM_GAL_LAYER( VIA_HOLES_VISIBLE ); + aCount = 2; +} + + // see class_track.h void TRACK::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) { diff --git a/pcbnew/class_track.h b/pcbnew/class_track.h index df6aed9c37..0690a207c9 100644 --- a/pcbnew/class_track.h +++ b/pcbnew/class_track.h @@ -414,6 +414,9 @@ public: EDA_ITEM* Clone() const; + /// @copydoc VIEW_ITEM::ViewGetLayers() + virtual void ViewGetLayers( int aLayers[], int& aCount ) const; + #if defined (DEBUG) void Show( int nestLevel, std::ostream& os ) const; // overload #endif diff --git a/pcbnew/hotkeys.cpp b/pcbnew/hotkeys.cpp index 5487510c30..c0fbfb006a 100644 --- a/pcbnew/hotkeys.cpp +++ b/pcbnew/hotkeys.cpp @@ -82,6 +82,9 @@ static EDA_HOTKEY HkResetLocalCoord( wxT( "Reset Local Coordinates" ), HK_RESET_LOCAL_COORD, ' ' ); static EDA_HOTKEY HkSwitchHighContrastMode( wxT("Switch Highcontrast mode"), HK_SWITCH_HIGHCONTRAST_MODE,'H'); +#ifdef KICAD_GAL +static EDA_HOTKEY HkSwitchCanvas( wxT( "Switch canvas" ), HK_SWITCH_CANVAS, WXK_F12 ); +#endif /* Fit on Screen */ #if !defined( __WXMAC__ ) static EDA_HOTKEY HkZoomAuto( wxT( "Zoom Auto" ), HK_ZOOM_AUTO, WXK_HOME ); @@ -228,6 +231,9 @@ EDA_HOTKEY* board_edit_Hotkey_List[] = &HkRecordMacros6, &HkCallMacros6, &HkRecordMacros7, &HkCallMacros7, &HkRecordMacros8, &HkCallMacros8, &HkRecordMacros9, &HkCallMacros9, &HkSwitchHighContrastMode, +#ifdef KICAD_GAL + &HkSwitchCanvas, +#endif NULL }; diff --git a/pcbnew/hotkeys.h b/pcbnew/hotkeys.h index 98e18fb565..5c24b43ea0 100644 --- a/pcbnew/hotkeys.h +++ b/pcbnew/hotkeys.h @@ -82,6 +82,9 @@ enum hotkey_id_commnand { HK_CALL_MACROS_9, HK_MACRO_ID_END, HK_SWITCH_HIGHCONTRAST_MODE, +#ifdef KICAD_GAL + HK_SWITCH_CANVAS, +#endif }; // Full list of hotkey descriptors for board editor and footprint editor diff --git a/pcbnew/menubar_pcbframe.cpp b/pcbnew/menubar_pcbframe.cpp index 722bece58d..c3e1cd2301 100644 --- a/pcbnew/menubar_pcbframe.cpp +++ b/pcbnew/menubar_pcbframe.cpp @@ -348,6 +348,17 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() _( "&List Nets" ), _( "View a list of nets with names and id's" ), KiBitmap( tools_xpm ) ); +#ifdef KICAD_GAL + // Switching GAL-based canvas on/off + viewMenu->AppendSeparator(); + + text = AddHotkeyName( _( "&Switch canvas" ), g_Pcbnew_Editor_Hokeys_Descr, HK_SWITCH_CANVAS, IS_ACCELERATOR ); + + AddMenuItem( viewMenu, ID_SWITCH_CANVAS, + text, _( "Switch the canvas implementation between old (XOR-based) and new (GAL-based)" ), + KiBitmap( tools_xpm ) ); +#endif + /** Create Place Menu **/ wxMenu* placeMenu = new wxMenu; diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp new file mode 100644 index 0000000000..cf34a162ad --- /dev/null +++ b/pcbnew/pcb_painter.cpp @@ -0,0 +1,421 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * @author Maciej Suminski + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace KiGfx; + +PCB_RENDER_SETTINGS::PCB_RENDER_SETTINGS() +{ + Update(); +} + + +void PCB_RENDER_SETTINGS::ImportLegacyColors( COLORS_DESIGN_SETTINGS* aSettings ) +{ + for( int i = 0; i < LAYER_COUNT; i++ ) + { + m_layerColors[i] = m_legacyColorMap[aSettings->GetLayerColor( i )]; + } + + for( int i = 0; i < END_PCB_VISIBLE_LIST; i++ ) + { + m_itemColors[i] = m_legacyColorMap[aSettings->GetItemColor( i )]; + } + + m_itemColors[VIA_HOLES_VISIBLE] = COLOR4D( 0.5f, 0.4f, 0.0f, 1.0f ); + m_itemColors[PAD_HOLES_VISIBLE] = COLOR4D( 0.0f, 0.5f, 0.5f, 1.0f ); + m_itemColors[VIAS_VISIBLE] = COLOR4D( 0.7f, 0.7f, 0.7f, 1.0f ); + m_itemColors[PADS_VISIBLE] = COLOR4D( 0.7f, 0.7f, 0.7f, 1.0f ); + + Update(); +} + + +void PCB_RENDER_SETTINGS::Update() +{ + // Calculate darkened/highlighted variants of layer colors + for( int i = 0; i < LAYER_COUNT; i++ ) + { + m_layerColors[i].a = m_layerOpacity; + m_layerColorsHi[i] = m_layerColors[i].Highlighted( m_highlightFactor ); + m_layerColorsDark[i] = m_layerColors[i].Darkened( 1.0 - m_highlightFactor ); + m_layerColorsSel[i] = m_layerColors[i].Highlighted( m_selectFactor ); + } + + for( int i = 0; i < END_PCB_VISIBLE_LIST; i++ ) + { + m_itemColors[i].a = m_layerOpacity; + m_itemColorsHi[i] = m_itemColors[i].Highlighted( m_highlightFactor ); + m_itemColorsDark[i] = m_itemColors[i].Darkened( 1.0 - m_highlightFactor ); + m_itemColorsSel[i] = m_itemColors[i].Highlighted( m_selectFactor ); + } + + m_hiContrastColor = COLOR4D( m_hiContrastFactor, m_hiContrastFactor, m_highlightFactor, + m_layerOpacity ); +} + + +PCB_PAINTER::PCB_PAINTER( GAL* aGal ) : + PAINTER( aGal ) +{ +} + + +const COLOR4D& PCB_PAINTER::getLayerColor( int aLayer, int aNetCode ) const +{ + // For item layers (vias, texts, and so on) + if( aLayer >= LAYER_COUNT ) + return getItemColor( aLayer - LAYER_COUNT, aNetCode ); + + if( m_pcbSettings->m_hiContrastEnabled && m_pcbSettings->m_activeLayer != aLayer ) + { + return m_pcbSettings->m_hiContrastColor; + } + else if( m_pcbSettings->m_highlightEnabled ) + { + if( aNetCode == m_pcbSettings->m_highlightNetcode ) + { + return m_pcbSettings->m_layerColorsHi[aLayer]; + } + else + { + return m_pcbSettings->m_layerColorsDark[aLayer]; + } + } + else + { + return m_pcbSettings->m_layerColors[aLayer]; + } +} + + +const COLOR4D& PCB_PAINTER::getItemColor( int aItemType, int aNetCode ) const +{ + // if(aNetCode == m_settings->m_hiContrastEnabled) + // return m_settings->m_itemColorsHi[ aItemType ]; + if( m_pcbSettings->m_highlightEnabled ) + { + if( aNetCode == m_pcbSettings->m_highlightNetcode ) + { + return m_pcbSettings->m_itemColorsHi[aItemType]; + } + else + { + return m_pcbSettings->m_itemColorsDark[aItemType]; + } + } + else + { + return m_pcbSettings->m_itemColors[aItemType]; + } +} + + +bool PCB_PAINTER::Draw( const EDA_ITEM* aItem, int aLayer ) +{ + // the "cast" applied in here clarifies which overloaded draw() is called + switch( aItem->Type() ) + { + case PCB_ZONE_T: + case PCB_TRACE_T: + draw( (TRACK*) aItem ); + break; + + case PCB_VIA_T: + draw( (SEGVIA*) aItem, aLayer ); + break; + + case PCB_PAD_T: + draw( (D_PAD*) aItem, aLayer ); + break; + + case PCB_LINE_T: + case PCB_MODULE_EDGE_T: + draw( (DRAWSEGMENT*) aItem ); + break; + + case PCB_TEXT_T: + draw( (TEXTE_PCB*) aItem ); + break; + + case PCB_MODULE_TEXT_T: + draw( (TEXTE_MODULE*) aItem, aLayer ); + break; + + case PCB_ZONE_AREA_T: + draw( (ZONE_CONTAINER*) aItem ); + break; + + default: + // Painter does not know how to draw the object + return false; + break; + } + + return true; +} + + +void PCB_PAINTER::draw( const TRACK* aTrack ) +{ + VECTOR2D start( aTrack->GetStart() ); + VECTOR2D end( aTrack->GetEnd() ); + COLOR4D strokeColor = getLayerColor( aTrack->GetLayer(), aTrack->GetNet() ); + + m_gal->SetLineCap( LINE_CAP_ROUND ); + m_gal->SetLineJoin( LINE_JOIN_ROUND ); + m_gal->SetLineWidth( aTrack->GetWidth() ); + m_gal->SetStrokeColor( strokeColor ); + m_gal->DrawLine( start, end ); +} + + +void PCB_PAINTER::draw( const SEGVIA* aVia, int aLayer ) +{ + VECTOR2D center( aVia->GetStart() ); + double radius; + COLOR4D fillColor; + + // Choose drawing settings depending on if we are drawing via's pad or hole + if( aLayer == ITEM_GAL_LAYER( VIAS_VISIBLE ) ) + { + radius = aVia->GetWidth() / 2.0f; + } + else if( aLayer == ITEM_GAL_LAYER( VIA_HOLES_VISIBLE ) ) + { + radius = aVia->GetDrillValue() / 2.0f; + } + else + return; + + fillColor = getLayerColor( aLayer, aVia->GetNet() ); + + m_gal->SetIsStroke( false ); + m_gal->SetIsFill( true ); + m_gal->SetFillColor( fillColor ); + m_gal->DrawCircle( center, radius ); +} + + +void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer ) +{ + COLOR4D fillColor; + VECTOR2D size; + PAD_SHAPE_T shape; + double m, n; + + m_gal->Save(); + m_gal->Translate( VECTOR2D( aPad->GetPosition() ) ); + m_gal->Rotate( -aPad->GetOrientation() * M_PI / 1800.0 ); // orientation is in tenths of degree + + // Choose drawing settings depending on if we are drawing a pad itself or a hole + if( aLayer == ITEM_GAL_LAYER( PAD_HOLES_VISIBLE ) ) + { + // Drawing hole + size = VECTOR2D( aPad->GetDrillSize() ) / 2.0f; + shape = aPad->GetDrillShape(); + } + else + { + // Drawing every kind of pad + m_gal->Translate( VECTOR2D( aPad->GetOffset() ) ); + size = VECTOR2D( aPad->GetSize() ) / 2.0f; + shape = aPad->GetShape(); + } + + fillColor = getLayerColor( aLayer, aPad->GetNet() ); + + m_gal->SetIsFill( true ); + m_gal->SetIsStroke( false ); + m_gal->SetFillColor( fillColor ); + + switch( shape ) + { + case PAD_OVAL: + if( size.y >= size.x ) + { + m = ( size.y - size.x ); + n = size.x; + + m_gal->DrawCircle( VECTOR2D( 0, -m ), n ); + m_gal->DrawCircle( VECTOR2D( 0, m ), n ); + m_gal->DrawRectangle( VECTOR2D( -n, -m ), VECTOR2D( n, m ) ); + } + else + { + m = ( size.x - size.y ); + n = size.y; + + m_gal->DrawCircle( VECTOR2D( -m, 0 ), n ); + m_gal->DrawCircle( VECTOR2D( m, 0 ), n ); + m_gal->DrawRectangle( VECTOR2D( -m, -n ), VECTOR2D( m, n ) ); + } + break; + + case PAD_RECT: + case PAD_TRAPEZOID: + m_gal->DrawRectangle( VECTOR2D( -size.x, -size.y ), VECTOR2D( size.x, size.y ) ); + break; + + case PAD_CIRCLE: + m_gal->DrawCircle( VECTOR2D( 0.0, 0.0 ), size.x ); + break; + + case PAD_OCTAGON: // it is not used anywhere, neither you can set it using pcbnew.. + case PAD_NONE: + break; + } + + m_gal->Restore(); +} + + +void PCB_PAINTER::draw( const DRAWSEGMENT* aSegment ) +{ + COLOR4D strokeColor = getLayerColor( aSegment->GetLayer(), 0 ); + std::deque pointsList; + + m_gal->SetIsFill( false ); + m_gal->SetIsStroke( true ); + m_gal->SetStrokeColor( strokeColor ); + m_gal->SetLineWidth( aSegment->GetWidth() ); + m_gal->SetLineCap( LINE_CAP_ROUND ); + m_gal->SetLineJoin( LINE_JOIN_ROUND ); + + switch( aSegment->GetShape() ) + { + case S_SEGMENT: + m_gal->DrawLine( VECTOR2D( aSegment->GetStart() ), VECTOR2D( aSegment->GetEnd() ) ); + break; + + case S_RECT: + m_gal->SetLineCap( LINE_CAP_SQUARED ); + m_gal->SetLineJoin( LINE_JOIN_BEVEL ); + m_gal->DrawLine( VECTOR2D( aSegment->GetStart() ), VECTOR2D( aSegment->GetEnd() ) ); + break; + + case S_ARC: + m_gal->DrawArc( VECTOR2D( aSegment->GetCenter() ), aSegment->GetRadius(), + aSegment->GetArcAngleStart() * M_PI / 1800.0, + ( aSegment->GetArcAngleStart() + aSegment->GetAngle() ) * M_PI / 1800.0 ); + break; + + case S_CIRCLE: + m_gal->DrawCircle( VECTOR2D( aSegment->GetCenter() ), aSegment->GetRadius() ); + break; + + case S_POLYGON: + std::copy( aSegment->GetPolyPoints().begin(), aSegment->GetPolyPoints().end(), + std::back_inserter( pointsList ) ); + m_gal->DrawPolygon( pointsList ); + break; + + case S_CURVE: + m_gal->DrawCurve( VECTOR2D( aSegment->GetStart() ), + VECTOR2D( aSegment->GetBezControl1() ), + VECTOR2D( aSegment->GetBezControl2() ), + VECTOR2D( aSegment->GetEnd() ) ); + break; + + case S_LAST: + break; + } +} + + +void PCB_PAINTER::draw( const TEXTE_PCB* aText ) +{ + COLOR4D strokeColor = getLayerColor( aText->GetLayer(), 0 ); + VECTOR2D position( aText->GetTextPosition().x, aText->GetTextPosition().y ); + double orientation = aText->GetOrientation() * M_PI / 1800.0; + + m_gal->SetStrokeColor( strokeColor ); + m_gal->SetLineWidth( aText->GetThickness() ); + m_stroke_font->LoadAttributes( aText ); + m_stroke_font->Draw( std::string( aText->GetText().mb_str() ), position, orientation ); +} + + +void PCB_PAINTER::draw( const TEXTE_MODULE* aText, int aLayer ) +{ + COLOR4D strokeColor = getLayerColor( aLayer, 0 ); + + m_gal->SetStrokeColor( strokeColor ); + m_gal->SetLineWidth( aText->GetThickness() ); + m_stroke_font->LoadAttributes( aText ); + m_stroke_font->Draw( std::string( aText->GetText().mb_str() ), + VECTOR2D( aText->GetTextPosition().x, aText->GetTextPosition().y), + aText->GetDrawRotation() * M_PI / 1800.0 ); +} + + +void PCB_PAINTER::draw( const ZONE_CONTAINER* aContainer ) +{ + COLOR4D fillColor = getLayerColor( aContainer->GetLayer(), aContainer->GetNet() ); + std::vector::iterator polyIterator; + std::vector polyPoints = aContainer->GetFilledPolysList(); + std::deque corners; + + m_gal->SetLineCap( LINE_CAP_ROUND ); + m_gal->SetLineJoin( LINE_JOIN_ROUND ); + m_gal->SetFillColor( fillColor ); + m_gal->SetStrokeColor( fillColor ); + m_gal->SetIsFill( aContainer->IsFilled() ); + m_gal->SetIsStroke( true ); + m_gal->SetLineWidth( aContainer->GetThermalReliefCopperBridge() / 2.0 ); + + // FIXME implement hatch mode + + for( polyIterator = polyPoints.begin(); polyIterator != polyPoints.end(); polyIterator++ ) + { + // Find out all of polygons and then draw them + if( !polyIterator->end_contour ) + { + corners.push_back( VECTOR2D( *polyIterator ) ); + } + else + { + m_gal->DrawPolygon( corners ); + m_gal->DrawPolyline( corners ); + corners.clear(); + } + } +} diff --git a/pcbnew/pcb_painter.h b/pcbnew/pcb_painter.h new file mode 100644 index 0000000000..80e2f78694 --- /dev/null +++ b/pcbnew/pcb_painter.h @@ -0,0 +1,143 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Tomasz Wlostowski + * @author Maciej Suminski + * + * 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 + */ + +#ifndef __CLASS_PCB_PAINTER_H +#define __CLASS_PCB_PAINTER_H + +#include + +#include + + +class EDA_ITEM; +class COLORS_DESIGN_SETTINGS; +class BOARD_ITEM; +class BOARD; +class SEGVIA; +class TRACK; +class D_PAD; +class DRAWSEGMENT; +class MODULE; +class SEGZONE; +class ZONE_CONTAINER; +class TEXTE_PCB; +class TEXTE_MODULE; + +namespace KiGfx +{ +class GAL; +class STROKE_FONT; + +/** + * Class PCB_RENDER_SETTINGS + * Stores PCB specific render settings. + */ +class PCB_RENDER_SETTINGS : public RENDER_SETTINGS +{ +public: + + friend class PCB_PAINTER; + + enum ClearanceMode { + CL_VIAS = 0x1, + CL_PADS = 0x2, + CL_TRACKS = 0x4 + }; + + PCB_RENDER_SETTINGS(); + + /// @copydoc RENDER_SETTINGS::Update() + void Update(); + + /// @copydoc RENDER_SETTINGS::ImportLegacyColors() + void ImportLegacyColors( COLORS_DESIGN_SETTINGS* aSettings ); + +protected: + + /// Colors for all layers (including special, highlighted & darkened versions) + COLOR4D m_layerColors [LAYER_COUNT]; + COLOR4D m_layerColorsHi [LAYER_COUNT]; + COLOR4D m_layerColorsSel [LAYER_COUNT]; + COLOR4D m_layerColorsDark[LAYER_COUNT]; + COLOR4D m_itemColors [END_PCB_VISIBLE_LIST]; + COLOR4D m_itemColorsHi [END_PCB_VISIBLE_LIST]; + COLOR4D m_itemColorsSel [END_PCB_VISIBLE_LIST]; + COLOR4D m_itemColorsDark [END_PCB_VISIBLE_LIST]; + + bool m_sketchModeSelect[END_PCB_VISIBLE_LIST]; + bool m_visibleLayers [LAYER_COUNT]; + bool m_visibleItems [END_PCB_VISIBLE_LIST]; +}; + + +/** + * Class PCB_PAINTER + * Contains methods for drawing PCB-specific items. + */ +class PCB_PAINTER : public PAINTER +{ +public: + + PCB_PAINTER( GAL* aGal ); + + /// @copydoc PAINTER::Draw() + virtual bool Draw( const EDA_ITEM*, int ); + + /// @copydoc PAINTER::ApplySettings() + virtual void ApplySettings( RENDER_SETTINGS* aSettings ) + { + PAINTER::ApplySettings( aSettings ); + + // Store PCB specific render settings + m_pcbSettings = dynamic_cast ( aSettings ); + } + +protected: + + PCB_RENDER_SETTINGS* m_pcbSettings; + + /// @copydoc PAINTER::getLayerColor() + const COLOR4D& getLayerColor( int aLayer, int aNetCode ) const; + + /** + * Function getItemColor + * Returns color for a special layer (eg. vias/pads holes, texts on front/bottom layer, etc.) + * @param aItemType Layer number of the item to be drawn. + * @param aNetCode Net number of the item to be drawn. + */ + const COLOR4D& getItemColor( int aItemType, int aNetCode ) const; + + // Drawing functions for various types of PCB-specific items + void draw( const TRACK* ); + void draw( const SEGVIA*, int ); + void draw( const D_PAD*, int ); + void draw( const DRAWSEGMENT* ); + void draw( const TEXTE_PCB* ); + void draw( const TEXTE_MODULE*, int ); + void draw( const ZONE_CONTAINER* ); +}; +} // namespace KiGfx + +#endif /* __CLASS_PAINTER_H */ diff --git a/tools/class_painter.h b/tools/class_painter.h deleted file mode 100644 index ee201c1c50..0000000000 --- a/tools/class_painter.h +++ /dev/null @@ -1,172 +0,0 @@ - - -#if defined(PCBNEW) - -class BOARD; -class TRACK; -class ZONE_CONTAINER; - -//: - -#elif defined(EESCHEMA) - -class SCH_SHEET; -//: - -#endif - - -/** - * Class PAINTER - * contains all the knowledge about how to draw any graphical object onto - * any particular output device. - * This knowledge is held outside the individual graphical objects so that - * alternative output devices may be used, and so that the graphical objects - * themselves to not contain drawing routines. Drawing routines in the objects - * cause problems with usages of the objects as simple container objects in - * DLL/DSOs. - */ -class PAINTER -{ -public: - - - /** - * Constructor PAINTER( wxDC& ) - * initializes this object for painting on any of the polymorphic - * wxDC derivatives. - * - * @param aDC is a reference to a polymorphic wx device context on which - * to draw. It can be any of the wxDC derivatives. - * No ownership is given to this PAINTER of aDC. - */ - PAINTER( wxDC& aDC ) : - m_dc( aDC ), - m_highlight( false ), - m_grayed( false ) - { - } - - - -#if defined(PCBNEW) - - void Draw( const BOARD_ITEM* ); - -#elif defined(EESCHEMA) - - void Draw( const SCH_ITEM* ); - -#endif - - -private: - - wxDC& m_dc; - - // drawing state information. - bool m_highlite; - bool m_grayed; - - -#if defined(PCBNEW) - - void draw( const TRACK* ); - void draw( const MODULE* ); - void draw( const EDGE_MODULE* ); - // : - -#elif defined(EESCHEMA) - void draw( const SCH_WIRE* ); - // : - -#endif -} - - -#if defined(PCBNEW) - -void PAINTER::Draw( const BOARD_ITEM* aItem ) -{ - // the "cast" applied in here clarifies which overloaded draw() is called - - switch( aItem->Type() ) - { - case PCB_MODULE_T: - draw( (MODULE*) aItem ); - break; - - case PCB_PAD_T: - draw( (D_PAD*) aItem ); - break; - - case PCB_LINE_T: - draw( (TEXTE_PCB*) aItem ); - break; - - case PCB_TEXT_T: - draw( (TEXTE_PCB*) aItem ); - break; - - case PCB_MODULE_TEXT_T: - draw( (TEXTE_PCB*) aItem ); - break; - - case PCB_MODULE_EDGE_T: - draw( (EDGE_MODULE*) aItem ); - break; - - case PCB_TRACE_T: - draw( (TRACKE*) aItem ); - break; - - case PCB_VIA_T: - draw( (VIA*) aItem ); - break; - - case PCB_ZONE_T: - draw( (SEGZONE*) aItem ); - break; - - case PCB_MARKER_T: - draw( (MARKER_PCB*) aItem ); - break; - - case PCB_DIMENSION_T: - draw( (DIMENSION*) aItem ); - break; - - case PCB_TARGET_T: - draw( (TARGET*) aItem ); - break; - - - case PCB_ZONE_AREA_T: - draw( (ZONE_CONTAINER*) aItem ); - break; - - /* not used - case PCB_ITEM_LIST_T: - draw( (BOARD_ITEM_LIST*) aItem ); - break; - */ - - default: - ; // nothing - } -} - -#elif defined(EESCHEMA) - -void PAINTER::Draw( const SCH_ITEM* aItem ) -{ - // the "cast" applied in here clarifies which overloaded draw() is called - - switch( aItem->Type() ) - { - //: - } -} - - -#endif \ No newline at end of file