Commit e262b321 authored by Maciej Suminski's avatar Maciej Suminski

Introduction of Graphics Abstraction Layer based rendering for pcbnew.

New classes:
    - VIEW - represents view that is seen by user, takes care of layer ordering & visibility and how it is displayed (which location, how much zoomed, etc.)
    - VIEW_ITEM - Base class for every item that can be displayed on VIEW (the biggest change is that now it may be necessary to override ViewBBox & ViewGetLayers method for derived classes).
    - EDA_DRAW_PANEL_GAL - Inherits after EDA_DRAW_PANEL, displays VIEW output, right now it is not editable (in opposite to usual EDA_DRAW_PANEL).
    - GAL/OPENGL_GAL/CAIRO_GAL - Base Graphics Abstraction Layer class + two different flavours (Cairo is not fully supported yet), that offers methods to draw primitives using different libraries.
    - WX_VIEW_CONTROLS - Controller for VIEW, handles user events, allows zooming, panning, etc.
    - PAINTER/PCB_PAINTER - Classes that uses GAL interface to draw items (as you may have already guessed - PCB_PAINTER is a class for drawing PCB specific object, PAINTER is an abstract class). Its methods are invoked by VIEW, when an item has to be drawn. To display a new type of item - you need to implement draw(ITEM_TYPE*) method that draws it using GAL methods.
    - STROKE_FONT - Implements stroke font drawing using GAL methods.
                          
Most important changes to Kicad original code:
    * EDA_ITEM now inherits from VIEW_ITEM, which is a base class for all drawable objects.
    * EDA_DRAW_FRAME contains both usual EDA_DRAW_PANEL and new EDA_DRAW_PANEL_GAL, that can be switched anytime.
    * There are some new layers for displaying multilayer pads, vias & pads holes (these are not shown yet on the right sidebar in pcbnew)
    * Display order of layers is different than in previous versions (if you are curious - you may check m_galLayerOrder@pcbnew/basepcbframe.cpp). Preserving usual order would result in not very natural display, such as showing silkscreen texts on the bottom.
    * Introduced new hotkey (Alt+F12) and new menu option (View->Switch canvas) for switching canvas during runtime.
    * Some of classes (mostly derived from BOARD_ITEM) now includes ViewBBox & ViewGetLayers methods.
    * Removed tools/class_painter.h, as now it is extended and included in source code.
                         
Build changes:
    * GAL-based rendering option is turned on by a new compilation CMake option KICAD_GAL.
    * When compiling with CMake option KICAD_GAL=ON, GLEW and Cairo libraries are required.
    * GAL-related code is compiled into a static library (common/libgal).
    * Build with KICAD_GAL=OFF should not need any new libraries and should come out as a standard version of Kicad
                            
Currently most of items in pcbnew can be displayed using OpenGL (to be done are DIMENSIONS and MARKERS).
More details about GAL can be found in: http://www.ohwr.org/attachments/1884/view-spec.pdf
parent ef2c9406
...@@ -56,6 +56,10 @@ option(KICAD_SCRIPTING_MODULES ...@@ -56,6 +56,10 @@ option(KICAD_SCRIPTING_MODULES
option(KICAD_SCRIPTING_WXPYTHON option(KICAD_SCRIPTING_WXPYTHON
"set this option ON to build wxpython implementation for wx interface building in python and py.shell" "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: # when option KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES is enabled:
# PYTHON_EXECUTABLE can be defined when invoking cmake # PYTHON_EXECUTABLE can be defined when invoking cmake
...@@ -189,6 +193,10 @@ if(USE_WX_GRAPHICS_CONTEXT) ...@@ -189,6 +193,10 @@ if(USE_WX_GRAPHICS_CONTEXT)
add_definitions(-DUSE_WX_GRAPHICS_CONTEXT) add_definitions(-DUSE_WX_GRAPHICS_CONTEXT)
endif() endif()
if(KICAD_GAL)
add_definitions(-DKICAD_GAL)
endif()
# Allow user to override the default settings for adding images to menu items. By default # 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 # 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. # defining -DUSE_IMAGES_IN_MENUS=ON/OFF to force the preferred behavior.
...@@ -257,6 +265,20 @@ add_definitions(-DWX_COMPATIBILITY) ...@@ -257,6 +265,20 @@ add_definitions(-DWX_COMPATIBILITY)
find_package(OpenGL QUIET) find_package(OpenGL QUIET)
check_find_package_result(OPENGL_FOUND "OpenGL") 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 # # Find Boost library #
###################### ######################
......
...@@ -4,12 +4,29 @@ include_directories( ...@@ -4,12 +4,29 @@ include_directories(
./dialogs ./dialogs
./dialog_about ./dialog_about
${Boost_INCLUDE_DIR} ${Boost_INCLUDE_DIR}
${CAIRO_INCLUDE_DIR}
../3d-viewer ../3d-viewer
../pcbnew ../pcbnew
../polygon ../polygon
${INC_AFTER} ${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 set(COMMON_ABOUT_DLG_SRCS
dialog_about/AboutDialog_main.cpp dialog_about/AboutDialog_main.cpp
dialog_about/dialog_about.cpp dialog_about/dialog_about.cpp
...@@ -82,6 +99,14 @@ set(COMMON_SRCS ...@@ -82,6 +99,14 @@ set(COMMON_SRCS
zoom.cpp 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}) add_library(common STATIC ${COMMON_SRCS})
set(PCB_COMMON_SRCS set(PCB_COMMON_SRCS
...@@ -131,6 +156,12 @@ set(PCB_COMMON_SRCS ...@@ -131,6 +156,12 @@ set(PCB_COMMON_SRCS
fp_lib_table.cpp 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 # add -DPCBNEW to compilation of these PCBNEW sources
set_source_files_properties( ${PCB_COMMON_SRCS} PROPERTIES set_source_files_properties( ${PCB_COMMON_SRCS} PROPERTIES
......
...@@ -222,6 +222,22 @@ EDA_ITEM& EDA_ITEM::operator=( const EDA_ITEM& aItem ) ...@@ -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) #if defined(DEBUG)
// A function that should have been in wxWidgets // A function that should have been in wxWidgets
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <macros.h> #include <macros.h>
#include <id.h> #include <id.h>
#include <class_drawpanel.h> #include <class_drawpanel.h>
#include <class_drawpanel_gal.h>
#include <class_base_screen.h> #include <class_base_screen.h>
#include <msgpanel.h> #include <msgpanel.h>
#include <wxstruct.h> #include <wxstruct.h>
...@@ -66,6 +67,7 @@ BEGIN_EVENT_TABLE( EDA_DRAW_FRAME, EDA_BASE_FRAME ) ...@@ -66,6 +67,7 @@ BEGIN_EVENT_TABLE( EDA_DRAW_FRAME, EDA_BASE_FRAME )
EVT_MENU_OPEN( EDA_DRAW_FRAME::OnMenuOpen ) EVT_MENU_OPEN( EDA_DRAW_FRAME::OnMenuOpen )
EVT_ACTIVATE( EDA_DRAW_FRAME::OnActivate ) EVT_ACTIVATE( EDA_DRAW_FRAME::OnActivate )
EVT_MENU_RANGE( ID_ZOOM_IN, ID_ZOOM_REDRAW, EDA_DRAW_FRAME::OnZoom ) 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_OFFCENTER_ZOOM_IN, ID_OFFCENTER_ZOOM_OUT, EDA_DRAW_FRAME::OnZoom )
EVT_MENU_RANGE( ID_POPUP_ZOOM_START_RANGE, ID_POPUP_ZOOM_END_RANGE, EVT_MENU_RANGE( ID_POPUP_ZOOM_START_RANGE, ID_POPUP_ZOOM_END_RANGE,
EDA_DRAW_FRAME::OnZoom ) EDA_DRAW_FRAME::OnZoom )
...@@ -100,6 +102,8 @@ EDA_DRAW_FRAME::EDA_DRAW_FRAME( wxWindow* aParent, ...@@ -100,6 +102,8 @@ EDA_DRAW_FRAME::EDA_DRAW_FRAME( wxWindow* aParent,
m_HotkeysZoomAndGridList = NULL; m_HotkeysZoomAndGridList = NULL;
m_canvas = NULL; m_canvas = NULL;
m_galCanvas = NULL;
m_galCanvasActive = false;
m_messagePanel = NULL; m_messagePanel = NULL;
m_currentScreen = NULL; m_currentScreen = NULL;
m_toolId = ID_NO_TOOL_SELECTED; m_toolId = ID_NO_TOOL_SELECTED;
...@@ -937,3 +941,27 @@ void EDA_DRAW_FRAME::AdjustScrollBars( const wxPoint& aCenterPositionIU ) ...@@ -937,3 +941,27 @@ void EDA_DRAW_FRAME::AdjustScrollBars( const wxPoint& aCenterPositionIU )
screen->m_ScrollbarPos.x, screen->m_ScrollbarPos.x,
screen->m_ScrollbarPos.y, noRefresh ); 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;
}
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <macros.h> #include <macros.h>
#include <id.h> #include <id.h>
#include <class_drawpanel.h> #include <class_drawpanel.h>
#include <class_drawpanel_gal.h>
#include <class_base_screen.h> #include <class_base_screen.h>
#include <wxstruct.h> #include <wxstruct.h>
...@@ -74,6 +75,9 @@ BEGIN_EVENT_TABLE( EDA_DRAW_PANEL, wxScrolledWindow ) ...@@ -74,6 +75,9 @@ BEGIN_EVENT_TABLE( EDA_DRAW_PANEL, wxScrolledWindow )
EVT_ERASE_BACKGROUND( EDA_DRAW_PANEL::OnEraseBackground ) EVT_ERASE_BACKGROUND( EDA_DRAW_PANEL::OnEraseBackground )
EVT_SCROLLWIN( EDA_DRAW_PANEL::OnScroll ) EVT_SCROLLWIN( EDA_DRAW_PANEL::OnScroll )
EVT_ACTIVATE( EDA_DRAW_PANEL::OnActivate ) 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 ) EVT_MENU_RANGE( ID_PAN_UP, ID_PAN_RIGHT, EDA_DRAW_PANEL::OnPan )
END_EVENT_TABLE() END_EVENT_TABLE()
...@@ -1377,6 +1381,18 @@ void EDA_DRAW_PANEL::OnPan( wxCommandEvent& event ) ...@@ -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, void EDA_DRAW_PANEL::EndMouseCapture( int id, int cursor, const wxString& title,
bool aCallEndFunc ) bool aCallEndFunc )
{ {
......
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 <wx/wx.h>
#include <wx/frame.h>
#include <wx/window.h>
#include <wx/event.h>
#include <wx/colour.h>
#include <wx/filefn.h>
#include <class_drawpanel_gal.h>
#include <view/view.h>
#include <view/wx_view_controls.h>
#include <pcb_painter.h>
#include <gal/graphics_abstraction_layer.h>
#include <gal/opengl/opengl_gal.h>
#include <gal/cairo/cairo_gal.h>
#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<KiGfx::OPENGL_GAL*> (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 );
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <wx/dcbuffer.h>
#include <wx/rawbmp.h>
#include <wx/log.h>
#include <gal/cairo/cairo_gal.h>
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<VECTOR2D>& aPointList )
{
bool isFirstPoint = true;
// Iterate over the point list and draw the segments
for( std::deque<VECTOR2D>::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<VECTOR2D>& aPointList )
{
bool isFirstPoint = true;
// Iterate over the point list and draw the polygon
for( std::deque<VECTOR2D>::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<GroupElement>::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;
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <wx/log.h>
#include <gal/graphics_abstraction_layer.h>
#include <gal/definitions.h>
#include <gal/color4d.h>
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 );
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <gal/opengl/opengl_gal.h>
#include <gal/opengl/shader.h>
#include <gal/definitions.h>
#include <wx/log.h>
#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<GLuint>::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<VECTOR2D>& 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<VECTOR2D>::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<VECTOR2D> 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<VECTOR2D>::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<VECTOR2D>& aPointList )
{
// Any non convex polygon needs to be tesselated
// for this purpose the GLU standard functions are used
GLUtesselator* tesselator = gluNewTess();
typedef std::vector<OGLPOINT> 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<VECTOR2D> 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<GLuint>::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;
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <iostream>
#include <fstream>
#include <wx/log.h>
#include <gal/opengl/shader.h>
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<GLuint>::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 );
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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;
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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();
}
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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;
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 ) );
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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();
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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;
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2013 Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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);
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2013 Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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;
}
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <gal/stroke_font.h>
#include <gal/graphics_abstraction_layer.h>
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<VECTOR2D> 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<VECTOR2D> 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<VECTOR2D>::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<BOX2D>::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<VECTOR2D> pointListScaled;
for( std::deque<VECTOR2D>::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<BOX2D>::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;
}
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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 <eda_text.h>
#include <view/view.h>
#include <painter.h>
#include <gal/stroke_font.h>
#include <newstroke_font.h>
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;
}
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 <sys/time.h>
#endif
#include <stdint.h>
/**
* 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
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 <boost/foreach.hpp>
#include <base_struct.h>
#include <layers_id_colors_and_visibility.h>
#include <view/view.h>
#include <view/view_rtree.h>
#include <gal/definitions.h>
#include <gal/graphics_abstraction_layer.h>
#include <painter.h>
#include <profile.h>
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 <class Container>
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<LayerItemPair>& aResult )
{
if( m_orderedLayers.empty() )
return 0;
std::vector<VIEW_LAYER*>::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<std::vector<LayerItemPair> > 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<EDA_ITEM*>( 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 );
};
}
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 <gal/definitions.h>
#include <view/view_item.h>
#include <view/view.h>
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 );
}
}
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 <wx/wx.h>
#include <wx/window.h>
#include <view/view.h>
#include <view/wx_view_controls.h>
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();
}
...@@ -39,7 +39,11 @@ ...@@ -39,7 +39,11 @@
#include <hotkeys_basic.h> #include <hotkeys_basic.h>
#include <menus_helpers.h> #include <menus_helpers.h>
#include <base_units.h> #include <base_units.h>
#ifdef KICAD_GAL
#include <class_drawpanel_gal.h>
#include <gal/graphics_abstraction_layer.h>
#include <view/view.h>
#endif
void EDA_DRAW_FRAME::RedrawScreen( const wxPoint& aCenterPoint, bool aWarpPointer ) void EDA_DRAW_FRAME::RedrawScreen( const wxPoint& aCenterPoint, bool aWarpPointer )
...@@ -82,7 +86,10 @@ void EDA_DRAW_FRAME::Zoom_Automatique( bool aWarpPointer ) ...@@ -82,7 +86,10 @@ void EDA_DRAW_FRAME::Zoom_Automatique( bool aWarpPointer )
if( screen->m_FirstRedraw ) if( screen->m_FirstRedraw )
screen->SetCrossHairPosition( screen->GetScrollCenterPosition() ); 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 ) ...@@ -160,6 +167,36 @@ void EDA_DRAW_FRAME::OnZoom( wxCommandEvent& event )
m_canvas->Refresh(); m_canvas->Refresh();
break; 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: case ID_POPUP_ZOOM_CENTER:
center = screen->GetCrossHairPosition(); center = screen->GetCrossHairPosition();
RedrawScreen( center, true ); RedrawScreen( center, true );
......
...@@ -104,6 +104,13 @@ target_link_libraries(cvpcb ...@@ -104,6 +104,13 @@ target_link_libraries(cvpcb
${GDI_PLUS_LIBRARIES} ${GDI_PLUS_LIBRARIES}
) )
if(KICAD_GAL)
target_link_libraries(cvpcb
gal
${GLEW_LIBRARIES}
)
endif(KICAD_GAL)
### ###
# Add cvpcb as install target # Add cvpcb as install target
### ###
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <colors.h> #include <colors.h>
#include <bitmaps.h> #include <bitmaps.h>
#include <richio.h> #include <richio.h>
#include <view/view_item.h>
#include <boost/ptr_container/ptr_vector.hpp> #include <boost/ptr_container/ptr_vector.hpp>
...@@ -389,7 +390,11 @@ public: ...@@ -389,7 +390,11 @@ public:
* is a base class for most all the KiCad significant classes, used in * is a base class for most all the KiCad significant classes, used in
* schematics and boards. * schematics and boards.
*/ */
#ifdef KICAD_GAL
class EDA_ITEM : public KiGfx::VIEW_ITEM
#else
class EDA_ITEM class EDA_ITEM
#endif
{ {
private: private:
...@@ -740,6 +745,12 @@ public: ...@@ -740,6 +745,12 @@ public:
*/ */
virtual EDA_ITEM& operator=( const EDA_ITEM& aItem ); 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) #if defined(DEBUG)
/** /**
......
...@@ -248,6 +248,9 @@ public: ...@@ -248,6 +248,9 @@ public:
static std::string FormatInternalUnits( const wxPoint& aPoint ); static std::string FormatInternalUnits( const wxPoint& aPoint );
static std::string FormatInternalUnits( const wxSize& aSize ); static std::string FormatInternalUnits( const wxSize& aSize );
/// @copydoc VIEW_ITEM::ViewGetLayers()
virtual void ViewGetLayers( int aLayers[], int& aCount ) const;
}; };
#endif /* BOARD_ITEM_STRUCT_H */ #endif /* BOARD_ITEM_STRUCT_H */
...@@ -258,6 +258,9 @@ public: ...@@ -258,6 +258,9 @@ public:
void OnCharHook( wxKeyEvent& event ); void OnCharHook( wxKeyEvent& event );
void OnPan( wxCommandEvent& event ); void OnPan( wxCommandEvent& event );
#ifdef KICAD_GAL
void OnSize( wxSizeEvent& event );
#endif
void EraseScreen( wxDC* DC ); void EraseScreen( wxDC* DC );
void OnScrollWin( wxCommandEvent& event ); void OnScrollWin( wxCommandEvent& event );
......
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 <wx/wx.h>
#include <wx/window.h>
#include <math/vector2d.h>
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
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <map>
#include <iterator>
#include <cairo.h>
#include <gal/graphics_abstraction_layer.h>
#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. "
* <br>
* 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<VECTOR2D>& 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<VECTOR2D>& 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<wxColour> 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<cairo_path_t*> 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<GroupElement> Group; ///< A graphic group type definition
std::deque<Group> 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<LineCap, cairo_line_cap_t> lineCapMap; ///< Line cap style mapping
std::map<LineJoin, cairo_line_join_t> 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_
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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_ */
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <deque>
#include <stack>
#include <wx/event.h>
#include <math/matrix3x3.h>
#include <gal/color4d.h>
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.
* <br>
* 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<VECTOR2D>& 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<VECTOR2D>& 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<double> 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_ */
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <gal/graphics_abstraction_layer.h>
#include <GL/glew.h>
// wxWidgets imports
#include <wx/wx.h>
#include <wx/glcanvas.h>
// STL imports
#include <cmath>
#include <iterator>
#include <vector>
#include <algorithm>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#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<VECTOR2D>& 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<VECTOR2D>& 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<GLuint> displayListsGroup; ///< List of display lists used for groups
double curvePoints[12]; ///< Coefficients for curves
std::deque<VECTOR2D> unitCirclePoints; ///< List of the points on a unit circle
// Polygon tesselation
GLUtesselator* tesselator; ///< Pointer to the tesselator
// Shader
std::deque<SHADER> 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_
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <GL/glew.h>
#include <string>
#include <deque>
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.
* <br>
* 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<GLuint> 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<GLint> parameterLocation; ///< Location of the parameter
};
} // namespace KiGfx
#endif /* SHADER_H_ */
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> 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 <string>
#include <deque>
#include <eda_text.h>
#include <math/box2.h>
namespace KiGfx
{
class GAL;
typedef std::deque< std::deque<VECTOR2D> > Glyph;
typedef std::deque<Glyph> 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<BOX2D> 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_ */
...@@ -213,6 +213,7 @@ enum main_id ...@@ -213,6 +213,7 @@ enum main_id
ID_ZOOM_OUT, ID_ZOOM_OUT,
ID_ZOOM_PAGE, ID_ZOOM_PAGE,
ID_ZOOM_REDRAW, ID_ZOOM_REDRAW,
ID_SWITCH_CANVAS,
/* Panning command event IDs. */ /* Panning command event IDs. */
ID_PAN_UP, ID_PAN_UP,
......
...@@ -143,9 +143,19 @@ enum PCB_VISIBLE ...@@ -143,9 +143,19 @@ enum PCB_VISIBLE
MOD_VALUES_VISIBLE, ///< show modules values (when texts are visibles) MOD_VALUES_VISIBLE, ///< show modules values (when texts are visibles)
MOD_REFERENCES_VISIBLE, ///< show modules references (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 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 * Function IsValidLayerIndex
......
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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 <vector>
#include <map>
#include <gal/color4d.h>
#include <colors.h>
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<EDA_COLOR_T, COLOR4D> 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 */
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 <vector>
#include <boost/unordered/unordered_map.hpp>
#include <math/box2.h>
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<VIEW_ITEM*, int> 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<LayerItemPair>& 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<class T> void AddItems( const T& aItems );
* template<class T> 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<VIEW_ITEM*> 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<int, VIEW_LAYER> LayerMap;
typedef LayerMap::iterator LayerMapIter;
typedef std::vector<VIEW_LAYER*> LayerOrder;
typedef std::vector<VIEW_LAYER*>::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
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 <math/box2.h>
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
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 <math/box2.h>
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
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 <wx/wx.h>
#include <wx/event.h>
#include <view/view_controls.h>
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
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
class EDA_ITEM; class EDA_ITEM;
class EDA_RECT; class EDA_RECT;
class EDA_DRAW_PANEL; class EDA_DRAW_PANEL;
class EDA_DRAW_PANEL_GAL;
class EDA_MSG_PANEL; class EDA_MSG_PANEL;
class BASE_SCREEN; class BASE_SCREEN;
class PARAM_CFG_BASE; class PARAM_CFG_BASE;
...@@ -391,11 +392,15 @@ protected: ...@@ -391,11 +392,15 @@ protected:
EDA_HOTKEY_CONFIG* m_HotkeysZoomAndGridList; EDA_HOTKEY_CONFIG* m_HotkeysZoomAndGridList;
int m_LastGridSizeId; int m_LastGridSizeId;
bool m_DrawGrid; // hide/Show grid bool m_DrawGrid; // hide/Show grid
bool m_galCanvasActive; // whether to use new GAL engine
EDA_COLOR_T m_GridColor; // Grid color EDA_COLOR_T m_GridColor; // Grid color
/// The area to draw on. /// The area to draw on.
EDA_DRAW_PANEL* m_canvas; 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. /// Tool ID of previously active draw tool bar button.
int m_lastDrawToolId; int m_lastDrawToolId;
...@@ -904,6 +909,30 @@ public: ...@@ -904,6 +909,30 @@ public:
*/ */
wxString LengthDoubleToString( double aValue, bool aConvertToMils = false ) const; 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() DECLARE_EVENT_TABLE()
}; };
......
...@@ -426,6 +426,13 @@ target_link_libraries(pcbnew ...@@ -426,6 +426,13 @@ target_link_libraries(pcbnew
${PCBNEW_EXTRA_LIBS} ${PCBNEW_EXTRA_LIBS}
) )
if(KICAD_GAL)
target_link_libraries(pcbnew
gal
${GLEW_LIBRARIES}
)
endif(KICAD_GAL)
### ###
# Add pcbnew as install target # Add pcbnew as install target
### ###
......
...@@ -42,10 +42,18 @@ ...@@ -42,10 +42,18 @@
#include <pcbnew.h> #include <pcbnew.h>
#include <pcbnew_id.h> #include <pcbnew_id.h>
#include <class_board.h> #include <class_board.h>
#include <class_track.h>
#include <class_module.h>
#include <class_drawsegment.h>
#include <collectors.h> #include <collectors.h>
#include <class_drawpanel.h> #include <class_drawpanel.h>
#include <class_drawpanel_gal.h>
#include <view/view.h>
#include <math/vector2d.h> #include <math/vector2d.h>
#ifdef KICAD_GAL
#include <pcb_painter.h>
#endif
// Configuration entry names. // Configuration entry names.
...@@ -77,6 +85,27 @@ BEGIN_EVENT_TABLE( PCB_BASE_FRAME, EDA_DRAW_FRAME ) ...@@ -77,6 +85,27 @@ BEGIN_EVENT_TABLE( PCB_BASE_FRAME, EDA_DRAW_FRAME )
END_EVENT_TABLE() 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, PCB_BASE_FRAME::PCB_BASE_FRAME( wxWindow* aParent, ID_DRAWFRAME_TYPE aFrameType,
const wxString& aTitle, const wxString& aTitle,
const wxPoint& aPos, const wxSize& aSize, const wxPoint& aPos, const wxSize& aSize,
...@@ -101,6 +130,12 @@ PCB_BASE_FRAME::PCB_BASE_FRAME( wxWindow* aParent, ID_DRAWFRAME_TYPE aFrameType, ...@@ -101,6 +130,12 @@ PCB_BASE_FRAME::PCB_BASE_FRAME( wxWindow* aParent, ID_DRAWFRAME_TYPE aFrameType,
m_FastGrid1 = 0; m_FastGrid1 = 0;
m_FastGrid2 = 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; m_auxiliaryToolBar = NULL;
} }
...@@ -110,6 +145,9 @@ PCB_BASE_FRAME::~PCB_BASE_FRAME() ...@@ -110,6 +145,9 @@ PCB_BASE_FRAME::~PCB_BASE_FRAME()
delete m_Collector; delete m_Collector;
delete m_Pcb; // is already NULL for FOOTPRINT_EDIT_FRAME 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 ) ...@@ -117,6 +155,78 @@ void PCB_BASE_FRAME::SetBoard( BOARD* aBoard )
{ {
delete m_Pcb; delete m_Pcb;
m_Pcb = aBoard; 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
} }
......
...@@ -132,3 +132,11 @@ std::string BOARD_ITEM::FormatInternalUnits( const wxSize& aSize ) ...@@ -132,3 +132,11 @@ std::string BOARD_ITEM::FormatInternalUnits( const wxSize& aSize )
{ {
return FormatInternalUnits( aSize.GetWidth() ) + " " + FormatInternalUnits( aSize.GetHeight() ); return FormatInternalUnits( aSize.GetWidth() ) + " " + FormatInternalUnits( aSize.GetHeight() );
} }
void BOARD_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const
{
// Basic fallback
aCount = 1;
aLayers[0] = m_Layer;
}
...@@ -850,6 +850,24 @@ EDA_ITEM* D_PAD::Clone() const ...@@ -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) #if defined(DEBUG)
void D_PAD::Show( int nestLevel, std::ostream& os ) const void D_PAD::Show( int nestLevel, std::ostream& os ) const
......
...@@ -395,6 +395,9 @@ public: ...@@ -395,6 +395,9 @@ public:
EDA_ITEM* Clone() const; EDA_ITEM* Clone() const;
/// @copydoc VIEW_ITEM::ViewGetLayers()
virtual void ViewGetLayers( int aLayers[], int& aCount ) const;
#if defined(DEBUG) #if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const; // overload void Show( int nestLevel, std::ostream& os ) const; // overload
#endif #endif
......
...@@ -488,6 +488,23 @@ EDA_ITEM* TEXTE_MODULE::Clone() const ...@@ -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) #if defined(DEBUG)
void TEXTE_MODULE::Show( int nestLevel, std::ostream& os ) const void TEXTE_MODULE::Show( int nestLevel, std::ostream& os ) const
......
...@@ -156,6 +156,9 @@ public: ...@@ -156,6 +156,9 @@ public:
EDA_ITEM* Clone() const; EDA_ITEM* Clone() const;
/// @copydoc VIEW_ITEM::ViewGetLayers()
virtual void ViewGetLayers( int aLayers[], int& aCount ) const;
#if defined(DEBUG) #if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const; // overload void Show( int nestLevel, std::ostream& os ) const; // overload
#endif #endif
......
...@@ -958,6 +958,25 @@ void SEGVIA::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode, ...@@ -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 // see class_track.h
void TRACK::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) void TRACK::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
{ {
......
...@@ -414,6 +414,9 @@ public: ...@@ -414,6 +414,9 @@ public:
EDA_ITEM* Clone() const; EDA_ITEM* Clone() const;
/// @copydoc VIEW_ITEM::ViewGetLayers()
virtual void ViewGetLayers( int aLayers[], int& aCount ) const;
#if defined (DEBUG) #if defined (DEBUG)
void Show( int nestLevel, std::ostream& os ) const; // overload void Show( int nestLevel, std::ostream& os ) const; // overload
#endif #endif
......
...@@ -82,6 +82,9 @@ static EDA_HOTKEY HkResetLocalCoord( wxT( "Reset Local Coordinates" ), ...@@ -82,6 +82,9 @@ static EDA_HOTKEY HkResetLocalCoord( wxT( "Reset Local Coordinates" ),
HK_RESET_LOCAL_COORD, ' ' ); HK_RESET_LOCAL_COORD, ' ' );
static EDA_HOTKEY HkSwitchHighContrastMode( wxT("Switch Highcontrast mode"), static EDA_HOTKEY HkSwitchHighContrastMode( wxT("Switch Highcontrast mode"),
HK_SWITCH_HIGHCONTRAST_MODE,'H'); HK_SWITCH_HIGHCONTRAST_MODE,'H');
#ifdef KICAD_GAL
static EDA_HOTKEY HkSwitchCanvas( wxT( "Switch canvas" ), HK_SWITCH_CANVAS, WXK_F12 );
#endif
/* Fit on Screen */ /* Fit on Screen */
#if !defined( __WXMAC__ ) #if !defined( __WXMAC__ )
static EDA_HOTKEY HkZoomAuto( wxT( "Zoom Auto" ), HK_ZOOM_AUTO, WXK_HOME ); static EDA_HOTKEY HkZoomAuto( wxT( "Zoom Auto" ), HK_ZOOM_AUTO, WXK_HOME );
...@@ -228,6 +231,9 @@ EDA_HOTKEY* board_edit_Hotkey_List[] = ...@@ -228,6 +231,9 @@ EDA_HOTKEY* board_edit_Hotkey_List[] =
&HkRecordMacros6, &HkCallMacros6, &HkRecordMacros7, &HkCallMacros7, &HkRecordMacros6, &HkCallMacros6, &HkRecordMacros7, &HkCallMacros7,
&HkRecordMacros8, &HkCallMacros8, &HkRecordMacros9, &HkCallMacros9, &HkRecordMacros8, &HkCallMacros8, &HkRecordMacros9, &HkCallMacros9,
&HkSwitchHighContrastMode, &HkSwitchHighContrastMode,
#ifdef KICAD_GAL
&HkSwitchCanvas,
#endif
NULL NULL
}; };
......
...@@ -82,6 +82,9 @@ enum hotkey_id_commnand { ...@@ -82,6 +82,9 @@ enum hotkey_id_commnand {
HK_CALL_MACROS_9, HK_CALL_MACROS_9,
HK_MACRO_ID_END, HK_MACRO_ID_END,
HK_SWITCH_HIGHCONTRAST_MODE, HK_SWITCH_HIGHCONTRAST_MODE,
#ifdef KICAD_GAL
HK_SWITCH_CANVAS,
#endif
}; };
// Full list of hotkey descriptors for board editor and footprint editor // Full list of hotkey descriptors for board editor and footprint editor
......
...@@ -348,6 +348,17 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() ...@@ -348,6 +348,17 @@ void PCB_EDIT_FRAME::ReCreateMenuBar()
_( "&List Nets" ), _( "View a list of nets with names and id's" ), _( "&List Nets" ), _( "View a list of nets with names and id's" ),
KiBitmap( tools_xpm ) ); 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 **/ /** Create Place Menu **/
wxMenu* placeMenu = new wxMenu; wxMenu* placeMenu = new wxMenu;
......
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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 <class_track.h>
#include <class_module.h>
#include <class_pad.h>
#include <class_drawsegment.h>
#include <class_zone.h>
#include <class_pcb_text.h>
#include <class_colors_design_settings.h>
#include <class_marker_pcb.h>
#include <class_dimension.h>
#include <eda_text.h>
#include <view/view.h>
#include <pcb_painter.h>
#include <gal/graphics_abstraction_layer.h>
#include <gal/stroke_font.h>
#include <newstroke_font.h>
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<VECTOR2D> 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<CPolyPt>::iterator polyIterator;
std::vector<CPolyPt> polyPoints = aContainer->GetFilledPolysList();
std::deque<VECTOR2D> 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();
}
}
}
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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 <layers_id_colors_and_visibility.h>
#include <painter.h>
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<PCB_RENDER_SETTINGS*> ( 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 */
#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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment