// -*- mode:C++ ; compile-command: "g++ -DHAVE_CONFIG_H -I. -I.. -I../include -I../../giac/include -g -c Graph.cc -DHAVE_CONFIG_H -DIN_GIAC" -*-
#include "Graph.h"
/*
* Copyright (C) 2000,2014 B. Parisse, Institut Fourier, 38402 St Martin d'Heres
*
* 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 3 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, see .
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_LIBFLTK
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "vector.h"
#include
#include
#include
#include // for nanosleep
#include
#include
#include // auto-recovery function
#ifdef HAVE_SYS_TIME_H
#include
#endif
#include "path.h"
#ifndef IN_GIAC
#include
#else
#include "plot.h"
#endif
#include "Equation.h"
#include "Editeur.h"
#include "Xcas1.h"
#include "Print.h"
#include "Graph3d.h"
#include
#include "Tableur.h"
#ifdef HAVE_UNISTD_H
#include
#endif
using namespace std;
using namespace giac;
#ifndef NO_NAMESPACE_XCAS
namespace xcas {
#endif // ndef NO_NAMESPACE_XCAS
bool do_helpon=true;
std::map *> texture2d_cache;
static double pow10(double d){
return std::pow(10.,d);
}
void get_texture2d(const string & s,std::pair * & texture){
texture=0;
std::map *>::const_iterator it,itend=texture2d_cache.end();
it=texture2d_cache.find(s);
if (it!=itend){
texture=it->second;
// texture->uncache();
}
else {
Fl_Shared_Image * ptr =Fl_Shared_Image::get(s.c_str());
if (ptr)
ptr->reload();
texture=new std::pair(ptr,0);
texture2d_cache[s]=texture;
}
}
void xcas_color(int color,bool dim3){
if (color>=0x100 && color<0x17e){
int r,g,b;
arc_en_ciel(color-(0x100),r,g,b);
#ifdef HAVE_LIBFLTK_GL
if (dim3)
glColor3f(r/255.,g/255.,b/255.);
else
#endif
fl_color(r,g,b);
}
else {
if (color>=512){
int r=8*((color>>11)&0x1f);
int g=4*((color>>5) &0x3f);
int b=8*(color & 0x1f);
#ifdef HAVE_LIBFLTK_GL
if (dim3)
glColor3f(r/255.,g/255.,b/255.);
else
#endif
fl_color(r,g,b);
}
else {
if (dim3)
gl_color(color);
else
fl_color(color);
}
}
}
vector animations;
inline int Min(int i,int j) {return i>j?j:i;}
inline int Max(int i,int j) {return i>j?i:j;}
quaternion_double::quaternion_double(double theta_x,double theta_y,double theta_z) {
*this=euler_deg_to_quaternion_double(theta_x,theta_y,theta_z);
}
quaternion_double euler_deg_to_quaternion_double(double a,double b,double c){
double phi=a*M_PI/180, theta=b*M_PI/180, psi=c*M_PI/180;
double c1 = std::cos(phi/2);
double s1 = std::sin(phi/2);
double c2 = std::cos(theta/2);
double s2 = std::sin(theta/2);
double c3 = std::cos(psi/2);
double s3 = std::sin(psi/2);
double c1c2 = c1*c2;
double s1s2 = s1*s2;
double w =c1c2*c3 - s1s2*s3;
double x =c1c2*s3 + s1s2*c3;
double y =s1*c2*c3 + c1*s2*s3;
double z =c1*s2*c3 - s1*c2*s3;
return quaternion_double(w,x,y,z);
}
void quaternion_double_to_euler_deg(const quaternion_double & q,double & phi,double & theta, double & psi){
double test = q.x*q.y + q.z*q.w;
if (test > 0.499) { // singularity at north pole
phi = 2 * atan2(q.x,q.w) * 180/M_PI;
theta = 90;
psi = 0;
return;
}
if (test < -0.499) { // singularity at south pole
phi = -2 * atan2(q.x,q.w) * 180/M_PI;
theta = - 90;
psi = 0;
return;
}
double sqx = q.x*q.x;
double sqy = q.y*q.y;
double sqz = q.z*q.z;
phi = atan2(2*q.y*q.w-2*q.x*q.z , 1 - 2*sqy - 2*sqz) * 180/M_PI;
theta = asin(2*test) * 180/M_PI;
psi = atan2(2*q.x*q.w-2*q.y*q.z , 1 - 2*sqx - 2*sqz) * 180/M_PI;
}
quaternion_double operator * (const quaternion_double & q1,const quaternion_double & q2){
double z=q1.w*q2.z+q2.w*q1.z+q1.x*q2.y-q2.x*q1.y;
double x=q1.w*q2.x+q2.w*q1.x+q1.y*q2.z-q2.y*q1.z;
double y=q1.w*q2.y+q2.w*q1.y+q1.z*q2.x-q2.z*q1.x;
double w=q1.w*q2.w-q1.x*q2.x-q1.y*q2.y-q1.z*q2.z;
return quaternion_double(w,x,y,z);
}
// q must be a unit
void get_axis_angle_deg(const quaternion_double & q,double &x,double &y,double & z, double &theta){
double scale=1-q.w*q.w;
if (scale>1e-6){
scale=std::sqrt(scale);
theta=2*std::acos(q.w)*180/M_PI;
x=q.x/scale;
y=q.y/scale;
z=q.z/scale;
}
else {
x=0; y=0; z=1;
theta=0;
}
}
ostream & operator << (ostream & os,const quaternion_double & q){
return os << q.w << "+" << q.x << "i+" << q.y << "j+" << q.z << "k";
}
bool readrgb(const string & s,int W,int H,gen & res){
Fl_Image * image=Fl_Shared_Image::get(s.c_str());
if (image && W && H)
image=image->copy(W,H);
if (!image || image->count()!=1)
return false;
const char * data = image->data()[0];
unsigned ih=image->h(),iw=image->w(),id=image->d();
gen tmpr=vecteur(ih),tmpg=vecteur(ih),tmpb=vecteur(ih),tmpa=vecteur(ih);
// alpha is returned before blue because red==1, green==2, blue==4
res=gen(makevecteur(makevecteur(4,int(ih),int(iw)),tmpr,tmpg,tmpa,tmpb),_RGBA__VECT);
vecteur & vr=*tmpr._VECTptr;
vecteur & vg=*tmpg._VECTptr;
vecteur & vb=*tmpb._VECTptr;
vecteur & va=*tmpa._VECTptr;
unsigned l=0;
for (unsigned i=0;iparent();
if (!g) return;
int i=g->find(b); // position of button inside group of buttons
g = g->parent(); // should be mouse_param_group
if (!g) return;
if (Mouse_Position * mp=dynamic_cast(g->child(0))){
Graph3d * g3=dynamic_cast(mp->graphic);
double dh=(mp->graphic->window_ymax-mp->graphic->window_ymin)/10;
double dw=(mp->graphic->window_xmax-mp->graphic->window_xmin)/10;
double dz=(mp->graphic->window_zmax-mp->graphic->window_zmin)/10;
switch (i){
case 0:
mp->graphic->zoom(0.707);
break;
case 1:
mp->graphic->up(dh);
break;
case 2:
if (g3)
mp->graphic->up_z(dz);
else {
mp->graphic->zoomy(0.707);
mp->graphic->push_cfg();
}
break;
case 3:
mp->graphic->left(dw);
break;
case 4:
if (g3){
g3->below_depth_hidden=!g3->below_depth_hidden;
g3->redraw();
}
else
mp->graphic->orthonormalize();
break;
case 5:
mp->graphic->right(dw);
break;
case 6:
mp->graphic->zoom(1.414);
break;
case 7:
mp->graphic->down(dh);
break;
case 8:
if (g3)
mp->graphic->down_z(dz);
else {
mp->graphic->zoomy(1.414);
mp->graphic->push_cfg();
}
break;
case 9:
mp->graphic->move_cfg(-1);
break;
case 10:
mp->graphic->move_cfg(1);
break;
case 11:
mp->graphic->config();
break;
case 12:
mp->graphic->paused=!mp->graphic->paused;
break;
case 13:
mp->graphic->autoscale(false);
break;
}
if (Fl_Widget * g=dynamic_cast(mp->graphic))
g->redraw();
}
}
void Graph2d3d::update_infos(const gen & g,GIAC_CONTEXT){
if (g.type==_VECT && g.subtype==_GRAPH__VECT){
show_axes=false;
orthonormalize();
}
if (g.is_symb_of_sommet(at_equal)){
// detect a title or a x/y-axis name
gen & f = g._SYMBptr->feuille;
if (f.type==_VECT && f._VECTptr->size()==2){
gen & optname = f._VECTptr->front();
gen & optvalue= f._VECTptr->back();
if (optname==at_legende && optvalue.type==_VECT){
vecteur & optv=(*optvalue._VECTptr);
int optvs=optv.size();
if (optvs>=1)
x_axis_unit=printstring(optv[0],contextptr);
if (optvs>=2)
y_axis_unit=printstring(optv[1],contextptr);
if (optvs>=3)
z_axis_unit=printstring(optv[2],contextptr);
}
if (optname.type==_INT_ && optname.subtype == _INT_PLOT){
if (optname.val==_GL_TEXTURE){
if (optvalue.type==_VECT && optvalue._VECTptr->size()==2 && optvalue._VECTptr->front().type==_STRNG && is_undef(optvalue._VECTptr->back())){
// reload cached image
optvalue=optvalue._VECTptr->front();
std::map *>::iterator it,itend=texture2d_cache.end();
it=texture2d_cache.find(optvalue._STRNGptr->c_str());
if (it!=itend){
std::pair * old= it->second;
delete old;
texture2d_cache.erase(it);
}
get_texture2d(*optvalue._STRNGptr,background_image);
}
else {
if (optvalue.type==_STRNG){
get_texture2d(*optvalue._STRNGptr,background_image);
}
else {
background_image=0;
}
}
}
if (optname.val==_TITLE )
title=printstring(optvalue,contextptr);
if (optname.val==_AXES){
if (optvalue.type==_INT_)
show_axes=optvalue.val;
}
if (optname.val==_LABELS && optvalue.type==_VECT){
vecteur & optv=(*optvalue._VECTptr);
int optvs=optv.size();
if (optvs>=1)
x_axis_name=printstring(optv[0],contextptr);
if (optvs>=2)
y_axis_name=printstring(optv[1],contextptr);
if (optvs>=3)
z_axis_name=printstring(optv[2],contextptr);
}
if (optname.val==_GL_ORTHO && optvalue==1)
orthonormalize();
if (optname.val==_GL_X_AXIS_COLOR && optvalue.type==_INT_)
x_axis_color=optvalue.val;
if (optname.val==_GL_Y_AXIS_COLOR && optvalue.type==_INT_)
y_axis_color=optvalue.val;
if (optname.val==_GL_Z_AXIS_COLOR && optvalue.type==_INT_)
z_axis_color=optvalue.val;
if (optname.val>=_GL_X && optname.val<=_GL_Z && optvalue.is_symb_of_sommet(at_interval)){
gen optvf=evalf_double(optvalue._SYMBptr->feuille,1,contextptr);
if (optvf.type==_VECT && optvf._VECTptr->size()==2){
gen a=optvf._VECTptr->front();
gen b=optvf._VECTptr->back();
if (a.type==_DOUBLE_ && b.type==_DOUBLE_){
switch (optname.val){
case _GL_X:
window_xmin=a._DOUBLE_val;
window_xmax=b._DOUBLE_val;
break;
case _GL_Y:
window_ymin=a._DOUBLE_val;
window_ymax=b._DOUBLE_val;
break;
case _GL_Z:
window_zmin=a._DOUBLE_val;
window_zmax=b._DOUBLE_val;
break;
}
}
}
}
gen optvalf=evalf_double(optvalue,1,contextptr);
if (optname.val==_GL_XTICK && optvalf.type==_DOUBLE_)
x_tick=optvalf._DOUBLE_val;
if (optname.val==_GL_YTICK && optvalf.type==_DOUBLE_)
y_tick=optvalf._DOUBLE_val;
if (optname.val==_GL_ZTICK && optvalf.type==_DOUBLE_)
z_tick=optvalf._DOUBLE_val;
if (optname.val==_GL_ANIMATE && optvalf.type==_DOUBLE_)
animation_dt=optvalf._DOUBLE_val;
if (optname.val==_GL_SHOWAXES && optvalue.type==_INT_)
show_axes=optvalue.val;
if (optname.val==_GL_SHOWNAMES && optvalue.type==_INT_)
show_names=optvalue.val;
if (optname.val>=_GL_X_AXIS_NAME && optname.val<=_GL_Z_AXIS_UNIT && optvalue.type==_STRNG){
if (optname.val==_GL_X_AXIS_NAME) x_axis_name=*optvalue._STRNGptr;
if (optname.val==_GL_Y_AXIS_NAME) y_axis_name=*optvalue._STRNGptr;
if (optname.val==_GL_Z_AXIS_NAME) z_axis_name=*optvalue._STRNGptr;
if (optname.val==_GL_X_AXIS_UNIT) x_axis_unit=*optvalue._STRNGptr;
if (optname.val==_GL_Y_AXIS_UNIT) y_axis_unit=*optvalue._STRNGptr;
if (optname.val==_GL_Z_AXIS_UNIT) z_axis_unit=*optvalue._STRNGptr;
}
if (optname.val==_GL_QUATERNION && optvalf.type==_VECT && optvalf._VECTptr->size()==4){
vecteur & optvalv=*optvalf._VECTptr;
if (optvalv[0].type==_DOUBLE_ && optvalv[1].type==_DOUBLE_ &&
optvalv[2].type==_DOUBLE_ && optvalv[3].type==_DOUBLE_){
q.x=optvalv[0]._DOUBLE_val;
q.y=optvalv[1]._DOUBLE_val;
q.z=optvalv[2]._DOUBLE_val;
q.w=optvalv[3]._DOUBLE_val;
}
}
if (optname.val==_GL_LOGX && optvalue.type==_INT_){
display_mode &= (0xffff ^ 0x400);
if (optvalue.val)
display_mode |= 0x400;
}
if (optname.val==_GL_LOGY && optvalue.type==_INT_){
display_mode &= (0xffff ^ 0x800);
if (optvalue.val)
display_mode |= 0x800;
}
if (optname.val==_GL_LOGZ && optvalue.type==_INT_){
display_mode &= (0xffff ^ 0x1000);
if (optvalue.val)
display_mode |= 0x1000;
}
if (dynamic_cast(this)){
if (optname.val==_GL_ROTATION_AXIS && optvalf.type==_VECT && optvalf._VECTptr->size()==3){
vecteur & optvalv=*optvalf._VECTptr;
if (optvalv[0].type==_DOUBLE_ && optvalv[1].type==_DOUBLE_ &&
optvalv[2].type==_DOUBLE_ ){
rotanim_rx=optvalv[0]._DOUBLE_val;
rotanim_ry=optvalv[1]._DOUBLE_val;
rotanim_rz=optvalv[2]._DOUBLE_val;
}
}
if (optname.val==_GL_FLAT && optvalue.type==_INT_){
display_mode &= (0xffff ^ 0x10);
if (optvalue.val)
display_mode |= 0x10;
}
if (optname.val==_GL_LIGHT && optvalue.type==_INT_){
display_mode &= (0xffff ^ 0x8);
if (optvalue.val)
display_mode |= 0x8;
}
if (optname.val==_GL_PERSPECTIVE && optvalue.type==_INT_){
display_mode &= (0xffff ^ 0x4);
if (!optvalue.val)
display_mode |= 0x4;
}
// GL_LIGHT_MODEL_COLOR_CONTROL=GL_SEPARATE_SPECULAR_COLOR || GL_SINGLE_COLOR
#ifndef WIN32
if (optname.val==_GL_LIGHT_MODEL_COLOR_CONTROL && optvalue.type==_INT_)
glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,optvalue.val);
/* GL_LIGHT_MODEL_LOCAL_VIEWER=floating-point value that spec-
ifies how specular reflection angles are computed. If params
is 0 (or 0.0), specular reflection angles take the view
direction to be parallel to and in the direction of the -z
axis, regardless of the location of the vertex in eye coordi-
nates. Otherwise, specular reflections are computed from the
origin of the eye coordinate system. The initial value is 0. */
if (optname.val==_GL_LIGHT_MODEL_LOCAL_VIEWER){
if (optvalf.type==_DOUBLE_)
glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER,optvalf._DOUBLE_val);
}
#endif
#ifdef HAVE_LIBFLTK_GL
/* GL_LIGHT_MODEL_TWO_SIDE = true /false */
if (optname.val==_GL_LIGHT_MODEL_TWO_SIDE && optvalue.type==_INT_){
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,optvalue.val);
}
/* GL_LIGHT_MODEL_AMBIENT=[r,g,b,a] */
if (optname.val==_GL_LIGHT_MODEL_AMBIENT && optvalf.type==_VECT && optvalf._VECTptr->size()==4){
vecteur & w=*optvalf._VECTptr;
GLfloat tab[4]={(GLfloat)w[0]._DOUBLE_val,(GLfloat)w[1]._DOUBLE_val,(GLfloat)w[2]._DOUBLE_val,(GLfloat)w[3]._DOUBLE_val};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT,tab);
}
// gl_blend=[d,s]
// habituellement gl_blend=[gl_src_alpha,gl_one_minus_src_alpha]
if (optname.val==_GL_BLEND){
if (is_zero(optvalue)){
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
}
else {
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
if (optvalue.type==_VECT && optvalue._VECTptr->size()==2)
glBlendFunc(optvalue._VECTptr->front().val,optvalue._VECTptr->back().val);
if (is_minus_one(optvalue))
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
}
}
#endif
// gl_light0=[option1=value1,...]
if (optname.val>=_GL_LIGHT0 && optname.val<=_GL_LIGHT7 && optvalue.type==_VECT){
int j=optname.val-_GL_LIGHT0;
// reset light0+j
light_x[j]=0;light_y[j]=0;light_z[j]=0;light_w[j]=1;
float di=j?0:1;
light_diffuse_r[j]=di;light_diffuse_g[j]=di;light_diffuse_b[j]=di;light_diffuse_a[j]=di;
light_specular_r[j]=di;light_specular_g[j]=di;light_specular_b[j]=di;light_specular_a[j]=di;
light_ambient_r[j]=0;light_ambient_g[j]=0;light_ambient_b[j]=0;light_ambient_a[j]=1;
light_spot_x[j]=0;light_spot_y[j]=0;light_spot_z[j]=-1;light_spot_w[j]=0;
light_spot_exponent[j]=0;light_spot_cutoff[j]=180;
light_0[j]=1;light_1[j]=0;light_2[j]=0;
vecteur & optv=*optvalue._VECTptr;
for (unsigned i=0;ifeuille.type==_VECT && g._SYMBptr->feuille._VECTptr->size()==2){
gen & optgname = optg._SYMBptr->feuille._VECTptr->front();
gen optgval = evalf_double(optg._SYMBptr->feuille._VECTptr->back(),1,contextptr);
bool vect4=optgval.type==_VECT && optgval._VECTptr->size()==4;
vecteur xyzw;
if (vect4)
xyzw=*optgval._VECTptr;
switch (optgname.val){
case _GL_AMBIENT:
light_ambient_r[j]=xyzw[0]._DOUBLE_val;
light_ambient_g[j]=xyzw[1]._DOUBLE_val;
light_ambient_b[j]=xyzw[2]._DOUBLE_val;
light_ambient_a[j]=xyzw[3]._DOUBLE_val;
break;
case _GL_SPECULAR:
light_specular_r[j]=xyzw[0]._DOUBLE_val;
light_specular_g[j]=xyzw[1]._DOUBLE_val;
light_specular_b[j]=xyzw[2]._DOUBLE_val;
light_specular_a[j]=xyzw[3]._DOUBLE_val;
break;
case _GL_DIFFUSE:
light_diffuse_r[j]=xyzw[0]._DOUBLE_val;
light_diffuse_g[j]=xyzw[1]._DOUBLE_val;
light_diffuse_b[j]=xyzw[2]._DOUBLE_val;
light_diffuse_a[j]=xyzw[3]._DOUBLE_val;
break;
case _GL_POSITION:
light_x[j]=xyzw[0]._DOUBLE_val;
light_y[j]=xyzw[1]._DOUBLE_val;
light_z[j]=xyzw[2]._DOUBLE_val;
light_w[j]=xyzw[3]._DOUBLE_val;
break;
case _GL_SPOT_DIRECTION:
light_spot_x[j]=xyzw[0]._DOUBLE_val;
light_spot_y[j]=xyzw[1]._DOUBLE_val;
light_spot_z[j]=xyzw[2]._DOUBLE_val;
light_spot_w[j]=xyzw[3]._DOUBLE_val;
break;
case _GL_SPOT_EXPONENT:
light_spot_exponent[j]=optgval._DOUBLE_val;
break;
case _GL_SPOT_CUTOFF:
light_spot_cutoff[j]=optgval._DOUBLE_val;
break;
case _GL_CONSTANT_ATTENUATION:
light_0[j]=optgval._DOUBLE_val;
break;
case _GL_LINEAR_ATTENUATION:
light_1[j]=optgval._DOUBLE_val;
break;
case _GL_QUADRATIC_ATTENUATION:
light_2[j]=optgval._DOUBLE_val;
break;
}
}
;
} // end for i
}
} // end opengl options
}
}
}
if (g.type==_VECT){
const_iterateur it=g._VECTptr->begin(),itend=g._VECTptr->end();
for (;it!=itend;++it)
update_infos(*it,contextptr);
}
}
void Graph2d3d::move_cfg(int i){
if (history.empty()) return;
int j=i+history_pos;
int s=history.size();
if (j>s) j=s;
if (j<1) j=1;
history_pos=j;
window_xyz & h = history[j-1];
window_xmin=h.xmin;
window_xmax=h.xmax;
window_ymin=h.ymin;
window_ymax=h.ymax;
window_zmin=h.zmin;
window_zmax=h.zmax;
}
void Graph2d3d::push_cfg(){
int s=history.size();
if (history_pos=0){
history.erase(history.begin()+history_pos,history.end());
}
history.push_back(window_xyz(window_xmin,window_xmax,window_ymin,window_ymax,window_zmin,window_zmax));
history_pos=history.size();
}
void Graph2d3d::clear_cfg(){
history_pos=0;
history.clear();
}
void Graph2d3d::find_xyz(double i,double j,double k,double &x,double&y,double &z){
x=i; y=j; z=k;
}
void Graph2d::find_xy(double i,double j,double & x,double & y) const {
x=window_xmin+i*(window_xmax-window_xmin)/(w()-(show_axes?ylegende*labelsize():0));
y=window_ymax-j*(window_ymax-window_ymin)/(h()-(show_axes?((title.empty()?1:2)*labelsize()):0));
}
void Graph2d::find_xyz(double i,double j,double k,double & x,double & y,double & z) {
z=k;
find_xy(i,j,x,y);
}
Graph2d3d * in_find_graph2d3d(Fl_Widget * wid){
if (Graph2d3d * g=dynamic_cast(wid))
return g;
if (Fl_Group * gr =dynamic_cast(wid)){
// search in children
int n=gr->children();
for (int i=0;ichild(i)))
return res;
}
}
return 0;
}
Graph2d3d * find_graph2d3d(Fl_Widget * wid){
if (!wid) return 0;
if (Graph2d3d * res = dynamic_cast(Fl::focus()) )
return res;
if (Graph2d3d * res=in_find_graph2d3d(wid))
return res;
return find_graph2d3d(wid->parent());
}
static void cb_Graph2d3d_LaTeX_Preview(Fl_Menu_* m , void*) {
const char * filename=find_graph2d3d(m)->latex(0);
if (filename)
xdvi(filename);
}
static void cb_Graph2d3d_LaTeX_Print(Fl_Menu_* m , void*) {
const char * filename=find_graph2d3d(m)->latex(0);
if (filename)
dvips(filename);
}
static void cb_Graph2d3d_Print(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
widget_print(gr);
}
static void cb_Graph2d3d_Preview(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
widget_ps_print(gr,gr->title,true);
}
static void cb_Graph3dpng(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (Graph3d * gr3 = dynamic_cast(gr)){
char * filename = file_chooser(gettext("Export to PNG file"),"*.png","session.png");
if(!filename) return;
gr3->opengl2png(filename);
}
}
static void cb_Graph2d3d_Autoscale(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
gr->autoscale(false);
}
static void cb_Graph2d3d_AutoscaleFull(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
gr->autoscale(true);
}
static void cb_Graph2d3d_Orthonormalize(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
gr->orthonormalize();
}
static void cb_Graph2d3d_Next(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
gr->move_cfg(1);
}
static void cb_Graph2d3d_Previous(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
gr->move_cfg(-1);
}
static void cb_Graph2d3d_Zoomout(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
gr->zoom(1.414);
}
static void cb_Graph2d3d_Zoomin(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
gr->zoom(0.707);
}
static void cb_Graph2d3d_Config(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
gr->config();
}
static void cb_Graph2d3d_Pause(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
gr->paused=true;
}
static void cb_Graph2d3d_Stop(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr){
gr->animation_dt=0;
gr->animation_instructions_pos=0;
}
}
static void cb_Graph2d3d_Restart(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr)
gr->paused=false;
}
static void cb_Graph2d3d_Faster(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr){
if (gr->animation_dt)
gr->animation_dt /= 2;
else
gr->animation_dt = 0.2;
}
}
static void cb_Graph2d3d_Slower(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr){
if (gr->animation_dt)
gr->animation_dt *= 2;
else
gr->animation_dt = 0.2;
}
}
static void cb_Graph2d3d_hide(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (Graph3d * gr3 = dynamic_cast(gr)){
gr3->below_depth_hidden=true;
gr3->redraw();
}
}
static void cb_Graph2d3d_show(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (Graph3d * gr3 = dynamic_cast(gr)){
gr3->below_depth_hidden=false;
gr3->redraw();
}
}
static void cb_Graph2d3d_startview(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (Graph3d * gr3 = dynamic_cast(gr)){
gr3->theta_x=-13;
gr3->theta_y=-95;
gr3->theta_z=-110;
gr3->q=euler_deg_to_quaternion_double(gr3->theta_z,gr3->theta_x,gr3->theta_y);
gr3->redraw();
}
}
static void cb_Graph2d3d_xview(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (Graph3d * gr3 = dynamic_cast(gr)){
gr3->theta_x=0;
gr3->theta_y=-90;
gr3->theta_z=-90;
gr3->q=euler_deg_to_quaternion_double(gr3->theta_z,gr3->theta_x,gr3->theta_y);
gr3->redraw();
}
}
static void cb_Graph2d3d_yview(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (Graph3d * gr3 = dynamic_cast(gr)){
gr3->theta_x=0;
gr3->theta_y=-90;
gr3->theta_z=0;
gr3->q=euler_deg_to_quaternion_double(gr3->theta_z,gr3->theta_x,gr3->theta_y);
gr3->redraw();
}
}
static void cb_Graph2d3d_zview(Fl_Menu_* m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (Graph3d * gr3 = dynamic_cast(gr)){
gr3->theta_x=0;
gr3->theta_y=0;
gr3->theta_z=0;
gr3->q=euler_deg_to_quaternion_double(gr3->theta_z,gr3->theta_x,gr3->theta_y);
gr3->redraw();
}
}
static void cb_Graph2d3d_mouse_plan(Fl_Menu_* m , void*) {
if (!Fl::focus())
return;
Graph2d3d * gr = find_graph2d3d(m);
if (Graph3d * gr3d = dynamic_cast(gr)){
double a,b,c;
gr3d->current_normal(a,b,c);
gr3d->normal2plan(a,b,c); // divides a,b,c by dx^2,...
double x0,y0,z0,t0;
gr3d->find_xyz(gr3d->x()+gr3d->w()/2,gr3d->y()+gr3d->h()/2,gr3d->depth,x0,y0,z0);
t0=a*x0+b*y0+c*z0;
if (std::abs(t0)window_zmax-gr3d->window_zmin)/1000)
t0=0;
string s="plan("+print_DOUBLE_(a)+"*x+"+print_DOUBLE_(b)+"*y+"+print_DOUBLE_(c)+"*z="+print_DOUBLE_(t0)+")";
in_Xcas_input_char(Fl::focus(),s,' ');
}
}
// image of (x,y,z) by rotation around axis r(rx,ry,rz) of angle theta
void rotate(double rx,double ry,double rz,double theta,double x,double y,double z,double & X,double & Y,double & Z){
/*
quaternion_double q=rotation_2_quaternion_double(rx,ry,rz,theta);
quaternion_double qx(x,y,z,0);
quaternion_double qX=conj(q)*qx*q;
*/
// r(rx,ry,rz) the axis, v(x,y,z) projects on w=a*r with a such that
// w.r=a*r.r=v.r
double r2=rx*rx+ry*ry+rz*rz;
double r=std::sqrt(r2);
double a=(rx*x+ry*y+rz*z)/r2;
// v=w+V, w remains stable, V=v-w=v-a*r rotates
// Rv=w+RV, where RV=cos(theta)*V+sin(theta)*(r cross V)/sqrt(r2)
double Vx=x-a*rx,Vy=y-a*ry,Vz=z-a*rz;
// cross product of k with V
double kVx=ry*Vz-rz*Vy, kVy=rz*Vx-rx*Vz,kVz=rx*Vy-ry*Vx;
double c=std::cos(theta),s=std::sin(theta);
X=a*rx+c*Vx+s*kVx/r;
Y=a*ry+c*Vy+s*kVy/r;
Z=a*rz+c*Vz+s*kVz/r;
}
// type -1 = view point rotation around an axis,
// type bit 0 to 7: spot rotation
// step is in degree; tstep in seconds
// [0,0,0],[rx,ry,rz] axis of the rotation
void oxyz_rotate(Graph2d3d* gr,int type,int nstep,double tstep,int danim,double rx,double ry,double rz){
static Fl_Window * w = 0;
static Fl_Button * button = 0;
if (!w){
Fl_Group::current(0);
w=new Fl_Window(100,24);
button = new Fl_Button(2,2,w->w()-4,w->h()-4);
button->label(gettext("Cancel"));
w->label(gettext("Rotate Animation"));
w->end();
}
if (Graph3d * gr3 = dynamic_cast(gr)){
w->set_modal();
w->show();
w->hotspot(w);
Fl::focus(button);
double step=2*M_PI/nstep,lx[8],ly[8],lz[8],lX,lY,lZ;
quaternion_double qsave(gr3->q);
for (int j=0;j<8;++j){
lx[j]=gr3->light_x[j];
ly[j]=gr3->light_y[j];
lz[j]=gr3->light_z[j];
}
for (int i=1;ilight_x[j]=lX;
gr3->light_y[j]=lY;
gr3->light_z[j]=lZ;
}
}
if (type & (1<<8))
gr3->q=qsave*rotation_2_quaternion_double(rx,ry,rz,theta*180/M_PI);
gr3->animation_instructions_pos+=danim;
gr3->redraw();
int nwait=int(tstep/0.001),jwait;
if (nwait<=0)
nwait=1;
for (jwait=0;jwaithide();
for (int j=0;j<8;++j){
gr3->light_x[j]=lx[j];
gr3->light_y[j]=ly[j];
gr3->light_z[j]=lz[j];
}
gr3->q=qsave;
gr3->animation_instructions_pos+=danim;
gr3->redraw();
}
}
static void cb_Graph2d3d_rotate(Fl_Menu_* m , void*) {
if (!Fl::focus())
return;
Graph2d3d * gr = find_graph2d3d(m);
// control window for args of oxyz_rotate
oxyz_rotate(gr,gr->rotanim_type,gr->rotanim_nstep,gr->rotanim_tstep,gr->rotanim_danim,gr->rotanim_rx,gr->rotanim_ry,gr->rotanim_rz);
}
static void cb_Graph_Traceclear(Fl_Widget * m , void*) {
Graph2d3d * gr = find_graph2d3d(m);
if (gr){
gr->trace_instructions.clear();
gr->redraw();
}
}
Figure * do_find_figure(Fl_Widget * widget){
Fl_Widget * gr = widget;
for (;gr;gr=gr->parent()){
Figure * f =dynamic_cast