kicad/3d-viewer/3d_canvas.cpp

640 lines
15 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: 3d_canvas.cpp
/////////////////////////////////////////////////////////////////////////////
#ifdef __GNUG__
#pragma implementation
#pragma interface
#endif
#include "fctsys.h"
#include "trigo.h"
#include "wx/image.h"
#if !wxUSE_GLCANVAS
#error Please set wxUSE_GLCANVAS to 1 in setup.h.
#endif
#include "wx/dataobj.h"
#include "wx/clipbrd.h"
#include "fctsys.h"
#include "common.h"
#include "id.h"
#include "3d_viewer.h"
#include "trackball.h"
/* Tool and button Bitmaps */
#define XPM_3D_MAIN
#include "bitmaps3d.h"
#include "bitmaps.h"
enum onrclick_id
{
ID_POPUP_3D_VIEW_START = 2000,
ID_POPUP_ZOOMIN,
ID_POPUP_ZOOMOUT,
ID_POPUP_VIEW_XPOS,
ID_POPUP_VIEW_XNEG,
ID_POPUP_VIEW_YPOS,
ID_POPUP_VIEW_YNEG,
ID_POPUP_VIEW_ZPOS,
ID_POPUP_VIEW_ZNEG,
ID_POPUP_MOVE3D_LEFT,
ID_POPUP_MOVE3D_RIGHT,
ID_POPUP_MOVE3D_UP,
ID_POPUP_MOVE3D_DOWN,
ID_POPUP_3D_VIEW_END
};
/*
* Pcb3D_GLCanvas implementation
*/
BEGIN_EVENT_TABLE(Pcb3D_GLCanvas, wxGLCanvas)
EVT_SIZE(Pcb3D_GLCanvas::OnSize)
EVT_PAINT(Pcb3D_GLCanvas::OnPaint)
EVT_CHAR(Pcb3D_GLCanvas::OnChar)
EVT_MOUSE_EVENTS(Pcb3D_GLCanvas::OnMouseEvent)
EVT_ERASE_BACKGROUND(Pcb3D_GLCanvas::OnEraseBackground)
EVT_MENU_RANGE(ID_POPUP_3D_VIEW_START, ID_POPUP_3D_VIEW_END,
Pcb3D_GLCanvas::OnPopUpMenu)
END_EVENT_TABLE()
/*************************************************************************/
Pcb3D_GLCanvas::Pcb3D_GLCanvas(WinEDA3D_DrawFrame *parent, wxWindowID id,
int* gl_attrib):
wxGLCanvas(parent, id, wxPoint(-1,-1), wxSize(-1,-1), 0, wxT("Pcb3D_glcanvas"), gl_attrib)
/*************************************************************************/
{
m_init = FALSE;
m_gllist = 0;
m_Parent = parent;
DisplayStatus();
}
/*************************************/
Pcb3D_GLCanvas::~Pcb3D_GLCanvas(void)
/*************************************/
{
ClearLists();
}
/*************************************/
void Pcb3D_GLCanvas::ClearLists(void)
/*************************************/
{
if( m_gllist > 0 )
glDeleteLists(m_gllist, 1);
m_init = FALSE;
m_gllist = 0;
}
/*********************************************/
void Pcb3D_GLCanvas::OnChar(wxKeyEvent& event)
/*********************************************/
{
SetView3D(event.GetKeyCode());
event.Skip();
}
/*********************************************/
void Pcb3D_GLCanvas::SetView3D(int keycode)
/*********************************************/
{
int ii;
double delta_move = 0.7 * g_Parm_3D_Visu.m_Zoom;
switch(keycode)
{
case WXK_LEFT:
g_Draw3d_dx -= delta_move;
break;
case WXK_RIGHT:
g_Draw3d_dx += delta_move;
break;
case WXK_UP:
g_Draw3d_dy += delta_move;
break;
case WXK_DOWN:
g_Draw3d_dy -= delta_move;
break;
case WXK_HOME:
g_Parm_3D_Visu.m_Zoom = 1.0;
g_Draw3d_dx = g_Draw3d_dy = 0;
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
break;
case WXK_END:
break;
case WXK_F1:
g_Parm_3D_Visu.m_Zoom /= 1.4;
if ( g_Parm_3D_Visu.m_Zoom <= 0.01)
g_Parm_3D_Visu.m_Zoom = 0.01;
break;
case WXK_F2:
g_Parm_3D_Visu.m_Zoom *= 1.4;
break;
case '+':
break;
case '-':
break;
case 'r':
case 'R':
g_Draw3d_dx = g_Draw3d_dy = 0;
for ( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball(g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
break;
case 'x':
for ( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball(g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
g_Parm_3D_Visu.m_ROTZ = -90;
g_Parm_3D_Visu.m_ROTX = -90;
break;
case 'X':
for ( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball(g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
g_Parm_3D_Visu.m_ROTZ = 90;
g_Parm_3D_Visu.m_ROTX = -90;
break;
case 'y':
for ( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball(g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
g_Parm_3D_Visu.m_ROTX = -90;
break;
case 'Y':
for ( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
g_Parm_3D_Visu.m_ROTX = -90;
g_Parm_3D_Visu.m_ROTZ = -180;
break;
case 'z':
for ( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball(g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
break;
case 'Z':
for ( ii = 0; ii < 4; ii++ )
g_Parm_3D_Visu.m_Rot[ii] = 0.0;
trackball(g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
g_Parm_3D_Visu.m_ROTX = -180;
break;
default:
return;
}
DisplayStatus();
Refresh(FALSE);
}
/********************************************************/
void Pcb3D_GLCanvas::OnMouseEvent(wxMouseEvent& event)
/********************************************************/
{
wxSize size(GetClientSize());
float spin_quat[4];
if( event.RightDown() )
{
OnRightClick(event); return;
}
if ( event.m_wheelRotation )
{
if( event.ShiftDown() ) {
if ( event.GetWheelRotation() < 0 ) {
/* up */
SetView3D(WXK_UP);
} else {
/* down */
SetView3D(WXK_DOWN);
}
} else if( event.ControlDown() ) {
if ( event.GetWheelRotation() > 0 ) {
/* right */
SetView3D(WXK_RIGHT);
} else {
/* left */
SetView3D(WXK_LEFT);
}
}
else {
if ( event.GetWheelRotation() > 0 )
{
g_Parm_3D_Visu.m_Zoom /= 1.4;
if ( g_Parm_3D_Visu.m_Zoom <= 0.01)
g_Parm_3D_Visu.m_Zoom = 0.01;
}
else g_Parm_3D_Visu.m_Zoom *= 1.4;
DisplayStatus();
Refresh(FALSE);
}
}
if (event.Dragging())
{
/* drag in progress, simulate trackball */
trackball(spin_quat,
(2.0*g_Parm_3D_Visu.m_Beginx - size.x) / size.x,
(size.y - 2.0*g_Parm_3D_Visu.m_Beginy) / size.y,
( 2.0*event.GetX() - size.x) / size.x,
( size.y - 2.0*event.GetY()) / size.y);
add_quats( spin_quat, g_Parm_3D_Visu.m_Quat, g_Parm_3D_Visu.m_Quat );
/* orientation has changed, redraw mesh */
DisplayStatus();
Refresh(FALSE);
}
g_Parm_3D_Visu.m_Beginx = event.GetX();
g_Parm_3D_Visu.m_Beginy = event.GetY();
}
/*******************************************************/
void Pcb3D_GLCanvas::OnRightClick(wxMouseEvent& event)
/*******************************************************/
/* Construit et affiche un menu Popup lorsque on actionne le bouton droit
de la souris
*/
{
wxPoint pos;
wxMenu PopUpMenu;
pos.x = event.GetX(); pos.y = event.GetY();
wxMenuItem *item = new wxMenuItem(&PopUpMenu, ID_POPUP_ZOOMIN,
_("Zoom +"));
item->SetBitmap(zoom_in_xpm);
PopUpMenu.Append(item);
item = new wxMenuItem(&PopUpMenu, ID_POPUP_ZOOMOUT,
_("Zoom -"));
item->SetBitmap(zoom_out_xpm);
PopUpMenu.Append(item);
PopUpMenu.AppendSeparator();
item = new wxMenuItem(&PopUpMenu, ID_POPUP_VIEW_ZPOS,
_("Top View"));
item->SetBitmap(axis3d_top_xpm);
PopUpMenu.Append(item);
item = new wxMenuItem(&PopUpMenu, ID_POPUP_VIEW_ZNEG,
_("Bottom View"));
item->SetBitmap(axis3d_bottom_xpm);
PopUpMenu.Append(item);
PopUpMenu.AppendSeparator();
item = new wxMenuItem(&PopUpMenu, ID_POPUP_VIEW_XPOS,
_("Right View"));
item->SetBitmap(axis3d_right_xpm);
PopUpMenu.Append(item);
item = new wxMenuItem(&PopUpMenu, ID_POPUP_VIEW_XNEG,
_("Left View"));
item->SetBitmap(axis3d_left_xpm);
PopUpMenu.Append(item);
PopUpMenu.AppendSeparator();
item = new wxMenuItem(&PopUpMenu, ID_POPUP_VIEW_YPOS,
_("Front View"));
item->SetBitmap(axis3d_front_xpm);
PopUpMenu.Append(item);
item = new wxMenuItem(&PopUpMenu, ID_POPUP_VIEW_YNEG,
_("Back View"));
item->SetBitmap(axis3d_back_xpm);
PopUpMenu.Append(item);
PopUpMenu.AppendSeparator();
item = new wxMenuItem(&PopUpMenu, ID_POPUP_MOVE3D_LEFT,
_("Move left <-"));
item->SetBitmap(left_xpm);
PopUpMenu.Append(item);
item = new wxMenuItem(&PopUpMenu, ID_POPUP_MOVE3D_RIGHT,
_("Move right ->"));
item->SetBitmap(right_xpm);
PopUpMenu.Append(item);
item = new wxMenuItem(&PopUpMenu, ID_POPUP_MOVE3D_UP,
_("Move Up ^"));
item->SetBitmap(up_xpm);
PopUpMenu.Append(item);
item = new wxMenuItem(&PopUpMenu, ID_POPUP_MOVE3D_DOWN,
_("Move Down"));
item->SetBitmap(down_xpm);
PopUpMenu.Append(item);
PopupMenu( &PopUpMenu, pos);
}
/*******************************************************/
void Pcb3D_GLCanvas::OnPopUpMenu(wxCommandEvent & event)
/*******************************************************/
{
int key = 0;
switch( event.GetId() )
{
case ID_POPUP_ZOOMIN:
key = WXK_F1;
break;
case ID_POPUP_ZOOMOUT:
key = WXK_F2;
break;
case ID_POPUP_VIEW_XPOS:
key = 'x';
break;
case ID_POPUP_VIEW_XNEG:
key = 'X';
break;
case ID_POPUP_VIEW_YPOS:
key = 'y';
break;
case ID_POPUP_VIEW_YNEG:
key = 'Y';
break;
case ID_POPUP_VIEW_ZPOS:
key = 'z';
break;
case ID_POPUP_VIEW_ZNEG:
key = 'Z';
break;
case ID_POPUP_MOVE3D_LEFT:
key = WXK_LEFT;
break;
case ID_POPUP_MOVE3D_RIGHT:
key = WXK_RIGHT;
break;
case ID_POPUP_MOVE3D_UP:
key = WXK_UP;
break;
case ID_POPUP_MOVE3D_DOWN:
key = WXK_DOWN;
break;
default:
return;
}
SetView3D(key);
}
/***************************************/
void Pcb3D_GLCanvas::DisplayStatus(void)
/***************************************/
{
wxString msg;
msg.Printf(wxT("dx %3.2f"), g_Draw3d_dx);
m_Parent->SetStatusText(msg,1);
msg.Printf(wxT("dy %3.2f"), g_Draw3d_dy);
m_Parent->SetStatusText(msg,2);
msg.Printf(wxT("View: %3.1f"), 45 * g_Parm_3D_Visu.m_Zoom);
m_Parent->SetStatusText(msg,3);
}
/*************************************************/
void Pcb3D_GLCanvas::OnPaint( wxPaintEvent& event )
/*************************************************/
{
wxPaintDC dc(this);
#ifndef __WXMOTIF__
if (!GetContext()) return;
#endif
Redraw();
event.Skip();
}
/**********************************************/
void Pcb3D_GLCanvas::OnSize(wxSizeEvent& event)
/**********************************************/
{
// this is also necessary to update the context on some platforms
wxGLCanvas::OnSize(event);
// set GL viewport (not called by wxGLCanvas::OnSize on all platforms...)
int w, h;
GetClientSize(&w, &h);
#ifndef __WXMOTIF__
if (GetContext())
#endif
{
SetCurrent();
glViewport(0, 0, (GLint) w, (GLint) h);
}
}
/***********************************************************/
void Pcb3D_GLCanvas::OnEraseBackground(wxEraseEvent& event)
/***********************************************************/
{
// Do nothing, to avoid flashing.
}
/****************************/
void Pcb3D_GLCanvas::InitGL()
/****************************/
/* Int parametres generaux pour OPENGL
*/
{
wxSize size = GetClientSize();
if (! m_init)
{
m_init = TRUE;
g_Parm_3D_Visu.m_Zoom = 1.0;
ZBottom = 1.0; ZTop = 10.0;
}
SetCurrent();
/* set viewing projection */
double ratio_HV = (double) size.x / size.y; // Ratio largeur /hauteur de la fenetre d'affichage
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
#define MAX_VIEW_ANGLE 160.0/45.0
if ( g_Parm_3D_Visu.m_Zoom > MAX_VIEW_ANGLE)
g_Parm_3D_Visu.m_Zoom = MAX_VIEW_ANGLE;
gluPerspective( 45.0 * g_Parm_3D_Visu.m_Zoom, ratio_HV, 1 ,10 );
// glFrustum(-1., 1.1F, -1.1F, 1.1F, ZBottom, ZTop);
/* position viewer */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0F, 0.0F, - ( ZBottom +ZTop) / 2);
/* clear color and depth buffers */
glClearColor( g_Parm_3D_Visu.m_BgColor.m_Red,
g_Parm_3D_Visu.m_BgColor.m_Green,
g_Parm_3D_Visu.m_BgColor.m_Blue, 1 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
/* Setup light souces: */
SetLights();
glDisable(GL_CULL_FACE); // show back faces
glEnable(GL_DEPTH_TEST); // Enable z-buferring
glEnable(GL_LINE_SMOOTH);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
/* speedups */
glEnable(GL_DITHER);
glShadeModel(GL_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);
/* blend */
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
/***********************************/
void Pcb3D_GLCanvas::SetLights(void)
/***********************************/
/* Init sources lumineuses pour OPENGL
*/
{
double light;
GLfloat light_color[4];
SetCurrent();
/* set viewing projection */
light_color[3] = 1.0;
GLfloat Z_axis_pos[4] = { 0.0, 0.0, 3.0, 0.0 };
GLfloat lowZ_axis_pos[4] = { 0.0, 0.0, -3.0, 0.5 };
/* activate light */
light = 1.0;
light_color[0] = light_color[1] = light_color[2]= light;
glLightfv(GL_LIGHT0, GL_POSITION, Z_axis_pos);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color);
light = 0.3;
light_color[0] = light_color[1] = light_color[2] = light;
glLightfv(GL_LIGHT1, GL_POSITION, lowZ_axis_pos);
glLightfv(GL_LIGHT1, GL_DIFFUSE, light_color);
glEnable(GL_LIGHT0); // White spot on Z axis
glEnable(GL_LIGHT1); // White spot on Z axis ( bottom)
glEnable(GL_LIGHTING);
}
/**********************************************************/
void Pcb3D_GLCanvas::TakeScreenshot(wxCommandEvent & event)
/**********************************************************/
/* Create a Screenshot of the current 3D view.
Output file format is png or jpeg, or image is copied on clipboard
*/
{
wxString FullFileName;
wxString file_ext, mask;
bool fmt_is_jpeg = FALSE;
if ( event.GetId() == ID_MENU_SCREENCOPY_JPEG ) fmt_is_jpeg = TRUE;
if ( event.GetId() != ID_TOOL_SCREENCOPY_TOCLIBBOARD )
{
file_ext = fmt_is_jpeg ? wxT(".jpg") : wxT(".png";)
mask = wxT("*") + file_ext;
FullFileName = m_Parent->m_Parent->GetScreen()->m_FileName;
ChangeFileNameExt(FullFileName,file_ext);
FullFileName =
EDA_FileSelector( _("3D Image filename:"),
wxEmptyString, /* Chemin par defaut */
FullFileName, /* nom fichier par defaut */
file_ext, /* extension par defaut */
mask, /* Masque d'affichage */
this,
wxFD_SAVE,
TRUE
);
if ( FullFileName.IsEmpty() ) return;
}
Redraw(true);
wxSize image_size = GetClientSize();
wxClientDC dc(this);
wxBitmap bitmap(image_size.x, image_size.y );
wxMemoryDC memdc;
memdc.SelectObject( bitmap );
memdc.Blit(0, 0, image_size.x, image_size.y, &dc, 0, 0);
memdc.SelectObject( wxNullBitmap );
if ( event.GetId() == ID_TOOL_SCREENCOPY_TOCLIBBOARD )
{
wxBitmapDataObject *dobjBmp = new wxBitmapDataObject;
dobjBmp->SetBitmap(bitmap);
if (wxTheClipboard->Open())
{
if ( !wxTheClipboard->SetData(dobjBmp) )
wxLogError( _T("Failed to copy image to clipboard"));
wxTheClipboard->Flush(); /* the data on clipboard
will stay available after the application exits */
wxTheClipboard->Close();
}
}
else
{
wxImage image = bitmap.ConvertToImage();
if ( !image.SaveFile( FullFileName,
fmt_is_jpeg ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG))
wxLogError(wxT("Can't save file"));
image.Destroy();
}
}