///////////////////////////////////////////////////////////////////////////// // 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(); } }