Browse Source

Many things updated / fixed / improved. Including some metric output, support for MIL-DOT reticles, scope guides (indicates where +/-1 mil and +/-5 mil are in the plot window), plotting memory 3, computing the trajectory of different loads in the same barrel/scope alignment.

MikeNix
surkhe 7 months ago
parent
commit
3911e99509
  1. 49
      ChangeLog
  2. 1
      GBCSolution.cpp
  3. 75
      InputWindow.cpp
  4. 2
      InputWindow.h
  5. 807
      PlotWindow.cpp
  6. 15
      PlotWindow.h
  7. 605
      RangeWindow.cpp
  8. 13
      RangeWindow.h

49
ChangeLog

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
Oct 2010 mnix@wanm.com.au
* Added scope guides to the path plot as a ready reference to how the
trajectory compares to the field of view of the scope, in particular to
one and 5 MIL marks on a MIL-DOT reticle. Included code for MOA
as well, but commented it out pending GUI button to select
MIL/MOA/NONE
* Modified InputWindow and PlotWindow to handle plotting memory 3
* Consolidated a lot of code in PlotWindow into 3 new functions.
* modified InputWindow::cb_Solve() to treat zero ranges < 2 yards as a
zeroangle and report the zeroangle used in the output window.
This allows plotting the paths of different loads shot from the one
barrel and scope alignment (eg for a two different loads in a .308
without adjusting the scope, what is the elevation adjustment for
each load?) Useful for hunters who might select different loads for
different targets, but do not want to mess with their zero.
* Allowed for larger y_range in plots.
* Reduced the minimum range in the PlotWindow to 50 yds (default still
200). Useful for low power (ie .22) shooters.
* Added Drop (MIL), Windage (MIL) and Target Size (MIL) columns to the
RangeWindow (not all export formats have been updated yet).
All useful for users of MIL-DOT reticles.
Need to add a GUI box for entering the target size in inches - that
way you can enter the size in inches of whatever you intend to shoot,
then in the field measure your target in MIL in the scope, look that up
on the range table and instantly get bullet drop and windage in MIL,
no maths required.
* Added metric conversions of the summary data at the top of the range
table.
* Cleaned up SOME of the misuse of malloc/delete/free
delete calls the destructor then frees the memory. Calling free on
an object after deleting it is an error, so is calling delete after
calling the destructor!
Also mixing new/free and malloc/delete on the same object is bad.
Only use malloc with free, and only new with delete.
* Re-wrote the GenTable and Export* code in RangeWindow to be data-driven.
Adding a column, or modifying the layout of the load data is now
done in one place and automatically appears in ALL output formats.

1
GBCSolution.cpp

@ -32,6 +32,7 @@ GBCSolution::~GBCSolution(){ @@ -32,6 +32,7 @@ GBCSolution::~GBCSolution(){
if (this->sln != NULL) {
free(this->sln);
this->sln=NULL;
}
}

75
InputWindow.cpp

@ -61,7 +61,7 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){ @@ -61,7 +61,7 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){
inBC = new Fl_Float_Input(10,80,50,20," Drag Coefficient");
inWeight=new Fl_Int_Input(10,105,50,20," Projectile Weight (grains)");
inMV = new Fl_Int_Input(10,130,50,20," Initial Velocity (ft/s)");
inZero = new Fl_Int_Input(10,155,50,20," Zero Range (yds)");
inZero = new Fl_Float_Input(10,155,50,20," Zero Range (yds)");
inSH = new Fl_Float_Input(10,180,50,20," Sight Height Over Bore (in)");
inAngle = new Fl_Int_Input(10,205,50,20," Shooting Angle (deg)");
inVwind = new Fl_Int_Input(10,230,50,20," Wind Velocity (mi/hr)");
@ -158,8 +158,8 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){ @@ -158,8 +158,8 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){
Fl_Menu_Item m_Storage = {"&Solution Memory",0,0,0, FL_SUBMENU};
Fl_Menu_Item mc_Store1 = {"Store to Memory 1 ",0,(Fl_Callback*)cb_Store1,this};
Fl_Menu_Item mc_Store2 = {"Store to Memory 2 ",0,(Fl_Callback*)cb_Store2,this,FL_MENU_DIVIDER};
Fl_Menu_Item mc_Store3 = {"Store to Memory 3 ",0,(Fl_Callback*)cb_Store3,this};
Fl_Menu_Item mc_Store2 = {"Store to Memory 2 ",0,(Fl_Callback*)cb_Store2,this};
Fl_Menu_Item mc_Store3 = {"Store to Memory 3 ",0,(Fl_Callback*)cb_Store3,this,FL_MENU_DIVIDER};
Fl_Menu_Item mc_Store4 = {"Store to Memory 4 ",0,(Fl_Callback*)cb_Store4,this};
Fl_Menu_Item mc_Store5 = {"Store to Memory 5 ",0,(Fl_Callback*)cb_Store5,this};
Fl_Menu_Item mc_Store6 = {"Store to Memory 6 ",0,(Fl_Callback*)cb_Store6,this};
@ -242,7 +242,7 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){ @@ -242,7 +242,7 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){
InputWindow::~InputWindow(){
// Free any memory we are using.
if (Solution!=NULL) {free(Solution);Solution=NULL;}
if (Solution!=NULL) { free(Solution); Solution=NULL;}
if (Smem1!=NULL) { free(Smem1); Smem1=NULL; }
if (Smem2!=NULL) { free(Smem2); Smem2=NULL; }
if (Smem3!=NULL) { free(Smem3); Smem3=NULL; }
@ -252,15 +252,15 @@ InputWindow::~InputWindow(){ @@ -252,15 +252,15 @@ InputWindow::~InputWindow(){
if (Smem7!=NULL) { free(Smem7); Smem7=NULL; }
if (Smem8!=NULL) { free(Smem8); Smem8=NULL; }
if (gsln!=NULL) { gsln->~GBCSolution(); delete gsln; gsln=NULL;}
if (mem1!=NULL) { mem1->~GBCSolution(); delete mem1; mem1=NULL;}
if (mem2!=NULL) { mem2->~GBCSolution(); delete mem2; mem2=NULL;}
if (mem3!=NULL) { mem3->~GBCSolution(); delete mem3; mem3=NULL;}
if (mem4!=NULL) { mem4->~GBCSolution(); delete mem4; mem4=NULL;}
if (mem5!=NULL) { mem5->~GBCSolution(); delete mem5; mem5=NULL;}
if (mem6!=NULL) { mem6->~GBCSolution(); delete mem6; mem6=NULL;}
if (mem7!=NULL) { mem7->~GBCSolution(); delete mem7; mem7=NULL;}
if (mem8!=NULL) { mem8->~GBCSolution(); delete mem8; mem8=NULL;}
if (gsln!=NULL) { delete gsln; gsln=NULL;}
if (mem1!=NULL) { delete mem1; mem1=NULL;}
if (mem2!=NULL) { delete mem2; mem2=NULL;}
if (mem3!=NULL) { delete mem3; mem3=NULL;}
if (mem4!=NULL) { delete mem4; mem4=NULL;}
if (mem5!=NULL) { delete mem5; mem5=NULL;}
if (mem6!=NULL) { delete mem6; mem6=NULL;}
if (mem7!=NULL) { delete mem7; mem7=NULL;}
if (mem8!=NULL) { delete mem8; mem8=NULL;}
delete inG1;
delete inG2;
@ -359,8 +359,11 @@ void InputWindow::cb_Solve(Fl_Widget* o, void* vd) { @@ -359,8 +359,11 @@ void InputWindow::cb_Solve(Fl_Widget* o, void* vd) {
}
// Find the zero angle of the bore relative to the sighting system.
zeroangle=ZeroAngle(df,bc,v,sh,zero,0);
// if the zero distance <= 2 yards, it is a preset zero angle used to match the
// firing trajectory from another solution
if (zero <= 2.0) zeroangle=zero;
else zeroangle=ZeroAngle(df,bc,v,sh,zero,0);
// Generate a solution using the GNU Ballistics library call.
numRows = SolveAll(df,bc,v,sh,angle,zeroangle,windspeed,windangle,&(lSolution));
@ -403,7 +406,7 @@ void InputWindow::cb_Solve(Fl_Widget* o, void* vd) { @@ -403,7 +406,7 @@ void InputWindow::cb_Solve(Fl_Widget* o, void* vd) {
}
// Otherwise we assume success, and inform the user of our success.
sprintf(txt1,"GNU Ballistics Computer: Solution Valid.\nSolution's maximum valid range is %d yards\nUse the options in the 'Solution' menu to view results.",numRows-2);
sprintf(txt1,"GNU Ballistics Computer: Solution Valid.\nSolution's maximum valid range is %d yards\nBore angle is %0.5f above sights\nUse the options in the 'Solution' menu to view results.",numRows-2,zeroangle);
T->oStatus->value(txt1);
EnableMenu((InputWindow*) vd);
@ -415,10 +418,6 @@ void InputWindow::cb_Solve(Fl_Widget* o, void* vd) { @@ -415,10 +418,6 @@ void InputWindow::cb_Solve(Fl_Widget* o, void* vd) {
}
T->gsln=lsln;
}
return;
@ -511,15 +510,15 @@ void InputWindow::cb_Clear(Fl_Widget* o, void* v) { @@ -511,15 +510,15 @@ void InputWindow::cb_Clear(Fl_Widget* o, void* v) {
if (T->Smem7!=NULL) { free(T->Smem7); T->Smem7=NULL; }
if (T->Smem8!=NULL) { free(T->Smem8); T->Smem8=NULL; }
if (T->gsln!=NULL) { T->gsln->~GBCSolution(); delete T->gsln; T->gsln=NULL;}
if (T->mem1!=NULL) { T->mem1->~GBCSolution(); delete T->mem1; T->mem1=NULL;}
if (T->mem2!=NULL) { T->mem2->~GBCSolution(); delete T->mem2; T->mem2=NULL;}
if (T->mem3!=NULL) { T->mem3->~GBCSolution(); delete T->mem3; T->mem3=NULL;}
if (T->mem4!=NULL) { T->mem4->~GBCSolution(); delete T->mem4; T->mem4=NULL;}
if (T->mem5!=NULL) { T->mem5->~GBCSolution(); delete T->mem5; T->mem5=NULL;}
if (T->mem6!=NULL) { T->mem6->~GBCSolution(); delete T->mem6; T->mem6=NULL;}
if (T->mem7!=NULL) { T->mem7->~GBCSolution(); delete T->mem7; T->mem7=NULL;}
if (T->mem8!=NULL) { T->mem8->~GBCSolution(); delete T->mem8; T->mem8=NULL;}
if (T->gsln!=NULL) { /*T->gsln->~GBCSolution();*/ delete T->gsln; T->gsln=NULL;}
if (T->mem1!=NULL) { /*T->mem1->~GBCSolution();*/ delete T->mem1; T->mem1=NULL;}
if (T->mem2!=NULL) { /*T->mem2->~GBCSolution();*/ delete T->mem2; T->mem2=NULL;}
if (T->mem3!=NULL) { /*T->mem3->~GBCSolution();*/ delete T->mem3; T->mem3=NULL;}
if (T->mem4!=NULL) { /*T->mem4->~GBCSolution();*/ delete T->mem4; T->mem4=NULL;}
if (T->mem5!=NULL) { /*T->mem5->~GBCSolution();*/ delete T->mem5; T->mem5=NULL;}
if (T->mem6!=NULL) { /*T->mem6->~GBCSolution();*/ delete T->mem6; T->mem6=NULL;}
if (T->mem7!=NULL) { /*T->mem7->~GBCSolution();*/ delete T->mem7; T->mem7=NULL;}
if (T->mem8!=NULL) { /*T->mem8->~GBCSolution();*/ delete T->mem8; T->mem8=NULL;}
// Turn the memory labels back to black to show they are null.
@ -565,7 +564,7 @@ void InputWindow::cb_RangeTable(Fl_Widget*, void* v){ @@ -565,7 +564,7 @@ void InputWindow::cb_RangeTable(Fl_Widget*, void* v){
// Spawn a new RangeWindow using the current solution to populate it.
if (T->gsln != NULL && T->gsln->sln != NULL){
new RangeWindow(600,400,T->gsln);
new RangeWindow(760,520,T->gsln);
}
else {
T->oStatus->value("Error creating Range Window. Unknown Error.");
@ -686,23 +685,21 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){ @@ -686,23 +685,21 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){
// and open it for binary read.
FILE* ofile = fopen(fname,"rb");
// Free an old solution if there is one, because we want to
// write over it.
if (T->gsln != NULL) {
free(T->gsln);
delete(T->gsln);
T->gsln=NULL;
}
// Allocate some space for the loaded solution.
if (lsln != NULL) {
free(lsln);
delete(lsln);
lsln=NULL;
}
// Assign some new memory for lsln.
lsln = (GBCSolution*)malloc(sizeof(GBCSolution));
lsln = new GBCSolution; // (GBCSolution*)malloc(sizeof(GBCSolution));
if (lsln==NULL){
T->oStatus->value("GNU Ballistic Computer: Memory allocation error.");
@ -718,12 +715,12 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){ @@ -718,12 +715,12 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){
if (read!=sizeof(GBCSolution)){
T->oStatus->value("GNU Ballistic Computer: Error reading file data!\nPlease ensure the file is a valid GNUBC solution.");
if (lsln!=NULL) {
free(lsln);
delete(lsln);
lsln=NULL;
}
if (T->gsln!=NULL) {
free(T->gsln);
delete(T->gsln);
T->gsln=NULL;
}
@ -790,8 +787,9 @@ void InputWindow::cb_Plot(Fl_Widget* o, void* v){ @@ -790,8 +787,9 @@ void InputWindow::cb_Plot(Fl_Widget* o, void* v){
GBCSolution* sln = T->gsln;
GBCSolution* lmem1 = T->mem1;
GBCSolution* lmem2 = T->mem2;
GBCSolution* lmem3 = T->mem3;
new PlotWindow(700,500,sln,lmem1,lmem2);
new PlotWindow(700,500,sln,lmem1,lmem2,lmem3);
}
@ -801,7 +799,6 @@ void InputWindow::cb_Store1(Fl_Widget* o, void* v){ @@ -801,7 +799,6 @@ void InputWindow::cb_Store1(Fl_Widget* o, void* v){
T->oStatus->value("GNU Ballistics Computer: Stored to Memory 1");
if (T->mem1 != NULL) {
if (T->mem1->sln!=NULL) free (T->mem1->sln);
delete(T->mem1);
}

2
InputWindow.h

@ -81,7 +81,7 @@ class InputWindow : public Fl_Window{ @@ -81,7 +81,7 @@ class InputWindow : public Fl_Window{
Fl_Float_Input* inSH;
Fl_Int_Input* inMV;
Fl_Int_Input* inAngle;
Fl_Int_Input* inZero;
Fl_Float_Input* inZero;
Fl_Int_Input* inVwind;
Fl_Int_Input* inAwind;
Fl_Int_Input* inTemp; Fl_Float_Input* inPressure;

807
PlotWindow.cpp

@ -1,11 +1,12 @@ @@ -1,11 +1,12 @@
#include "PlotWindow.h"
PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCSolution* GMEM2) : Fl_Window(w,h,"Solution Visualization Tools"){
PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCSolution* GMEM2, GBCSolution* GMEM3) : Fl_Window(w,h,"Solution Visualization Tools"){
// Store a pointer to the calling solution.
gsln=Gsln;
mem1=GMEM1;
mem2=GMEM2;
mem3=GMEM3;
x_click=0;
y_click=0;
@ -36,7 +37,7 @@ PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCS @@ -36,7 +37,7 @@ PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCS
sl_x = new Fl_Value_Slider(150,40,350,30,"Range (yds)");
sl_x->align(FL_ALIGN_RIGHT);
sl_x->value(200);
sl_x->minimum(200);
sl_x->minimum(50);
sl_x->maximum(gsln->MaxRows());
sl_x->type(FL_HOR_NICE_SLIDER);
sl_x->callback(cb_slUpdate,this);
@ -48,18 +49,24 @@ PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCS @@ -48,18 +49,24 @@ PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCS
btMem1 = new Fl_Check_Button(590,30,20,20,"Memory 1");
btMem2 = new Fl_Check_Button(590,50,20,20,"Memory 2");
btMem3 = new Fl_Check_Button(590,70,20,20,"Memory 3");
btMem1->align(FL_ALIGN_RIGHT);
btMem2->align(FL_ALIGN_RIGHT);
btMem3->align(FL_ALIGN_RIGHT);
btMem1->labelcolor(FL_RED);
btMem2->labelcolor(FL_DARK_GREEN);
btMem3->labelcolor(FL_BLACK);
btMem1->callback(cb_Mem1,this);
btMem2->callback(cb_Mem2,this);
btMem3->callback(cb_Mem2,this);
if (mem1==NULL) btMem1->deactivate();
if (mem2==NULL) btMem2->deactivate();
if (mem3==NULL) btMem3->deactivate();
if (mem1!=NULL) btMem1->tooltip(mem1->Name());
if (mem2!=NULL) btMem2->tooltip(mem2->Name());
if (mem3!=NULL) btMem3->tooltip(mem3->Name());
rbPlotPath = new Fl_Round_Button(20,30,30,20,"Path (inches)");
rbPlotVelocity = new Fl_Round_Button(20,50,30,20,"Velocity");
@ -126,59 +133,94 @@ int PlotWindow::handle(int evt){ @@ -126,59 +133,94 @@ int PlotWindow::handle(int evt){
}
void PlotWindow::PlotPath(void){
int w = this->w();
int h = this->h();
int xmin = 5;
int xmax = w-5;
int ymin = 100;
int ymax = h-5;
int y0 = (ymax-ymin)/2 + ymin;
// The gridlines look best with 15-25 graduations in x, so we set some basic defaults here.
x_ticks=25;
if (sl_x->value() > 500) x_ticks=50;
if (sl_x->value() > 1000) x_ticks=100;
if (sl_x->value() > 2000) x_ticks=250;
if (sl_x->value() > 4000) x_ticks=500;
// We need to find the max and min y-values to determine our y scale and y-ticks
double _miny=0;
double _maxy=0;
for (int e=0;e<sl_x->value();e++){
if (gsln->GetPath(e) > _maxy) _maxy=gsln->GetPath(e);
if (gsln->GetPath(e) < _miny) _miny=gsln->GetPath(e);
}
int x_range=(int)sl_x->value();
double y_range=0;
if (fabs(_miny) > fabs(_maxy)) y_range = fabs(2.5*_miny);
else y_range = fabs(2.5*_maxy);
double x_scale = (double)(xmax-xmin) / (double)x_range;
double y_scale = (double)(ymax-ymin) / (double)y_range;
// Now do our custom drawing.
fl_draw_box(FL_FLAT_BOX,xmin,ymin,w-10,h-ymin-5,FL_WHITE);
// Draw the x-axis.
fl_color(FL_BLACK);
fl_line_style(FL_SOLID,2);
fl_line(xmin,y0,xmax,y0);
/*
PlotMem()
Plot the requested data from a given GBCSolution (the current solution or a memory)
mode = 0: Path, 1: Energy, 2: Velocity
*/
void PlotWindow::PlotMem(GBCSolution* mem, int mode){
int x1,x2;
double y1,y2;
int px1, px2, py1,py2;
x1=(int)mem->GetRange(0);
switch (mode) {
case 0: y1=mem->GetPath(0);
break;
case 1: y1=mem->GetEnergy(0);
break;
case 2: y1=mem->GetVelocity(0);
break;
}
int m=this->gsln->MaxRows();
for (int n=1;n<x_range && n<m ;n++){
x2=(int)mem->GetRange(n);
switch (mode) {
case 0: y2=mem->GetPath(n);
break;
case 1: y2=mem->GetEnergy(n);
break;
case 2: y2=mem->GetVelocity(n);
break;
}
// Translate the x,y values into scaled pixels.
px1=(int)(xmin+x_scale*(double)x1);
px2=(int)(xmin+x_scale*(double)x2);
py1=(int)((y0)-(double)y_scale*(double)y1);
py2=(int)((y0)-(double)y_scale*(double)y2);
if (py2>=ymax) break;
if (px2>=xmax) break;
// Plot the points.
if (px1>xmin && px1<xmax && py1<ymax && py1>ymin)
fl_line(px1,py1,px2,py2);
x1=x2;
y1=y2;
}
}
/*
SetupPlot()
Calculate some common parameters for the plots
This code was common to all plots (Path, Energy, Velocity) and was consolidated here
Requires x_range and y_range to be set before calling
*/
void PlotWindow::SetupPlot(void){
int w = this->w();
int h = this->h();
xmin = 5;
xmax = w-5;
ymin = 100;
ymax = h-5;
x_scale = (double)(xmax-xmin) / (double)x_range;
y_scale = (double)(ymax-ymin) / (double)y_range;
// The gridlines look best with 15-25 graduations in x, so we set some basic defaults here.
x_ticks=25;
if (sl_x->value() > 500) x_ticks=50;
if (sl_x->value() > 1000) x_ticks=100;
if (sl_x->value() > 2000) x_ticks=250;
if (sl_x->value() > 4000) x_ticks=500;
// The y-axis looks best with about 7-12 ticks, so we scale it here reasonably.
y_ticks=1;
if (y_range > 10) y_ticks=2;
@ -189,6 +231,34 @@ void PlotWindow::PlotPath(void){ @@ -189,6 +231,34 @@ void PlotWindow::PlotPath(void){
if (y_range > 400) y_ticks=100;
if (y_range > 800) y_ticks=250;
if (y_range > 1600) y_ticks=500;
if (y_range > 3200) y_ticks=1000;
if (y_range > 6400) y_ticks=2000;
if (y_range > 12800) y_ticks=4000;
if (y_range > 25600) y_ticks=8000;
if (y_range > 51200) y_ticks=16000;
if (y_range > 102400) y_ticks=32000;
if (y_range > 204800) y_ticks=64000;
}
/*
DrawPlotBackground()
Draw the graph background, axis, scale etc
Code was common to all plots and has been consolidated here
Requires SetupPlot to have been called and y0 set first
*/
void PlotWindow::DrawPlotBackground(int mode, const char *ylabel){
// Now do our custom drawing.
fl_draw_box(FL_FLAT_BOX,xmin,ymin,w()-10,h()-ymin-5,FL_WHITE);
// Draw the x-axis.
fl_color(FL_BLACK);
fl_line_style(FL_SOLID,2);
fl_line(xmin,y0,xmax,y0);
double px;
fl_color(FL_BLACK);
@ -196,11 +266,15 @@ void PlotWindow::PlotPath(void){ @@ -196,11 +266,15 @@ void PlotWindow::PlotPath(void){
char txt[64];
int r=0;
double txtw=0;
int tick_top=ymax;
if (mode>0) tick_top=y0;
// Draw X gridlines every x_ticks yds and label them.
for (px=xmin;px<xmax;px+=((double)x_ticks*x_scale)){
//fl_line((int)px,y_bottom,(int)px,y_top);
fl_line_style(FL_DOT,1);
fl_line((int)px,(int)ymin,(int)px,(int)ymax);
fl_line((int)px,(int)ymin,(int)px,tick_top);
sprintf(txt,"%d",r*x_ticks);
if (r%2==0 && r>0) {
txtw=fl_width(txt);
@ -217,131 +291,49 @@ void PlotWindow::PlotPath(void){ @@ -217,131 +291,49 @@ void PlotWindow::PlotPath(void){
for (py=y0;py>=ymin;py-=((double)y_ticks*y_scale)){
if (r>0){
fl_line(xmin,(int)py,xmax,(int)py);
sprintf(txt,"+%d\"",r*y_ticks);
sprintf(txt,"+%d%s",r*y_ticks, ylabel);
fl_draw(txt,xmin+10,(int)(py+fl_height()/2));
}
r++;
}
// Draw Y hashmarks every 1 y_tick going down
r=0;
for (py=y0;py<=ymax;py+=((double)y_ticks*y_scale)){
if (mode==0) {
r=0;
for (py=y0;py<=ymax;py+=((double)y_ticks*y_scale)){
if (r>0){
fl_line(xmin,(int)py,xmax,(int)py);
sprintf(txt,"-%d\"",r*y_ticks);
sprintf(txt,"-%d%s",r*y_ticks, ylabel);
if (py+fl_height()/2<ymax) fl_draw(txt,xmin+10,(int)(py+fl_height()/2));
}
r++;
}
}
int x1,x2;
double y1,y2;
// For the pixel locations to plot.
int px1, px2, py1,py2;
x1=(int)this->gsln->GetRange(0);
y1=this->gsln->GetPath(0);
int m=this->gsln->MaxRows();
}
void PlotWindow::DrawPlotData(int mode){
fl_color(FL_BLUE);
fl_line_style(FL_SOLID,1);
for (int n=1;n<x_range && n<m ;n++){
x2=(int)this->gsln->GetRange(n);
y2=this->gsln->GetPath(n);
// Translate the x,y values into scaled pixels.
px1=(int)(xmin+x_scale*(double)x1);
px2=(int)(xmin+x_scale*(double)x2);
py1=(int)((y0)-(double)y_scale*(double)y1);
py2=(int)((y0)-(double)y_scale*(double)y2);
if (py2>=ymax) break;
if (px2>=xmax) break;
// Plot the points.
fl_line(px1,py1,px2,py2);
x1=x2;
y1=y2;
}
PlotMem(this->gsln, mode);
if (btMem1->value()==1){
x1=(int)this->mem1->GetRange(0);
y1=this->mem1->GetPath(0);
int m=this->gsln->MaxRows();
fl_color(FL_RED);
fl_line_style(FL_SOLID,1);
for (int n=1;n<x_range && n<m ;n++){
x2=(int)this->mem1->GetRange(n);
y2=this->mem1->GetPath(n);
// Translate the x,y values into scaled pixels.
px1=(int)(xmin+x_scale*(double)x1);
px2=(int)(xmin+x_scale*(double)x2);
py1=(int)((y0)-(double)y_scale*(double)y1);
py2=(int)((y0)-(double)y_scale*(double)y2);
if (py2>=ymax) break;
if (px2>=xmax) break;
// Plot the points.
if (px1>xmin && px1<xmax && py1<ymax && py1>ymin)
fl_line(px1,py1,px2,py2);
x1=x2;
y1=y2;
PlotMem(mem1, mode);
}
}
if (btMem2->value()==1){
x1=(int)this->mem2->GetRange(0);
y1=this->mem2->GetPath(0);
int m=this->gsln->MaxRows();
fl_color(FL_GREEN);
fl_line_style(FL_SOLID,1);
for (int n=1;n<x_range && n<m ;n++){
x2=(int)this->mem2->GetRange(n);
y2=this->mem2->GetPath(n);
// Translate the x,y values into scaled pixels.
px1=(int)(xmin+x_scale*(double)x1);
px2=(int)(xmin+x_scale*(double)x2);
py1=(int)((y0)-(double)y_scale*(double)y1);
py2=(int)((y0)-(double)y_scale*(double)y2);
if (py2>=ymax) break;
if (px2>=xmax) break;
// Plot the points.
if (px1>xmin && px1<xmax && py1<ymax && py1>ymin)
fl_line(px1,py1,px2,py2);
x1=x2;
y1=y2;
PlotMem(mem2, mode);
}
if (btMem3->value()==1){
fl_color(FL_BLACK);
fl_line_style(FL_SOLID,1);
PlotMem(mem3, mode);
}
}
if (x_click>xmin && x_click<xmax && y_click>ymin && y_click<ymax){
// If the user clicks, show the coordinates they clicked on.
@ -353,40 +345,58 @@ void PlotWindow::PlotPath(void){ @@ -353,40 +345,58 @@ void PlotWindow::PlotPath(void){
fl_draw(lbl_point,x_click+5, y_click-5);
fl_rectf(x_click-2, y_click-2,4,4);
}
// Draw the frame around it last to make it all pretty.
fl_frame("aaaa",5,100,w-10,h-105);
return;
fl_frame("aaaa",5,100,w()-10,h()-105);
}
void PlotWindow::PlotPath(void){
// We need to find the max and min y-values to determine our y scale and y-ticks
double _miny=0;
double _maxy=0;
for (int e=0;e<sl_x->value();e++){
if (gsln->GetPath(e) > _maxy) _maxy=gsln->GetPath(e);
if (gsln->GetPath(e) < _miny) _miny=gsln->GetPath(e);
}
x_range=(int)sl_x->value();
if (fabs(_miny) > fabs(_maxy))
y_range = fabs(2.5*_miny);
else y_range = fabs(2.5*_maxy);
SetupPlot();
y0 = (ymax-ymin)/2 + ymin;
DrawPlotBackground(0, "\"");
// draw +/- 1 and 5 MOA or MIL lines
//if (draw_scope_guides) {
int pix_inset=50;
double oneunit=0.01; // one moa line
// if (draw_scope_guides == 2)
oneunit=oneunit * 3.438; // use one mil line instead
double range_inset=pix_inset/x_scale;
int oneunit_min=(double)y_scale * (range_inset * oneunit);
int oneunit_max=(double)y_scale * ((x_range - range_inset) * oneunit);
fl_line(xmin+pix_inset,y0-oneunit_min,xmax-pix_inset,y0-oneunit_max);
fl_line(xmin+pix_inset,y0+oneunit_min,xmax-pix_inset,y0+oneunit_max);
fl_line(xmin+pix_inset,y0-(oneunit_min*5),xmax-pix_inset,y0-(oneunit_max*5));
fl_line(xmin+pix_inset,y0+(oneunit_min*5),xmax-pix_inset,y0+(oneunit_max*5));
// }
DrawPlotData(0);
return;
}
void PlotWindow::PlotEnergy(void){
int w = this->w();
int h = this->h();
int xmin = 5;
int xmax = w-5;
int ymin = 100;
int ymax = h-5;
int y0 = (ymax)-50;
// The gridlines look best with 15-25 graduations in x, so we set some basic defaults here.
x_ticks=25;
if (sl_x->value() > 500) x_ticks=50;
if (sl_x->value() > 1000) x_ticks=100;
if (sl_x->value() > 2000) x_ticks=250;
if (sl_x->value() > 4000) x_ticks=500;
// We need to find the max y-value to determine our y scale and y-ticks
double _maxy=0;
@ -396,221 +406,23 @@ void PlotWindow::PlotEnergy(void){ @@ -396,221 +406,23 @@ void PlotWindow::PlotEnergy(void){
if (gsln->GetPath(e) > _maxy) _maxy=gsln->GetEnergy(e);
}
int x_range=(int)sl_x->value();
double y_range=0;
x_range=(int)sl_x->value();
if (fabs(_miny) > fabs(_maxy)) y_range = fabs(1.5*_miny);
if (fabs(_miny) > fabs(_maxy))
y_range = fabs(1.5*_miny);
else y_range = fabs(1.5*_maxy);
double x_scale = (double)(xmax-xmin) / (double)x_range;
double y_scale = (double)(ymax-ymin) / (double)y_range;
// Now do our custom drawing.
fl_draw_box(FL_FLAT_BOX,xmin,ymin,w-10,h-ymin-5,FL_WHITE);
// Draw the x-axis.
fl_color(FL_BLACK);
fl_line_style(FL_SOLID,2);
fl_line(xmin,y0,xmax,y0);
// The y-axis looks best with about 7-12 ticks, so we scale it here reasonably.
y_ticks=1;
if (y_range > 10) y_ticks=2;
if (y_range > 20) y_ticks=6;
if (y_range > 40) y_ticks=12;
if (y_range > 80) y_ticks=24;
if (y_range > 200) y_ticks=48;
if (y_range > 400) y_ticks=100;
if (y_range > 800) y_ticks=250;
if (y_range > 1600) y_ticks=500;
double px;
fl_color(FL_BLACK);
fl_line_style(FL_SOLID,1);
char txt[64];
int r=0;
double txtw=0;
// Draw X gridlines every x_ticks yds and label them.
for (px=xmin;px<xmax;px+=((double)x_ticks*x_scale)){
//fl_line((int)px,y_bottom,(int)px,y_top);
fl_line_style(FL_DOT,1);
fl_line((int)px,(int)ymin,(int)px,(int)y0);
sprintf(txt,"%d",r*x_ticks);
if (r%2==0 && r>0) {
txtw=fl_width(txt);
fl_line_style(FL_SOLID,1);
fl_line((int)px,y0,(int)px,y0+8);
fl_draw(txt,(int)((int)px-(txtw/2)),y0+10+fl_height());
}
r++;
}
// Draw Y hashmarks every 1 y_tick going up.
double py;
r=0;
for (py=y0;py>=ymin;py-=((double)y_ticks*y_scale)){
if (r>0){
fl_line(xmin,(int)py,xmax,(int)py);
sprintf(txt,"+%d ft-lb",r*y_ticks);
fl_draw(txt,xmin+10,(int)(py+fl_height()/2));
}
r++;
}
int x1,x2;
double y1,y2;
// For the pixel locations to plot.
int px1, px2, py1,py2;
x1=(int)this->gsln->GetRange(0);
y1=this->gsln->GetEnergy(0);
int m=this->gsln->MaxRows();
fl_color(FL_BLUE);
fl_line_style(FL_SOLID,1);
for (int n=1;n<x_range && n<m ;n++){
x2=(int)this->gsln->GetRange(n);
y2=this->gsln->GetEnergy(n);
// Translate the x,y values into scaled pixels.
px1=(int)(xmin+x_scale*(double)x1);
px2=(int)(xmin+x_scale*(double)x2);
py1=(int)((y0)-(double)y_scale*(double)y1);
py2=(int)((y0)-(double)y_scale*(double)y2);
if (py2>=ymax) break;
if (px2>=xmax) break;
// Plot the points.
fl_line(px1,py1,px2,py2);
x1=x2;
y1=y2;
}
if (btMem1->value()==1){
x1=(int)this->mem1->GetRange(0);
y1=this->mem1->GetEnergy(0);
int m=this->mem1->MaxRows();
fl_color(FL_RED);
fl_line_style(FL_SOLID,1);
for (int n=1;n<x_range && n<m ;n++){
x2=(int)this->mem1->GetRange(n);
y2=this->mem1->GetEnergy(n);
// Translate the x,y values into scaled pixels.
px1=(int)(xmin+x_scale*(double)x1);
px2=(int)(xmin+x_scale*(double)x2);
py1=(int)((y0)-(double)y_scale*(double)y1);
py2=(int)((y0)-(double)y_scale*(double)y2);
if (py2>=ymax) break;
if (px2>=xmax) break;
// Plot the points.
if (px1>xmin && px1<xmax && py1<ymax && py1>ymin) fl_line(px1,py1,px2,py2);
x1=x2;
y1=y2;
}
}
if (btMem2->value()==1){
x1=(int)this->mem2->GetRange(0);
y1=this->mem2->GetEnergy(0);
int m=this->mem2->MaxRows();
fl_color(FL_GREEN);
fl_line_style(FL_SOLID,1);
for (int n=1;n<x_range && n<m ;n++){
x2=(int)this->mem2->GetRange(n);
y2=this->mem2->GetEnergy(n);
// Translate the x,y values into scaled pixels.
px1=(int)(xmin+x_scale*(double)x1);
px2=(int)(xmin+x_scale*(double)x2);
py1=(int)((y0)-(double)y_scale*(double)y1);
py2=(int)((y0)-(double)y_scale*(double)y2);
if (py2>=ymax) break;
if (px2>=xmax) break;
// Plot the points.
if (px1>xmin && px1<xmax && py1<ymax && py1>ymin) fl_line(px1,py1,px2,py2);
x1=x2;
y1=y2;
}
}
if (x_click>xmin && x_click<xmax && y_click>ymin && y_click<ymax){
// If the user clicks, show the coordinates they clicked on.
double click_label_x= (x_click-xmin)/x_scale;
double click_label_y=(y0-y_click)/y_scale;
char lbl_point[20];
sprintf(lbl_point,"(%.0f,%.2f)",click_label_x, click_label_y);
fl_color(FL_BLACK);
fl_draw(lbl_point,x_click+5, y_click-5);
fl_rectf(x_click-2, y_click-2,4,4);
}
// Draw the frame around it last to make it all pretty.
fl_frame("aaaa",5,100,w-10,h-105);
SetupPlot();
y0 = (ymax)-50;
DrawPlotBackground(1, " ft-lbs");
DrawPlotData(1);
return;
}
void PlotWindow::PlotVelocity(void){
int w = this->w();
int h = this->h();
int xmin = 5;
int xmax = w-5;
int ymin = 100;
int ymax = h-5;
int y0 = (ymax)-50;
// The gridlines look best with 15-25 graduations in x, so we set some basic defaults here.
x_ticks=25;
if (sl_x->value() > 500) x_ticks=50;
if (sl_x->value() > 1000) x_ticks=100;
if (sl_x->value() > 2000) x_ticks=250;
if (sl_x->value() > 4000) x_ticks=500;
// We need to find the max y-value to determine our y scale and y-ticks
double _maxy=0;
@ -620,198 +432,19 @@ void PlotWindow::PlotVelocity(void){ @@ -620,198 +432,19 @@ void PlotWindow::PlotVelocity(void){
if (gsln->GetPath(e) > _maxy) _maxy=gsln->GetVelocity(e);
}
int x_range=(int)sl_x->value();
double y_range=0;
if (fabs(_miny) > fabs(_maxy)) y_range = fabs(1.5*_miny);
else y_range = fabs(1.5*_maxy);
double x_scale = (double)(xmax-xmin) / (double)x_range;
double y_scale = (double)(ymax-ymin) / (double)y_range;
// Now do our custom drawing.
fl_draw_box(FL_FLAT_BOX,xmin,ymin,w-10,h-ymin-5,FL_WHITE);
// Draw the x-axis.
fl_color(FL_BLACK);
fl_line_style(FL_SOLID,2);
fl_line(xmin,y0,xmax,y0);
// The y-axis looks best with about 7-12 ticks, so we scale it here reasonably.
y_ticks=1;
if (y_range > 10) y_ticks=2;
if (y_range > 20) y_ticks=6;
if (y_range > 40) y_ticks=12;
if (y_range > 80) y_ticks=24;
if (y_range > 200) y_ticks=48;
if (y_range > 400) y_ticks=100;
if (y_range > 800) y_ticks=250;
if (y_range > 1600) y_ticks=500;
double px;
fl_color(FL_BLACK);
fl_line_style(FL_SOLID,1);
char txt[64];
int r=0;
double txtw=0;
// Draw X gridlines every x_ticks yds and label them.
for (px=xmin;px<xmax;px+=((double)x_ticks*x_scale)){
//fl_line((int)px,y_bottom,(int)px,y_top);
fl_line_style(FL_DOT,1);
fl_line((int)px,(int)ymin,(int)px,(int)y0);
sprintf(txt,"%d",r*x_ticks);
if (r%2==0 && r>0) {
txtw=fl_width(txt);
fl_line_style(FL_SOLID,1);
fl_line((int)px,y0,(int)px,y0+8);
fl_draw(txt,(int)((int)px-(txtw/2)),y0+10+fl_height());
}
r++;
}
// Draw Y hashmarks every 1 y_tick going up.
double py;
r=0;
for (py=y0;py>=ymin;py-=((double)y_ticks*y_scale)){
if (r>0){
fl_line(xmin,(int)py,xmax,(int)py);
sprintf(txt,"+%d ft/s",r*y_ticks);
fl_draw(txt,xmin+10,(int)(py+fl_height()/2));
}
r++;
}
int x1,x2;
double y1,y2;
// For the pixel locations to plot.
int px1, px2, py1,py2;
x1=(int)this->gsln->GetRange(0);
y1=this->gsln->GetVelocity(0);
int m=this->gsln->MaxRows();
fl_color(FL_BLUE);
fl_line_style(FL_SOLID,1);
for (int n=1;n<x_range && n<m ;n++){
x2=(int)this->gsln->GetRange(n);
y2=this->gsln->GetVelocity(n);
// Translate the x,y values into scaled pixels.
px1=(int)(xmin+x_scale*(double)x1);
px2=(int)(xmin+x_scale*(double)x2);
py1=(int)((y0)-(double)y_scale*(double)y1);
py2=(int)((y0)-(double)y_scale*(double)y2);
if (py2>=ymax) break;
if (px2>=xmax) break;
// Plot the points.
fl_line(px1,py1,px2,py2);
x1=x2;
y1=y2;
}
if (btMem1->value()==1){
x1=(int)this->mem1->GetRange(0);
y1=this->mem1->GetVelocity(0);
int m=this->mem1->MaxRows();
fl_color(FL_RED);
fl_line_style(FL_SOLID,1);
for (int n=1;n<x_range && n<m ;n++){
x2=(int)this->mem1->GetRange(n);
y2=this->mem1->GetVelocity(n);
// Translate the x,y values into scaled pixels.
px1=(int)(xmin+x_scale*(double)x1);
px2=(int)(xmin+x_scale*(double)x2);
py1=(int)((y0)-(double)y_scale*(double)y1);
py2=(int)((y0)-(double)y_scale*(double)y2);
if (py2>=ymax) break;
if (px2>=xmax) break;
// Plot the points.
if (px1>xmin && px1<xmax && py1<ymax && py1>ymin) fl_line(px1,py1,px2,py2);
x1=x2;
y1=y2;
}
}
if (btMem2->value()==1){
x1=(int)this->mem2->GetRange(0);
y1=this->mem2->GetVelocity(0);
int m=this->mem2->MaxRows();
fl_color(FL_GREEN);
fl_line_style(FL_SOLID,1);
for (int n=1;n<x_range && n<m ;n++){
x2=(int)this->mem2->GetRange(n);
y2=this->mem2->GetVelocity(n);
// Translate the x,y values into scaled pixels.
px1=(int)(xmin+x_scale*(double)x1);
px2=(int)(xmin+x_scale*(double)x2);
py1=(int)((y0)-(double)y_scale*(double)y1);
py2=(int)((y0)-(double)y_scale*(double)y2);
if (py2>=ymax) break;
if (px2>=xmax) break;
// Plot the points.
if (px1>xmin && px1<xmax && py1<ymax && py1>ymin) fl_line(px1,py1,px2,py2);
x1=x2;
y1=y2;
}
}
if (x_click>xmin && x_click<xmax && y_click>ymin && y_click<ymax){
// If the user clicks, show the coordinates they clicked on.
double click_label_x= (x_click-xmin)/x_scale;
double click_label_y=(y0-y_click)/y_scale;
char lbl_point[20];
sprintf(lbl_point,"(%.0f,%.2f)",click_label_x, click_label_y);
fl_color(FL_BLACK);
fl_draw(lbl_point,x_click+5, y_click-5);
fl_rectf(x_click-2, y_click-2,4,4);
}
// Draw the frame around it last to make it all pretty.
fl_frame("aaaa",5,100,w-10,h-105);
x_range=(int)sl_x->value();
if (fabs(_miny) > fabs(_maxy))
y_range = fabs(1.5*_miny);
else y_range = fabs(1.5*_maxy);
SetupPlot();
y0 = (ymax)-50;
DrawPlotBackground(2, " ft/s");
DrawPlotData(2);
return;
}
void PlotWindow::cb_btPlot(Fl_Widget* o, void* v){
@ -855,4 +488,4 @@ void PlotWindow::cb_Mem2(Fl_Widget* o, void* v){ @@ -855,4 +488,4 @@ void PlotWindow::cb_Mem2(Fl_Widget* o, void* v){
T->redraw();
}

15
PlotWindow.h

@ -18,11 +18,12 @@ @@ -18,11 +18,12 @@
class PlotWindow : public Fl_Window {
public:
PlotWindow(int, int, GBCSolution*, GBCSolution*, GBCSolution*);
PlotWindow(int, int, GBCSolution*, GBCSolution*, GBCSolution*, GBCSolution*);
~PlotWindow(void);
GBCSolution* gsln;
GBCSolution* mem1;
GBCSolution* mem2;
GBCSolution* mem3;
@ -32,7 +33,17 @@ class PlotWindow : public Fl_Window { @@ -32,7 +33,17 @@ class PlotWindow : public Fl_Window {
int cheight;
int x_ticks;
int y_ticks;
int xmin, ymin, xmax, ymax;
int y0;
int x_range;
double y_range;
double x_scale, y_scale;
void PlotMem(GBCSolution* mem, int mode);
void SetupPlot(void);
void DrawPlotBackground(int mode, const char *ylabel);
void DrawPlotData(int mode);
void PlotPath(void);
void PlotEnergy(void);
void PlotVelocity(void);
@ -46,6 +57,7 @@ class PlotWindow : public Fl_Window { @@ -46,6 +57,7 @@ class PlotWindow : public Fl_Window {
Fl_Check_Button* btMem1;
Fl_Check_Button* btMem2;
Fl_Check_Button* btMem3;
Fl_Value_Slider* sl_x;
@ -58,6 +70,7 @@ class PlotWindow : public Fl_Window { @@ -58,6 +70,7 @@ class PlotWindow : public Fl_Window {
static void cb_slUpdate(Fl_Widget* o, void* v);
static void cb_Mem1(Fl_Widget* o, void* v);
static void cb_Mem2(Fl_Widget* o, void* v);
static void cb_Mem3(Fl_Widget* o, void* v);
};

605
RangeWindow.cpp

@ -1,5 +1,8 @@ @@ -1,5 +1,8 @@
#include "RangeWindow.h"
#define MOAtoMIL(moa) (moa/3.438)
#define MILtoMOA(mil) (mil*3.438)
RangeWindow::RangeWindow(int w, int h, GBCSolution* inSLN):Fl_Window(w,h,inSLN->Name()){
begin();
menu = new Fl_Menu_Bar(0,0,w,30,"MENU");
@ -55,16 +58,19 @@ RangeWindow::RangeWindow(int w, int h, GBCSolution* inSLN):Fl_Window(w,h,inSLN-> @@ -55,16 +58,19 @@ RangeWindow::RangeWindow(int w, int h, GBCSolution* inSLN):Fl_Window(w,h,inSLN->
tbl = new Fl_Browser(5,35,w-10,h-40,"Hello");
colwidths = (int*)malloc(10*sizeof(int));
colwidths = (int*)malloc(12*sizeof(int));
colwidths[0]=70; // Range (yds)
colwidths[1]=70; // Drop (in)
colwidths[2]=70; // Drop (MOA)
colwidths[3]=70; // Vel (ft/s)
colwidths[4]=70; // Energy (ft-lb)
colwidths[5]=70; // Winddrift (in)
colwidths[6]=70; // Windage (ft)
colwidths[7]=70; // Time (s)
colwidths[8]=0;
colwidths[3]=70; // Drop (MIL)
colwidths[4]=70; // Target Size (MILs)
colwidths[5]=70; // Vel (ft/s)
colwidths[6]=70; // Energy (ft-lb)
colwidths[7]=70; // Winddrift (in)
colwidths[8]=70; // Windage (ft)
colwidths[9]=70; // Windage (MIL)
colwidths[10]=70; // Time (s)
colwidths[11]=0;
tbl->column_widths(colwidths);
tbl->column_char('\t');
@ -72,6 +78,7 @@ RangeWindow::RangeWindow(int w, int h, GBCSolution* inSLN):Fl_Window(w,h,inSLN-> @@ -72,6 +78,7 @@ RangeWindow::RangeWindow(int w, int h, GBCSolution* inSLN):Fl_Window(w,h,inSLN->
min=0;
max=1000;
step=10;
targetsize=4.0; // inches
GenTable();
@ -88,21 +95,116 @@ RangeWindow::~RangeWindow(){ @@ -88,21 +95,116 @@ RangeWindow::~RangeWindow(){
}
/*
Mini-HOWTO: modifying the range table.
For the table data:
- update COLUMNS define, headers0, headers1, format,
sqlnames and xmlnames arrays below.
- add a case to GetData for the column data
For the load data at the top:
- modify GetPreface() and GetPrefaceLines()
Note: entries with a blank sqlname are not written to the sql output.
entries with a blank xmlname are not written to the xml output.
*/
#define COLUMNS 11
static const char *headers0[COLUMNS]={"Range", "Drop", "Drop", "Drop", "4\" Obj", "Velocity","Energy", "Wind Drift","Windage", "Windage", "Time"};
static const char *headers1[COLUMNS]={"(yards)","(inches)","(MOA)", "(MIL)", "(MIL)", "(ft/s)", "(ft-lbs)","(inches)", "(MOA)", "(MIL)", "(s)" };
static const char *format [COLUMNS]={"%.0f", "%.2f", "%.2f", "%.2f", "%.2f", "%.0f", "%.0f", "%.2f", "%.2f", "%.2f", "%.2f"};
static const char *sqlnames[COLUMNS]={"range", "drop", "elev_moa", "elev_mil","", "velocity","energy", "winddrift", "windage_moa","windage_mil","time"};
static const char *xmlnames[COLUMNS]={"Range", "Drop", "ElevMOA", "ElevMIL", "", "Velocity","Energy", "WindDrift", "WindageMOA", "WindageMIL", "Time"};
int RangeWindow::GetColumns(void){
return COLUMNS;
}
int RangeWindow::GetHeaderLines(void){
return 2;
}
int RangeWindow::GetPrefaceLines(void){
return 5;
}
void RangeWindow::GetPreface(int l, char *str){
switch (l) {
case 0: sprintf(str,"Drag Coefficient: %.3f Projectile Weight: %d grains",Sln->BC(), Sln->Weight());
break;