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
option(KICAD_SCRIPTING_WXPYTHON
"set this option ON to build wxpython implementation for wx interface building in python and py.shell"
)
option(KICAD_GAL
"set this option ON to build KICAD using Graphics Abstraction Layer as a rendering backend"
)
# when option KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES is enabled:
# PYTHON_EXECUTABLE can be defined when invoking cmake
......@@ -189,6 +193,10 @@ if(USE_WX_GRAPHICS_CONTEXT)
add_definitions(-DUSE_WX_GRAPHICS_CONTEXT)
endif()
if(KICAD_GAL)
add_definitions(-DKICAD_GAL)
endif()
# Allow user to override the default settings for adding images to menu items. By default
# images in menu items are enabled on all platforms except OSX. This can be over ridden by
# defining -DUSE_IMAGES_IN_MENUS=ON/OFF to force the preferred behavior.
......@@ -257,6 +265,20 @@ add_definitions(-DWX_COMPATIBILITY)
find_package(OpenGL QUIET)
check_find_package_result(OPENGL_FOUND "OpenGL")
if(KICAD_GAL)
#####################
# Find GLEW library #
#####################
find_package(GLEW)
check_find_package_result(GLEW_FOUND "GLEW")
######################
# Find Cairo library #
######################
find_package(Cairo 1.8.1 QUIET)
check_find_package_result(CAIRO_FOUND "Cairo")
endif(KICAD_GAL)
######################
# Find Boost library #
######################
......
......@@ -4,12 +4,29 @@ include_directories(
./dialogs
./dialog_about
${Boost_INCLUDE_DIR}
${CAIRO_INCLUDE_DIR}
../3d-viewer
../pcbnew
../polygon
${INC_AFTER}
)
if(KICAD_GAL)
set(GAL_SRCS
drawpanel_gal.cpp
painter.cpp
gal/graphics_abstraction_layer.cpp
gal/stroke_font.cpp
gal/color4d.cpp
gal/opengl/opengl_gal.cpp
gal/opengl/shader.cpp
gal/cairo/cairo_gal.cpp
view/wx_view_controls.cpp
)
add_library(gal STATIC ${GAL_SRCS})
endif(KICAD_GAL)
set(COMMON_ABOUT_DLG_SRCS
dialog_about/AboutDialog_main.cpp
dialog_about/dialog_about.cpp
......@@ -82,6 +99,14 @@ set(COMMON_SRCS
zoom.cpp
)
if(KICAD_GAL)
set(COMMON_SRCS
${COMMON_SRCS}
view/view.cpp
view/view_item.cpp
)
endif(KICAD_GAL)
add_library(common STATIC ${COMMON_SRCS})
set(PCB_COMMON_SRCS
......@@ -131,6 +156,12 @@ set(PCB_COMMON_SRCS
fp_lib_table.cpp
)
if(KICAD_GAL)
set(PCB_COMMON_SRCS
${PCB_COMMON_SRCS}
../pcbnew/pcb_painter.cpp
)
endif(KICAD_GAL)
# add -DPCBNEW to compilation of these PCBNEW sources
set_source_files_properties( ${PCB_COMMON_SRCS} PROPERTIES
......
......@@ -222,6 +222,22 @@ EDA_ITEM& EDA_ITEM::operator=( const EDA_ITEM& aItem )
}
const BOX2I EDA_ITEM::ViewBBox() const
{
// Basic fallback
return BOX2I( VECTOR2I( GetBoundingBox().GetOrigin() ),
VECTOR2I( GetBoundingBox().GetSize() ) );
}
void EDA_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const
{
// Basic fallback
aCount = 1;
aLayers[0] = 0;
}
#if defined(DEBUG)
// A function that should have been in wxWidgets
......
......@@ -35,6 +35,7 @@
#include <macros.h>
#include <id.h>
#include <class_drawpanel.h>
#include <class_drawpanel_gal.h>
#include <class_base_screen.h>
#include <msgpanel.h>
#include <wxstruct.h>
......@@ -66,6 +67,7 @@ BEGIN_EVENT_TABLE( EDA_DRAW_FRAME, EDA_BASE_FRAME )
EVT_MENU_OPEN( EDA_DRAW_FRAME::OnMenuOpen )
EVT_ACTIVATE( EDA_DRAW_FRAME::OnActivate )
EVT_MENU_RANGE( ID_ZOOM_IN, ID_ZOOM_REDRAW, EDA_DRAW_FRAME::OnZoom )
EVT_MENU( ID_SWITCH_CANVAS, EDA_DRAW_FRAME::OnZoom )
EVT_MENU_RANGE( ID_OFFCENTER_ZOOM_IN, ID_OFFCENTER_ZOOM_OUT, EDA_DRAW_FRAME::OnZoom )
EVT_MENU_RANGE( ID_POPUP_ZOOM_START_RANGE, ID_POPUP_ZOOM_END_RANGE,
EDA_DRAW_FRAME::OnZoom )
......@@ -100,6 +102,8 @@ EDA_DRAW_FRAME::EDA_DRAW_FRAME( wxWindow* aParent,
m_HotkeysZoomAndGridList = NULL;
m_canvas = NULL;
m_galCanvas = NULL;
m_galCanvasActive = false;
m_messagePanel = NULL;
m_currentScreen = NULL;
m_toolId = ID_NO_TOOL_SELECTED;
......@@ -937,3 +941,27 @@ void EDA_DRAW_FRAME::AdjustScrollBars( const wxPoint& aCenterPositionIU )
screen->m_ScrollbarPos.x,
screen->m_ScrollbarPos.y, noRefresh );
}
void EDA_DRAW_FRAME::UseGalCanvas( bool aEnable )
{
if( !( aEnable ^ m_galCanvasActive ) )
return;
if( aEnable )
{
m_canvas->Hide();
m_galCanvas->Show();
m_galCanvas->Raise();
m_galCanvas->Refresh();
}
else
{
m_galCanvas->Hide();
m_canvas->Show();
m_canvas->Raise();
m_canvas->Refresh();
}
m_galCanvasActive = aEnable;
}
......@@ -34,6 +34,7 @@
#include <macros.h>
#include <id.h>
#include <class_drawpanel.h>
#include <class_drawpanel_gal.h>
#include <class_base_screen.h>
#include <wxstruct.h>
......@@ -74,6 +75,9 @@ BEGIN_EVENT_TABLE( EDA_DRAW_PANEL, wxScrolledWindow )
EVT_ERASE_BACKGROUND( EDA_DRAW_PANEL::OnEraseBackground )
EVT_SCROLLWIN( EDA_DRAW_PANEL::OnScroll )
EVT_ACTIVATE( EDA_DRAW_PANEL::OnActivate )
#ifdef KICAD_GAL
EVT_SIZE( EDA_DRAW_PANEL::OnSize )
#endif
EVT_MENU_RANGE( ID_PAN_UP, ID_PAN_RIGHT, EDA_DRAW_PANEL::OnPan )
END_EVENT_TABLE()
......@@ -1377,6 +1381,18 @@ void EDA_DRAW_PANEL::OnPan( wxCommandEvent& event )
}
#ifdef KICAD_GAL
void EDA_DRAW_PANEL::OnSize( wxSizeEvent& SizeEv )
{
if( GetParent()->GetGalCanvas() != NULL )
{
GetParent()->GetGalCanvas()->SetPosition( GetPosition() );
GetParent()->GetGalCanvas()->SetSize( GetSize() );
}
}
#endif
void EDA_DRAW_PANEL::EndMouseCapture( int id, int cursor, const wxString& title,
bool aCallEndFunc )
{
......
/*
* 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 @@
#include <hotkeys_basic.h>
#include <menus_helpers.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 )
......@@ -82,7 +86,10 @@ void EDA_DRAW_FRAME::Zoom_Automatique( bool aWarpPointer )
if( screen->m_FirstRedraw )
screen->SetCrossHairPosition( screen->GetScrollCenterPosition() );
RedrawScreen( screen->GetScrollCenterPosition(), aWarpPointer );
if( !m_galCanvasActive )
RedrawScreen( screen->GetScrollCenterPosition(), aWarpPointer );
else
m_canvas->Hide();
}
......@@ -160,6 +167,36 @@ void EDA_DRAW_FRAME::OnZoom( wxCommandEvent& event )
m_canvas->Refresh();
break;
#ifdef KICAD_GAL
// Switch canvas between standard and GAL-based
case ID_SWITCH_CANVAS:
{
UseGalCanvas( !m_galCanvasActive );
KiGfx::VIEW* view = m_galCanvas->GetView();
KiGfx::GAL* gal = m_galCanvas->GetGAL();
double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor();
// Display the same view after canvas switching
if( m_galCanvasActive )
{
double zoom = 1 / ( zoomFactor * m_canvas->GetZoom() );
view->SetScale( zoom );
view->SetCenter( VECTOR2D( m_canvas->GetScreenCenterLogicalPosition() ) );
}
else
{
double zoom = 1 / ( zoomFactor * view->GetScale() );
m_canvas->SetZoom( zoom );
VECTOR2D center = view->GetCenter();
RedrawScreen( wxPoint( center.x, center.y ), false );
}
}
break;
#endif
case ID_POPUP_ZOOM_CENTER:
center = screen->GetCrossHairPosition();
RedrawScreen( center, true );
......
......@@ -104,6 +104,13 @@ target_link_libraries(cvpcb
${GDI_PLUS_LIBRARIES}
)
if(KICAD_GAL)
target_link_libraries(cvpcb
gal
${GLEW_LIBRARIES}
)
endif(KICAD_GAL)
###
# Add cvpcb as install target
###
......
......@@ -35,6 +35,7 @@
#include <colors.h>
#include <bitmaps.h>
#include <richio.h>
#include <view/view_item.h>
#include <boost/ptr_container/ptr_vector.hpp>
......@@ -389,7 +390,11 @@ public:
* is a base class for most all the KiCad significant classes, used in
* schematics and boards.
*/
#ifdef KICAD_GAL
class EDA_ITEM : public KiGfx::VIEW_ITEM
#else
class EDA_ITEM
#endif
{
private:
......@@ -740,6 +745,12 @@ public:
*/
virtual EDA_ITEM& operator=( const EDA_ITEM& aItem );
/// @copydoc VIEW_ITEM::ViewBBox()
virtual const BOX2I ViewBBox() const;
/// @copydoc VIEW_ITEM::ViewGetLayers()
virtual void ViewGetLayers( int aLayers[], int& aCount ) const;
#if defined(DEBUG)
/**
......
......@@ -248,6 +248,9 @@ public:
static std::string FormatInternalUnits( const wxPoint& aPoint );
static std::string FormatInternalUnits( const wxSize& aSize );
/// @copydoc VIEW_ITEM::ViewGetLayers()
virtual void ViewGetLayers( int aLayers[], int& aCount ) const;
};
#endif /* BOARD_ITEM_STRUCT_H */
......@@ -258,6 +258,9 @@ public:
void OnCharHook( wxKeyEvent& event );
void OnPan( wxCommandEvent& event );
#ifdef KICAD_GAL
void OnSize( wxSizeEvent& event );
#endif
void EraseScreen( wxDC* DC );
void OnScrollWin( wxCommandEvent& event );
......
/*
* 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
ID_ZOOM_OUT,
ID_ZOOM_PAGE,
ID_ZOOM_REDRAW,
ID_SWITCH_CANVAS,
/* Panning command event IDs. */
ID_PAN_UP,
......
......@@ -143,9 +143,19 @@ enum PCB_VISIBLE
MOD_VALUES_VISIBLE, ///< show modules values (when texts are visibles)
MOD_REFERENCES_VISIBLE, ///< show modules references (when texts are visibles)
TRACKS_VISIBLE,
PADS_VISIBLE,
VIA_HOLES_VISIBLE,
PAD_HOLES_VISIBLE,
END_PCB_VISIBLE_LIST // sentinel
};
/// macro for obtaining layer number for specific item (eg. pad or text)
#define ITEM_GAL_LAYER(layer) (LAYER_COUNT + layer)
/// number of *all* layers including PCB and item layers
#define TOTAL_LAYER_COUNT (LAYER_COUNT + END_PCB_VISIBLE_LIST)
/**
* Function IsValidLayerIndex
......
/*
* 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 @@
class EDA_ITEM;
class EDA_RECT;
class EDA_DRAW_PANEL;
class EDA_DRAW_PANEL_GAL;
class EDA_MSG_PANEL;
class BASE_SCREEN;
class PARAM_CFG_BASE;
......@@ -391,11 +392,15 @@ protected:
EDA_HOTKEY_CONFIG* m_HotkeysZoomAndGridList;
int m_LastGridSizeId;
bool m_DrawGrid; // hide/Show grid
bool m_galCanvasActive; // whether to use new GAL engine
EDA_COLOR_T m_GridColor; // Grid color
/// The area to draw on.
EDA_DRAW_PANEL* m_canvas;
/// New type of area (GAL-based) to draw on.
EDA_DRAW_PANEL_GAL* m_galCanvas;
/// Tool ID of previously active draw tool bar button.
int m_lastDrawToolId;
......@@ -904,6 +909,30 @@ public:
*/
wxString LengthDoubleToString( double aValue, bool aConvertToMils = false ) const;
/**
* Function UseGalCanvas
* used to switch between standard and GAL-based canvas.
*
* @param aEnable True for GAL-based canvas, false for standard canvas.
*/
void UseGalCanvas( bool aEnable );
/**
* Function IsNewCanvasActive
* is used to check which canvas (GAL-based or standard) is currently in use.
*
* @return True for GAL-based canvas, false for standard canvas.
*/
bool IsGalCanvasActive() { return m_galCanvasActive; }
/**
* Function GetCalCanvas
* returns a pointer to GAL-based canvas of given EDA draw frame.
*
* @return Pointer to GAL-based canvas.
*/
EDA_DRAW_PANEL_GAL* GetGalCanvas() { return m_galCanvas; }
DECLARE_EVENT_TABLE()
};
......
......@@ -426,6 +426,13 @@ target_link_libraries(pcbnew
${PCBNEW_EXTRA_LIBS}
)
if(KICAD_GAL)
target_link_libraries(pcbnew
gal
${GLEW_LIBRARIES}
)
endif(KICAD_GAL)
###
# Add pcbnew as install target
###
......
......@@ -42,10 +42,18 @@
#include <pcbnew.h>
#include <pcbnew_id.h>
#include <class_board.h>
#include <class_track.h>
#include <class_module.h>
#include <class_drawsegment.h>
#include <collectors.h>
#include <class_drawpanel.h>
#include <class_drawpanel_gal.h>
#include <view/view.h>
#include <math/vector2d.h>
#ifdef KICAD_GAL
#include <pcb_painter.h>
#endif
// Configuration entry names.
......@@ -77,6 +85,27 @@ BEGIN_EVENT_TABLE( PCB_BASE_FRAME, EDA_DRAW_FRAME )
END_EVENT_TABLE()
/// Rendering order of layers on GAL-based canvas (lower index in the array
/// means that layer is displayed closer to the user, ie. on the top).
const int m_galLayerOrder[] =
{
DRAW_N, COMMENT_N, ECO1_N, ECO2_N, EDGE_N,
UNUSED_LAYER_29, UNUSED_LAYER_30, UNUSED_LAYER_31,
ITEM_GAL_LAYER( MOD_TEXT_FR_VISIBLE ),
SILKSCREEN_N_FRONT, SOLDERPASTE_N_FRONT, ADHESIVE_N_FRONT, SOLDERMASK_N_FRONT,
ITEM_GAL_LAYER( VIA_HOLES_VISIBLE ), ITEM_GAL_LAYER( PAD_HOLES_VISIBLE ),
ITEM_GAL_LAYER( VIAS_VISIBLE ), ITEM_GAL_LAYER( PADS_VISIBLE ),
LAYER_N_FRONT, LAYER_N_15, LAYER_N_14, LAYER_N_13, LAYER_N_12, LAYER_N_11,
LAYER_N_10, LAYER_N_9, LAYER_N_8, LAYER_N_7, LAYER_N_6, LAYER_N_5, LAYER_N_4,
LAYER_N_3, LAYER_N_2, LAYER_N_BACK,
SOLDERMASK_N_BACK, ADHESIVE_N_BACK, SOLDERPASTE_N_BACK, SILKSCREEN_N_BACK,
ITEM_GAL_LAYER( MOD_TEXT_BK_VISIBLE )
};
PCB_BASE_FRAME::PCB_BASE_FRAME( wxWindow* aParent, ID_DRAWFRAME_TYPE aFrameType,
const wxString& aTitle,
const wxPoint& aPos, const wxSize& aSize,
......@@ -101,6 +130,12 @@ PCB_BASE_FRAME::PCB_BASE_FRAME( wxWindow* aParent, ID_DRAWFRAME_TYPE aFrameType,
m_FastGrid1 = 0;
m_FastGrid2 = 0;
#ifdef KICAD_GAL
m_galCanvas = new EDA_DRAW_PANEL_GAL( this, -1, wxPoint( 0, 0 ), m_FrameSize,
EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL );
m_galCanvas->Hide();
#endif /* KICAD_GAL */
m_auxiliaryToolBar = NULL;
}
......@@ -110,6 +145,9 @@ PCB_BASE_FRAME::~PCB_BASE_FRAME()
delete m_Collector;
delete m_Pcb; // is already NULL for FOOTPRINT_EDIT_FRAME
#ifdef KICAD_GAL
delete m_galCanvas;
#endif /* KICAD_GAL */
}
......@@ -117,6 +155,78 @@ void PCB_BASE_FRAME::SetBoard( BOARD* aBoard )
{
delete m_Pcb;
m_Pcb = aBoard;
#ifdef KICAD_GAL
if( m_galCanvas )
{
KiGfx::VIEW* view = m_galCanvas->GetView();
view->Clear();
// All of PCB drawing elements should be added to the VIEW
// in order to be displayed
// Load zones
for( int i = 0; i < m_Pcb->GetAreaCount(); ++i )
{
view->Add( (KiGfx::VIEW_ITEM*) ( m_Pcb->GetArea( i ) ) );
}
// Load drawings
for( BOARD_ITEM* drawing = m_Pcb->m_Drawings; drawing; drawing = drawing->Next() )
{
view->Add( drawing );
}
// Load tracks
for( TRACK* track = m_Pcb->m_Track; track; track = track->Next() )
{
view->Add( track );
}
// Load modules and its additional elements
for( MODULE* module = m_Pcb->m_Modules; module; module = module->Next() )
{
// Load module's pads
for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() )
{
view->Add( pad );
}
// Load module's drawing (mostly silkscreen)
for( BOARD_ITEM* drawing = module->GraphicalItems().GetFirst(); drawing;
drawing = drawing->Next() )
{
view->Add( drawing );
}
// Load module's texts (name and value)
view->Add( &module->Reference() );
view->Add( &module->Value() );
}
// Segzones (equivalent of ZONE_CONTAINER for legacy boards)
for( SEGZONE* zone = m_Pcb->m_Zone; zone; zone = zone->Next() )
{
view->Add( zone );
}
// Apply layer coloring scheme
if( view->GetPainter() )
{
KiGfx::PCB_RENDER_SETTINGS* colorSettings = new KiGfx::PCB_RENDER_SETTINGS();
// Load layers' colors from PCB data
colorSettings->ImportLegacyColors( m_Pcb->GetColorsSettings() );
view->GetPainter()->ApplySettings( colorSettings );
}
// Set rendering order of layers (check m_galLayerOrder to see the order)
for( unsigned int i = 0; i < sizeof( m_galLayerOrder ) / sizeof( int ); ++i )
{
view->SetLayerOrder( m_galLayerOrder[i], i );
}
}
#endif
}
......
......@@ -132,3 +132,11 @@ std::string BOARD_ITEM::FormatInternalUnits( const wxSize& aSize )
{
return FormatInternalUnits( aSize.GetWidth() ) + " " + FormatInternalUnits( aSize.GetHeight() );
}
void BOARD_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const
{
// Basic fallback
aCount = 1;
aLayers[0] = m_Layer;
}
......@@ -850,6 +850,24 @@ EDA_ITEM* D_PAD::Clone() const
}
void D_PAD::ViewGetLayers( int aLayers[], int& aCount ) const
{
if( m_Attribute == PAD_SMD || m_Attribute == PAD_CONN)
{
// Single layer pad (smd) without hole
aLayers[0] = GetParent()->GetLayer();
aCount = 1;
}
else
{
// Multi layer pad with hole - pad is shown on one common layer, hole on the other
aLayers[0] = ITEM_GAL_LAYER( PADS_VISIBLE );
aLayers[1] = ITEM_GAL_LAYER( PAD_HOLES_VISIBLE );
aCount = 2;
}
}
#if defined(DEBUG)
void D_PAD::Show( int nestLevel, std::ostream& os ) const
......
......@@ -395,6 +395,9 @@ public:
EDA_ITEM* Clone() const;
/// @copydoc VIEW_ITEM::ViewGetLayers()
virtual void ViewGetLayers( int aLayers[], int& aCount ) const;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const; // overload
#endif
......
......@@ -488,6 +488,23 @@ EDA_ITEM* TEXTE_MODULE::Clone() const
}
void TEXTE_MODULE::ViewGetLayers( int aLayers[], int& aCount ) const
{
switch( GetParent()->GetLayer() )
{
case LAYER_N_BACK:
aLayers[0] = ITEM_GAL_LAYER( MOD_TEXT_BK_VISIBLE ); // how about SILKSCREEN_N_BACK?
break;
case LAYER_N_FRONT:
aLayers[0] = ITEM_GAL_LAYER( MOD_TEXT_FR_VISIBLE ); // how about SILKSCREEN_N_FRONT?
break;
}
aCount = 1;
}
#if defined(DEBUG)
void TEXTE_MODULE::Show( int nestLevel, std::ostream& os ) const
......
......@@ -156,6 +156,9 @@ public:
EDA_ITEM* Clone() const;
/// @copydoc VIEW_ITEM::ViewGetLayers()
virtual void ViewGetLayers( int aLayers[], int& aCount ) const;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const; // overload
#endif
......
......@@ -958,6 +958,25 @@ void SEGVIA::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode,
}
void SEGVIA::ViewGetLayers( int aLayers[], int& aCount ) const
{
/*int top_layer, bottom_layer;
ReturnLayerPair( &top_layer, &bottom_layer );
// We can add vias to all layers they belong, but then they are drawn this many times
aCount = 0;
for( int i = bottom_layer; i <= top_layer; ++i )
{
aLayers[aCount++] = i;
}*/
// Just show it on common via & via holes layers
aLayers[0] = ITEM_GAL_LAYER( VIAS_VISIBLE );
aLayers[1] = ITEM_GAL_LAYER( VIA_HOLES_VISIBLE );
aCount = 2;
}
// see class_track.h
void TRACK::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
{
......
......@@ -414,6 +414,9 @@ public:
EDA_ITEM* Clone() const;
/// @copydoc VIEW_ITEM::ViewGetLayers()
virtual void ViewGetLayers( int aLayers[], int& aCount ) const;
#if defined (DEBUG)
void Show( int nestLevel, std::ostream& os ) const; // overload
#endif
......
......@@ -82,6 +82,9 @@ static EDA_HOTKEY HkResetLocalCoord( wxT( "Reset Local Coordinates" ),
HK_RESET_LOCAL_COORD, ' ' );
static EDA_HOTKEY HkSwitchHighContrastMode( wxT("Switch Highcontrast mode"),
HK_SWITCH_HIGHCONTRAST_MODE,'H');
#ifdef KICAD_GAL
static EDA_HOTKEY HkSwitchCanvas( wxT( "Switch canvas" ), HK_SWITCH_CANVAS, WXK_F12 );
#endif
/* Fit on Screen */
#if !defined( __WXMAC__ )
static EDA_HOTKEY HkZoomAuto( wxT( "Zoom Auto" ), HK_ZOOM_AUTO, WXK_HOME );
......@@ -228,6 +231,9 @@ EDA_HOTKEY* board_edit_Hotkey_List[] =
&HkRecordMacros6, &HkCallMacros6, &HkRecordMacros7, &HkCallMacros7,
&HkRecordMacros8, &HkCallMacros8, &HkRecordMacros9, &HkCallMacros9,
&HkSwitchHighContrastMode,
#ifdef KICAD_GAL
&HkSwitchCanvas,
#endif
NULL
};
......
......@@ -82,6 +82,9 @@ enum hotkey_id_commnand {
HK_CALL_MACROS_9,
HK_MACRO_ID_END,
HK_SWITCH_HIGHCONTRAST_MODE,
#ifdef KICAD_GAL
HK_SWITCH_CANVAS,
#endif
};
// Full list of hotkey descriptors for board editor and footprint editor
......
......@@ -348,6 +348,17 @@ void PCB_EDIT_FRAME::ReCreateMenuBar()
_( "&List Nets" ), _( "View a list of nets with names and id's" ),
KiBitmap( tools_xpm ) );
#ifdef KICAD_GAL
// Switching GAL-based canvas on/off
viewMenu->AppendSeparator();
text = AddHotkeyName( _( "&Switch canvas" ), g_Pcbnew_Editor_Hokeys_Descr, HK_SWITCH_CANVAS, IS_ACCELERATOR );
AddMenuItem( viewMenu, ID_SWITCH_CANVAS,
text, _( "Switch the canvas implementation between old (XOR-based) and new (GAL-based)" ),
KiBitmap( tools_xpm ) );
#endif
/** Create Place Menu **/
wxMenu* placeMenu = new wxMenu;
......
/*
* 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