Browse Source

Scope guides can be in MIL, MOA or degrees. Files can be loaded from the command line so GEBC can open with your favourite ammo loaded. Target view - as seen by shooter. A number of standard ISSF and NRA targets can be drawn in the target view. A ring is drawn around the impact area to indicate 1 MOA zone as a guide to the possible variation from the predicted path.

MikeNix
surkhe 6 months ago
parent
commit
b97a467b33
  1. 39
      ChangeLog
  2. 8
      GBCSolution.cpp
  3. 1
      GBCSolution.h
  4. 5
      GNU_Ballistics.cpp
  5. 124
      InputWindow.cpp
  6. 6
      InputWindow.h
  7. 2
      Makefile.am
  8. 272
      PlotWindow.cpp
  9. 14
      PlotWindow.h
  10. 723
      TargetWindow.cpp
  11. 80
      TargetWindow.h
  12. 10
      lib/ballistics/ballistics.cpp
  13. 1
      lib/ballistics/ballistics.h

39
ChangeLog

@ -1,4 +1,43 @@ @@ -1,4 +1,43 @@
Jan 2011 mnix@wanm.com.au
* Added TargetWindow - view of target as seen from shooter.
* Added a perspective checkbox to the target window. This switches
between path as seen by shooter (on) and path relative to line of
sight (off). For example, a bullet 1/2 way to the target will
line up with a point twice as far above line of sight in the target
as it actually is. ie if the bullet is 1" above line of sight at 50
yds, it will be in line with a point 2" above line of sight on a
target at 100 yds. The line appears to move on the target because as
you move the target back (say to 200 yds) the bullet at 50 yds and
1" high will line up with a point 4" high on the target at 200 yds.
* If a file name is given on the command line it is loaded at startup.
No checking is done on the file name - if it does not exist,
bad things may happen.
* Added various target types including common NRA and ISSF targets
* Added a "variance" ring around the impact point. This is 1 MOA plus
a bit on the sides as alowance for spin drift (it's added to both
sides and left to the viewer to decide on whether to allow for left
or right twist. The amount is so small it doesn't matter much
anyway (we allow a generic 0.1 MOA per 100 yards beyond 100 yards)
Nov 2010 mnix#wanm.com.au
* Added a drop down box to the Plot Window to select scope guides.
* Changed the calculations for the scope guides to draw them more
accurately.
* Get Zero to float in the path plot
* Clip the scope guides
* Added an option for guides every 1 degree
Oct 2010 mnix@wanm.com.au
* Added scope guides to the path plot as a ready reference to how the

8
GBCSolution.cpp

@ -159,6 +159,14 @@ double GBCSolution::GetVy(int yardage){ @@ -159,6 +159,14 @@ double GBCSolution::GetVy(int yardage){
}
else return 0;
}
double GBCSolution::GetDrop(int yardage){
double size=sln[__BCOMP_MAXRANGE__*10+1];
if (yardage<size){
return sln[10*yardage+9];
}
else return 0;
}
double GBCSolution::GetEnergy(int k){
return (double)weight*GetVelocity(k)*GetVelocity(k)/450436;

1
GBCSolution.h

@ -58,6 +58,7 @@ class GBCSolution { @@ -58,6 +58,7 @@ class GBCSolution {
double GetVelocity(int yardage);
double GetVx(int yardage);
double GetVy(int yardage);
double GetDrop(int yardage);
double GetEnergy(int yardage);
int df;

5
GNU_Ballistics.cpp

@ -2,8 +2,11 @@ @@ -2,8 +2,11 @@
#include <FL/Fl_Window.H>
#include "InputWindow.h"
using namespace std;
int main (){
int main (int argc, char *argv[]){
InputWindow win(400,500,"GNU Exterior Ballistics Computer");
if (argc==2) {
win.OpenFile(argv[1]);
}
return Fl::run();
}

124
InputWindow.cpp

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
#include "lib/ballistics/ballistics.h"
#include "InputWindow.h"
#include "TargetWindow.h"
InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){
@ -146,6 +146,7 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){ @@ -146,6 +146,7 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){
Fl_Menu_Item m_Analysis = {"&Analysis",0,0,0, FL_SUBMENU};
Fl_Menu_Item mc_RangeTable = {"&Range Table",0,(Fl_Callback*)cb_RangeTable,this};
Fl_Menu_Item mc_QuickPlot = {"Quick &Plot",0,(Fl_Callback*)cb_Plot, this};
Fl_Menu_Item mc_TargetPlot= {"&Target Plot",0,(Fl_Callback*)cb_Target, this};
Fl_Menu_Item mc_RangeCard = {"&Range Card",0,(Fl_Callback*)cb_Nothing};
Fl_Menu_Item mc_ClickChart = {"&Click Chart",0,(Fl_Callback*)cb_Nothing,this,FL_MENU_DIVIDER};
Fl_Menu_Item mc_MVA = {"&Advanced Analysis",0,(Fl_Callback*)cb_Nothing,this};
@ -192,7 +193,7 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){ @@ -192,7 +193,7 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){
#define MANALYSIS 7
#define MSTORAGE 19
#define MSTORAGE 20
Fl_Menu_Item menuitems[] = {
m_File, //0
mc_New,
@ -204,18 +205,19 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){ @@ -204,18 +205,19 @@ InputWindow::InputWindow(int w, int h, const char* title):Fl_Window(w,h,title){
m_Analysis, //7
mc_RangeTable,
mc_QuickPlot,
mc_TargetPlot,
mc_RangeCard,
mc_ClickChart,
mc_MVA,
{0}, // 13
{0}, // 14
m_Tools,
mc_OptimizePBR,
mc_CalcBC,
mc_Options,
{0},
m_Storage,
mc_Store1, //20
mc_Store2, //21
mc_Store1, //21
mc_Store2, //22
mc_Store3,
mc_Store4,
mc_Store5,
@ -671,25 +673,30 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){ @@ -671,25 +673,30 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){
InputWindow* T = (InputWindow*)v;
// A solution for locally working with gsln (hence the name lsln)
GBCSolution* lsln = NULL;
// Get the file name to load
char* fname=NULL;
fname = fl_file_chooser("Please select a file to open.","*.gbc","",0);
if (fname==NULL) {
T->oStatus->value("GNU Ballistic Computer: Error opening file!");
return;
}
}
T->OpenFile(fname);
}
void InputWindow::OpenFile(char * fname) {
// A solution for locally working with gsln (hence the name lsln)
GBCSolution* lsln = NULL;
// 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) {
delete(T->gsln);
T->gsln=NULL;
if (gsln != NULL) {
delete(gsln);
gsln=NULL;
}
// Allocate some space for the loaded solution.
@ -702,7 +709,7 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){ @@ -702,7 +709,7 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){
lsln = new GBCSolution; // (GBCSolution*)malloc(sizeof(GBCSolution));
if (lsln==NULL){
T->oStatus->value("GNU Ballistic Computer: Memory allocation error.");
oStatus->value("GNU Ballistic Computer: Memory allocation error.");
return;
}
@ -713,15 +720,15 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){ @@ -713,15 +720,15 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){
fclose(ofile);
if (read!=sizeof(GBCSolution)){
T->oStatus->value("GNU Ballistic Computer: Error reading file data!\nPlease ensure the file is a valid GNUBC solution.");
oStatus->value("GNU Ballistic Computer: Error reading file data!\nPlease ensure the file is a valid GNUBC solution.");
if (lsln!=NULL) {
delete(lsln);
lsln=NULL;
}
if (T->gsln!=NULL) {
delete(T->gsln);
T->gsln=NULL;
if (gsln!=NULL) {
delete(gsln);
gsln=NULL;
}
return;
@ -729,51 +736,51 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){ @@ -729,51 +736,51 @@ void InputWindow::cb_Open(Fl_Widget* o, void*v){
// Set the parent's solution pointer to the local one we just imported.
T->gsln=lsln;
gsln=lsln;
// Load the solution data into the INPUT fields in the InputWindow.
char buff[1024];
T->inName->value(lsln->Name());
sprintf(buff,"%.3f",lsln->BC()); T->inBC->value(buff);
sprintf(buff,"%.2f",lsln->SightHeight()); T->inSH->value(buff);
sprintf(buff,"%d",lsln->MuzzleVelocity()); T->inMV->value(buff);
sprintf(buff,"%d",lsln->ShootingAngle()); T->inAngle->value(buff);
sprintf(buff,"%d",lsln->ZeroRange()); T->inZero->value(buff);
sprintf(buff,"%d",lsln->WindSpeed()); T->inVwind->value(buff);
sprintf(buff,"%d",lsln->WindAngle()); T->inAwind->value(buff);
sprintf(buff,"%d",lsln->Temp()); T->inTemp->value(buff);
sprintf(buff,"%.2f",lsln->Pressure()); T->inPressure->value(buff);
sprintf(buff,"%d",lsln->Humidity()); T->inHumidity->value(buff);
sprintf(buff,"%d",lsln->Altitude()); T->inAltitude->value(buff);
sprintf(buff,"%d",lsln->Weight()); T->inWeight->value(buff);
T->ckWeather->value(lsln->UseWeather());
T->cb_ckWeather(o,v);
T->inG1->value(0);
T->inG2->value(0);
T->inG5->value(0);
T->inG6->value(0);
T->inG7->value(0);
T->inG8->value(0);
if (lsln->df==G1) T->inG1->set();
if (lsln->df==G2) T->inG2->set();
if (lsln->df==G5) T->inG5->set();
if (lsln->df==G6) T->inG6->set();
if (lsln->df==G7) T->inG7->set();
if (lsln->df==G8) T->inG8->set();
inName->value(lsln->Name());
sprintf(buff,"%.3f",lsln->BC()); inBC->value(buff);
sprintf(buff,"%.2f",lsln->SightHeight()); inSH->value(buff);
sprintf(buff,"%d",lsln->MuzzleVelocity()); inMV->value(buff);
sprintf(buff,"%d",lsln->ShootingAngle()); inAngle->value(buff);
sprintf(buff,"%d",lsln->ZeroRange()); inZero->value(buff);
sprintf(buff,"%d",lsln->WindSpeed()); inVwind->value(buff);
sprintf(buff,"%d",lsln->WindAngle()); inAwind->value(buff);
sprintf(buff,"%d",lsln->Temp()); inTemp->value(buff);
sprintf(buff,"%.2f",lsln->Pressure()); inPressure->value(buff);
sprintf(buff,"%d",lsln->Humidity()); inHumidity->value(buff);
sprintf(buff,"%d",lsln->Altitude()); inAltitude->value(buff);
sprintf(buff,"%d",lsln->Weight()); inWeight->value(buff);
ckWeather->value(lsln->UseWeather());
cb_ckWeather(NULL,this);
inG1->value(0);
inG2->value(0);
inG5->value(0);
inG6->value(0);
inG7->value(0);
inG8->value(0);
if (lsln->df==G1) inG1->set();
if (lsln->df==G2) inG2->set();
if (lsln->df==G5) inG5->set();
if (lsln->df==G6) inG6->set();
if (lsln->df==G7) inG7->set();
if (lsln->df==G8) inG8->set();
// We don't actually have a valid solution, so make sure to set the solution
// double* pointer to NULL before we call solve.
T->gsln->sln=NULL;
gsln->sln=NULL;
// And now solve the loaded parameters.
DisableMenu(T);
T->cb_Solve(o,v);
DisableMenu(this);
cb_Solve(NULL,this);
// Finally inform the user of our success.
T->oStatus->value("GNU Ballistics Computer: File loaded successfully.");
oStatus->value("GNU Ballistics Computer: File loaded successfully.");
return;
@ -793,6 +800,19 @@ void InputWindow::cb_Plot(Fl_Widget* o, void* v){ @@ -793,6 +800,19 @@ void InputWindow::cb_Plot(Fl_Widget* o, void* v){
}
void InputWindow::cb_Target(Fl_Widget* o, void* v){
InputWindow* T = (InputWindow*)v;
GBCSolution* sln = T->gsln;
GBCSolution* lmem1 = T->mem1;
GBCSolution* lmem2 = T->mem2;
GBCSolution* lmem3 = T->mem3;
new TargetWindow(600,600,sln,lmem1,lmem2,lmem3);
}
void InputWindow::cb_Store1(Fl_Widget* o, void* v){
InputWindow* T = (InputWindow*)v;

6
InputWindow.h

@ -29,12 +29,16 @@ @@ -29,12 +29,16 @@
#include "AboutWindow.h"
#include "LicenseWindow.h"
#include "PlotWindow.h"
#include "TargetWindow.h"
class InputWindow : public Fl_Window{
public:
InputWindow(int w, int h, const char* title );
~InputWindow();
void OpenFile(char *fname);
Fl_Button* btSolve;
Fl_Button* btReset;
Fl_Button* btStdAtmosphere;
@ -107,6 +111,7 @@ class InputWindow : public Fl_Window{ @@ -107,6 +111,7 @@ class InputWindow : public Fl_Window{
Fl_Menu_Item m_Analyze;
Fl_Menu_Item mc_Table;
Fl_Menu_Item mc_Plot;
Fl_Menu_Item mc_Target;
Fl_Menu_Item mc_Compare;
Fl_Menu_Item m_Aids;
@ -154,6 +159,7 @@ class InputWindow : public Fl_Window{ @@ -154,6 +159,7 @@ class InputWindow : public Fl_Window{
static void cb_Save(Fl_Widget*, void*);
static void cb_Open(Fl_Widget*, void*);
static void cb_Plot(Fl_Widget*, void*);
static void cb_Target(Fl_Widget*, void*);
static void cb_Options(Fl_Widget*, void*);
static void cb_Store1(Fl_Widget*, void*);

2
Makefile.am

@ -10,6 +10,7 @@ gebcprogdir=../ @@ -10,6 +10,7 @@ gebcprogdir=../
gebc_SOURCES = \
InputWindow.cpp \
PlotWindow.cpp \
TargetWindow.cpp \
RangeWindow.cpp \
LicenseWindow.cpp \
AboutWindow.cpp \
@ -18,6 +19,7 @@ gebc_SOURCES = \ @@ -18,6 +19,7 @@ gebc_SOURCES = \
GNU_Ballistics.cpp \
InputWindow.h \
PlotWindow.h \
TargetWindow.h \
RangeWindow.h \
LicenseWindow.h \
AboutWindow.h \

272
PlotWindow.cpp

@ -1,5 +1,12 @@ @@ -1,5 +1,12 @@
#include "PlotWindow.h"
#define PT_PATH 0
#define PT_DROP 1
#define PT_VELOCITY 2
#define PT_HVELOCITY 3
#define PT_ENERGY 4
#define PT_WINDAGE 5
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.
@ -34,7 +41,7 @@ PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCS @@ -34,7 +41,7 @@ PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCS
menu->down_box(FL_NO_BOX);
// Put in a range slider for selecting the chart's range.
sl_x = new Fl_Value_Slider(150,40,350,30,"Range (yds)");
sl_x = new Fl_Value_Slider(150,30,350,30,"Range (yds)");
sl_x->align(FL_ALIGN_RIGHT);
sl_x->value(200);
sl_x->minimum(50);
@ -46,6 +53,24 @@ PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCS @@ -46,6 +53,24 @@ PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCS
//Fl_Button* btPlot = new Fl_Button(600,40,70,30,"UPDATE");
//btPlot->callback(cb_btPlot,this);
Fl_Menu_Item guideitems[]={
{"None",0,(Fl_Callback*)cb_Guides, this, 0},
{"1,5 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
{"1-5 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
{"1-10 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
{"1-5,10 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
{"2,10 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
{"2,4,6,8,10 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
{"2-20 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
{"2-10,20 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
{"1-5 Degrees",0,(Fl_Callback*)cb_Guides, this, 0},
{"5,10,15 Degree",0,(Fl_Callback*)cb_Guides, this, 0},
{0}
};
cScopeGuides = new Fl_Choice(290,65,150,30,"Scope Guides");
cScopeGuides->copy(guideitems);
cScopeGuides->value(1);
btMem1 = new Fl_Check_Button(590,30,20,20,"Memory 1");
btMem2 = new Fl_Check_Button(590,50,20,20,"Memory 2");
@ -68,23 +93,18 @@ PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCS @@ -68,23 +93,18 @@ PlotWindow::PlotWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCS
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");
rbPlotEnergy = new Fl_Round_Button(20,70,30,20,"Energy");
rbPlotPath->type(FL_RADIO_BUTTON);
rbPlotVelocity->type(FL_RADIO_BUTTON);
rbPlotEnergy->type(FL_RADIO_BUTTON);
rbPlotPath->callback(cb_btPlot,this);
rbPlotVelocity->callback(cb_btPlot,this);
rbPlotEnergy->callback(cb_btPlot,this);
rbPlotPath->value(1);
rbPlotPath->align(FL_ALIGN_RIGHT);
rbPlotVelocity->align(FL_ALIGN_RIGHT);
rbPlotEnergy->align(FL_ALIGN_RIGHT);
Fl_Menu_Item plottypeitems[]={
{"Path (inches)",0,(Fl_Callback*)cb_PlotType, this, 0},
{"Drop (inches)",0,(Fl_Callback*)cb_PlotType, this, 0},
{"Velocity",0,(Fl_Callback*)cb_PlotType, this, 0},
{"Horizontal Velocity",0,(Fl_Callback*)cb_PlotType, this, 0},
{"Energy",0,(Fl_Callback*)cb_PlotType, this, 0},
{"Wind Drift",0,(Fl_Callback*)cb_PlotType, this, 0},
{0}
};
cPlotType = new Fl_Choice(35,65,130,30,"Plot");
cPlotType->copy(plottypeitems);
cPlotType->value(0);
end();
show();
@ -96,11 +116,11 @@ PlotWindow::~PlotWindow(void){ @@ -96,11 +116,11 @@ PlotWindow::~PlotWindow(void){
delete menu;
delete sl_x;
delete rbPlotPath;
delete rbPlotVelocity;
delete rbPlotEnergy;
delete btMem1;
delete btMem2;
delete btMem3;
delete cScopeGuides;
delete cPlotType;
}
@ -111,10 +131,14 @@ void PlotWindow::draw(void){ @@ -111,10 +131,14 @@ void PlotWindow::draw(void){
// Let the window draw itself first.
Fl_Window::draw();
if (rbPlotPath->value()==1) PlotPath();
else if (rbPlotEnergy->value()==1) PlotEnergy();
else if (rbPlotVelocity->value()==1) PlotVelocity();
switch (cPlotType->value()) {
case PT_PATH : PlotPath(PT_PATH); break;
case PT_DROP : PlotPath(PT_DROP); break;
case PT_ENERGY: PlotEnergy(); break;
case PT_VELOCITY: PlotVelocity(PT_VELOCITY); break;
case PT_HVELOCITY:PlotVelocity(PT_HVELOCITY);break;
case PT_WINDAGE: PlotPath(PT_WINDAGE); break;
}
}
int PlotWindow::handle(int evt){
@ -138,7 +162,7 @@ int PlotWindow::handle(int evt){ @@ -138,7 +162,7 @@ int PlotWindow::handle(int evt){
Plot the requested data from a given GBCSolution (the current solution or a memory)
mode = 0: Path, 1: Energy, 2: Velocity
mode = 0: Path, 1: Energy, 2: Velocity, 3: Wind Drift
*/
@ -150,11 +174,23 @@ void PlotWindow::PlotMem(GBCSolution* mem, int mode){ @@ -150,11 +174,23 @@ void PlotWindow::PlotMem(GBCSolution* mem, int mode){
x1=(int)mem->GetRange(0);
switch (mode) {
case 0: y1=mem->GetPath(0);
case PT_PATH:
y1=mem->GetPath(0);
break;
case PT_DROP:
y1=mem->GetDrop(0);
break;
case PT_ENERGY:
y1=mem->GetEnergy(0);
break;
case 1: y1=mem->GetEnergy(0);
case PT_VELOCITY:
y1=mem->GetVelocity(0);
break;
case 2: y1=mem->GetVelocity(0);
case PT_HVELOCITY:
y1=mem->GetVx(0);
break;
case PT_WINDAGE:
y1=mem->GetWindage(0);
break;
}
@ -163,11 +199,23 @@ void PlotWindow::PlotMem(GBCSolution* mem, int mode){ @@ -163,11 +199,23 @@ void PlotWindow::PlotMem(GBCSolution* mem, int mode){
for (int n=1;n<x_range && n<m ;n++){
x2=(int)mem->GetRange(n);
switch (mode) {
case 0: y2=mem->GetPath(n);
case PT_PATH:
y2=mem->GetPath(n);
break;
case PT_DROP:
y2=mem->GetDrop(n);
break;
case PT_ENERGY:
y2=mem->GetEnergy(n);
break;
case 1: y2=mem->GetEnergy(n);
case PT_VELOCITY:
y2=mem->GetVelocity(n);
break;
case 2: y2=mem->GetVelocity(n);
case PT_HVELOCITY:
y2=mem->GetVx(n);
break;
case PT_WINDAGE:
y2=mem->GetWindage(n);
break;
}
@ -268,7 +316,7 @@ void PlotWindow::DrawPlotBackground(int mode, const char *ylabel){ @@ -268,7 +316,7 @@ void PlotWindow::DrawPlotBackground(int mode, const char *ylabel){
double txtw=0;
int tick_top=ymax;
if (mode>0) tick_top=y0;
if (mode != PT_PATH && mode != PT_DROP && mode != PT_WINDAGE) tick_top=y0;
// Draw X gridlines every x_ticks yds and label them.
for (px=xmin;px<xmax;px+=((double)x_ticks*x_scale)){
@ -298,7 +346,7 @@ void PlotWindow::DrawPlotBackground(int mode, const char *ylabel){ @@ -298,7 +346,7 @@ void PlotWindow::DrawPlotBackground(int mode, const char *ylabel){
}
// Draw Y hashmarks every 1 y_tick going down
if (mode==0) {
if (mode==PT_PATH || mode==PT_DROP || mode==PT_WINDAGE) {
r=0;
for (py=y0;py<=ymax;py+=((double)y_ticks*y_scale)){
if (r>0){
@ -352,45 +400,126 @@ void PlotWindow::DrawPlotData(int mode){ @@ -352,45 +400,126 @@ void PlotWindow::DrawPlotData(int mode){
}
void PlotWindow::PlotPath(void){
void PlotWindow::PlotPath(int ptype){
// 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 (ptype == PT_WINDAGE) {
for (int e=0;e<sl_x->value();e++){
if (gsln->GetWindage(e) > _maxy) _maxy=gsln->GetWindage(e);
if (gsln->GetWindage(e) < _miny) _miny=gsln->GetWindage(e);
}
} else if (ptype == PT_DROP) {
for (int e=0;e<sl_x->value();e++){
if (gsln->GetDrop(e) > _maxy) _maxy=gsln->GetDrop(e);
if (gsln->GetDrop(e) < _miny) _miny=gsln->GetDrop(e);
}
} else {
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);
x_range=(int)sl_x->value();
_miny*=1.1;
if (_miny > -4) _miny=-4;
if (_maxy < 3) _maxy=3;
else if (_maxy < 10) _maxy *= 2;
else _maxy*=1.3;
if (_maxy < fabs(_miny)/3) _maxy = fabs(_miny)/3;
else if (fabs(_miny) < (_maxy/3)) _miny = -_maxy/3;
y_range = _maxy - _miny;
SetupPlot();
y0 = (ymax-ymin)/2 + ymin;
y0 = ymax + (_miny * y_scale);
DrawPlotBackground(0, "\"");
DrawPlotBackground(ptype, "\"");
// 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
if (cScopeGuides->value() > 0) {
int sgmode=cScopeGuides->value();
double pix_inset=50;
// one MIL - not quite equal to one real milliradian
long double oneunit = 3.1415 / 3200;
if (sgmode >= 5)
oneunit=oneunit / 3.438; // use one moa instead
if (sgmode >= 9)
oneunit=0.0174532925; // use one degree
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);
double near, far;
int start, stop, step, step2, g;
char txt[10];
step2=0;
switch (sgmode) {
case 1: start=1; stop=5; step=4; break; // 1,5 MIL
case 2: start=1; stop=5; step=1; break; // 1-5 MIL
case 3: start=1; stop=10; step=1; break; // 1-10 MIL
case 4: start=1; stop=5; step=1; step2=5; break; // 1-5,10 MIL
case 5: start=2; stop=10; step=8; break; // 2,10 MOA
case 6: start=2; stop=10; step=2; break; // 2,4,6,8,10 MOA
case 7: start=2; stop=20; step=2; break; // 2-20 MOA
case 8: start=2; stop=10; step=2; step2=10; break; // 2,4,6,8,10,20 MOA
case 9: start=1; stop=5; step=1; break; // 1-10 Degrees
case 10: start=5; stop=15; step=5; break; // 5,10,15 Degrees
default: start=1; stop=1; step=1; break;
}
fl_clip(5,100,w()-10,h()-105);
for (g=start; g<=stop; g+=step) {
near=tanf(oneunit*g) * range_inset * 36.0; // and convert yards->inches
far =tanf(oneunit*g) * (x_range - range_inset) * 36.0;
if (near < y_range/2) {
fl_line(xmin+pix_inset,y0-(near*y_scale),xmax-pix_inset,y0-(far*y_scale));
if (g==start) {
if (sgmode>=9) sprintf(txt, "+%d Deg", g);
else if (sgmode>=5) sprintf(txt, "+%d MOA", g);
else sprintf(txt, "+%d MIL", g);
}
else sprintf(txt, "+%d", g);
fl_draw(txt, xmax-pix_inset+3, y0-(far*y_scale));
fl_line(xmin+pix_inset,y0+(near*y_scale),xmax-pix_inset,y0+(far*y_scale));
if (g==start) {
if (sgmode>=9) sprintf(txt, "-%d Deg", g);
else if (sgmode>=5) sprintf(txt, "-%d MOA", g);
else sprintf(txt, "-%d MIL", g);
}
else sprintf(txt, "-%d", g);
fl_draw(txt, xmax-pix_inset+3, y0+(far*y_scale));
}
}
if (step2 > 0) {
g+=step2-step;
near=tanf(oneunit*g) * range_inset * 36.0;
far =tanf(oneunit*g) * (x_range - range_inset) * 36.0;
if (near < y_range/2) {
fl_line(xmin+pix_inset,y0-(near*y_scale),xmax-pix_inset,y0-(far*y_scale));
sprintf(txt, "+%d", g);
fl_draw(txt, xmax-pix_inset+3, y0-(far*y_scale));
fl_line(xmin+pix_inset,y0+(near*y_scale),xmax-pix_inset,y0+(far*y_scale));
sprintf(txt, "-%d", g);
fl_draw(txt, xmax-pix_inset+3, y0+(far*y_scale));
}
}
fl_pop_clip();
}
DrawPlotData(ptype);
return;
}
@ -415,13 +544,13 @@ void PlotWindow::PlotEnergy(void){ @@ -415,13 +544,13 @@ void PlotWindow::PlotEnergy(void){
SetupPlot();
y0 = (ymax)-50;
DrawPlotBackground(1, " ft-lbs");
DrawPlotData(1);
DrawPlotBackground(PT_ENERGY, " ft-lbs");
DrawPlotData(PT_ENERGY);
return;
}
void PlotWindow::PlotVelocity(void){
void PlotWindow::PlotVelocity(int ptype){
// We need to find the max y-value to determine our y scale and y-ticks
@ -441,8 +570,8 @@ void PlotWindow::PlotVelocity(void){ @@ -441,8 +570,8 @@ void PlotWindow::PlotVelocity(void){
SetupPlot();
y0 = (ymax)-50;
DrawPlotBackground(2, " ft/s");
DrawPlotData(2);
DrawPlotBackground(PT_VELOCITY, " ft/s");
DrawPlotData(ptype);
return;
}
@ -489,3 +618,18 @@ void PlotWindow::cb_Mem2(Fl_Widget* o, void* v){ @@ -489,3 +618,18 @@ void PlotWindow::cb_Mem2(Fl_Widget* o, void* v){
}
void PlotWindow::cb_Guides(Fl_Widget* o, void* v){
PlotWindow* T = (PlotWindow*)v;
T->redraw();
}
void PlotWindow::cb_PlotType(Fl_Widget* o, void* v){
PlotWindow* T = (PlotWindow*)v;
T->redraw();
}

14
PlotWindow.h

@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
#include <FL/fl_draw.h>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Menu_Item.H>
#include <FL/Fl_Choice.H>
#include <stdio.h>
#include <stdlib.h>
@ -44,22 +45,21 @@ class PlotWindow : public Fl_Window { @@ -44,22 +45,21 @@ class PlotWindow : public Fl_Window {
void SetupPlot(void);
void DrawPlotBackground(int mode, const char *ylabel);
void DrawPlotData(int mode);
void PlotPath(void);
void PlotPath(int ptype);
void PlotEnergy(void);
void PlotVelocity(void);
void PlotVelocity(int ptype);
int x_click;
int y_click;
Fl_Round_Button* rbPlotPath;
Fl_Round_Button* rbPlotVelocity;
Fl_Round_Button* rbPlotEnergy;
Fl_Check_Button* btMem1;
Fl_Check_Button* btMem2;
Fl_Check_Button* btMem3;
Fl_Value_Slider* sl_x;
Fl_Choice* cScopeGuides;
Fl_Choice* cPlotType;
Fl_Menu_Bar* menu;
@ -71,6 +71,8 @@ class PlotWindow : public Fl_Window { @@ -71,6 +71,8 @@ class PlotWindow : public Fl_Window {
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);
static void cb_Guides(Fl_Widget* o, void* v);
static void cb_PlotType(Fl_Widget* o, void* v);
};

723
TargetWindow.cpp

@ -0,0 +1,723 @@ @@ -0,0 +1,723 @@
#include "TargetWindow.h"
#define PT_PATH 0
TargetWindow::TargetWindow(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;
begin();
this->size_range(0,0,1600,1600);
// Put a simple menu, for saving or closing the window.
menu = new Fl_Menu_Bar(0,0,w,25,"MENU");
Fl_Menu_Item items[]= {
{"&File",0,0,0,FL_SUBMENU},
{"Save as..",0,0,0,FL_SUBMENU | FL_MENU_INACTIVE},
{"PNG (.png)",0,(Fl_Callback*)cb_nothing,this,0},
{"JPEG (.jpg)",0,(Fl_Callback*)cb_nothing,this,0},
{"Bitmap (.bmp)",0,(Fl_Callback*)cb_nothing,this,0},
{"Adobe PDF (.pdf)",0,(Fl_Callback*)cb_nothing,this,0},
{0},
{"&Close",0,(Fl_Callback*)cb_Close,this,0},
{0},
{0}
};
menu->copy(items);
menu->down_box(FL_NO_BOX);
// Put in a range slider for selecting the chart's range.
sl_x = new Fl_Value_Slider(10,30,270,30,"Range (yds)");
sl_x->align(FL_ALIGN_RIGHT);
sl_x->value(200);
sl_x->minimum(30);
sl_x->maximum(gsln->MaxRows());
sl_x->type(FL_HOR_NICE_SLIDER);
sl_x->callback(cb_slUpdate,this);
sl_x->precision(0);
sl_x->step(10);
btPerspective=new Fl_Check_Button(380,35,20,20,"Perspective");
btPerspective->value(1);
btPerspective->callback(cb_Perspective,this);
btPerspective->tooltip("Apply perspective to bullet path");
Fl_Menu_Item guideitems[]={
{"None",0,(Fl_Callback*)cb_Guides, this, 0},
{"1,5 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
{"1-5 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
{"1-10 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
{"1-5,10 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
{"2,10 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
{"2,4,6,8,10 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
{"2-20 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
{"2-10,20 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
{"1-5 Degrees",0,(Fl_Callback*)cb_Guides, this, 0},
{"5,10,15 Degree",0,(Fl_Callback*)cb_Guides, this, 0},
{0}
};
cScopeGuides = new Fl_Choice(330,65,150,30,"Scope Guides");
cScopeGuides->copy(guideitems);
cScopeGuides->value(1);
btMem1 = new Fl_Check_Button(490,30,20,20,"Memory 1");
btMem2 = new Fl_Check_Button(490,50,20,20,"Memory 2");
btMem3 = new Fl_Check_Button(490,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());
#define TARGET_COUNT 15
Fl_Menu_Item plottypeitems[TARGET_COUNT+1]={
{"1/2\" Rings",0,(Fl_Callback*)cb_TargetType, this, 0},
{"1\" Rings",0,(Fl_Callback*)cb_TargetType, this, 0},
{"2\" Rings",0,(Fl_Callback*)cb_TargetType, this, 0},
{"ISSF 10m Air Pistol",0,(Fl_Callback*)cb_TargetType, this, 0},
{"ISSF 10m Air Rifle",0,(Fl_Callback*)cb_TargetType, this, 0},
{"ISSF 25m Rapid Fire Pistol",0,(Fl_Callback*)cb_TargetType, this, 0},
{"ISSF 25m Pistol",0,(Fl_Callback*)cb_TargetType, this, 0},
{"ISSF 50m Pistol",0,(Fl_Callback*)cb_TargetType, this, 0},
{"ISSF 50m Rifle",0,(Fl_Callback*)cb_TargetType, this, 0},
{"ISSF 300m Rifle",0,(Fl_Callback*)cb_TargetType, this, 0},
{"NRA 200 yard reduced for 100yd",0,(Fl_Callback*)cb_TargetType, this, 0},
{"NRA 200 yard",0,(Fl_Callback*)cb_TargetType, this, 0},
{"NRA 300 yard",0,(Fl_Callback*)cb_TargetType, this, 0},
{"NRA 600 yard",0,(Fl_Callback*)cb_TargetType, this, 0},
{"NRA 800 yard+",0,(Fl_Callback*)cb_TargetType, this, 0},
{0}
};
cTargetType = new Fl_Choice(60,65,160,30,"Target");
cTargetType->copy(plottypeitems);
cTargetType->value(1);
end();
show();
}
TargetWindow::~TargetWindow(void){
delete menu;
delete sl_x;
delete btMem1;
delete btMem2;
delete btMem3;
delete cScopeGuides;
delete cTargetType;
}
void TargetWindow::draw(void){
// Let the window draw itself first.
Fl_Window::draw();
PlotPath(PT_PATH);
}
int TargetWindow::handle(int evt){
if (evt==FL_PUSH && Fl::event_button1()){
x_click=Fl::event_x();
y_click=Fl::event_y();
this->redraw();
}
return Fl_Window::handle(evt);
}
/*
PlotMem()
Plot the requested data from a given GBCSolution (the current solution or a memory)
mode = 0: Path, 1: Energy, 2: Velocity, 3: Wind Drift
*/
void TargetWindow::PlotMem(GBCSolution* mem, int mode){
double x1,x2;
double y1,y2;
int px1, px2, py1,py2;
int range;
double t_range=sl_x->value();
int perspective=btPerspective->value()==1;
int start = perspective ? 20 : 0;
x1=mem->GetWindage(start);
y1=mem->GetPath(start);
// apply perspective
if (perspective) {
y1*=t_range / (double)(start+1);
x1*=t_range / (double)(start+1);
}
int m=this->gsln->MaxRows();
for (int n=start+1;n<=t_range && n<m ;n++){
x2=mem->GetWindage(n);
y2=mem->GetPath(n);
// apply perspective
if (perspective && n<t_range) {
y2*=t_range / (double)(n+1);
x2*=t_range / (double)(n+1);
}
// Translate the x,y values into scaled pixels.
px1=(int)(x0+x_scale*(double)x1);
px2=(int)(x0+x_scale*(double)x2);
py1=(int)((y0)-(double)y_scale*(double)y1);
py2=(int)((y0)-(double)y_scale*(double)y2);
if (n>50) {
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;
}
// draw a "bullet hole" at the impact point
//fl_color(FL_YELLOW);
double r=0.300/2; // should use half caliber here
double r2;
fl_pie(px2 - (r*x_scale), py2-(r*y_scale), r*2*x_scale, r*2*y_scale, 0, 360);
// and a 1-MOA ring (predicted impact variance)
// plus 0.1 MOA for every 100 yds over 100 yds (rough spin drift allowance)
r=(1.0 * t_range / 100.0)/2;
r2=r;
if (t_range > 100) r2 += (t_range - 100)/1000;
fl_color(fl_gray_ramp(FL_NUM_GRAY * 2 / 4));
fl_arc(px2 - (r2*x_scale), py2-(r*y_scale), r2*2*x_scale, r*2*y_scale, 0, 360);
}
/*
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
*/
int get_ticks(int range) {
if (range < 100) return 10;
if (range < 200) return 20;
if (range < 400) return 50;
if (range < 800) return 100;
if (range < 1600) return 200;
if (range < 3200) return 500;
if (range < 6400) return 1000;
if (range < 12800) return 2000;
if (range < 25600) return 4000;
if (range < 51200) return 8000;
if (range < 102400) return 16000;
if (range < 204800) return 32000;
return 64000;
}
void TargetWindow::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;
x_ticks=get_ticks(x_range);
y_ticks=get_ticks(y_range);
}
/*
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 TargetWindow::DrawPlotBackground(int mode, const char *ylabel){
// Draw the x-axis.
fl_color(FL_BLACK);
fl_line_style(FL_SOLID,2);
fl_line(xmin,y0,xmax,y0);
// Draw the y-axis
fl_line(x0,ymin,x0,ymax);
double px;
fl_color(FL_BLACK);
fl_line_style(FL_SOLID,1);
char txt[64];
int r=0;
double txtw=0;
int tick_top=ymax;
if (mode != PT_PATH) tick_top=y0;
// Draw X gridlines every x_ticks yds and label them.
if (xmin<0) {
for (px=x0;px>=xmin;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,tick_top);
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 X gridlines every x_ticks yds and label them.
for (px=x0;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,tick_top);
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%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
if (mode==PT_PATH) {
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%s",r*y_ticks, ylabel);
if (py+fl_height()/2<ymax) fl_draw(txt,xmin+10,(int)(py+fl_height()/2));
}
r++;
}
}
}
void TargetWindow::DrawPlotData(int mode){
fl_color(FL_BLUE);
fl_line_style(FL_SOLID,1);
PlotMem(this->gsln, mode);
if (btMem1->value()==1){
fl_color(FL_RED);
fl_line_style(FL_SOLID,1);
PlotMem(mem1, mode);
}
if (btMem2->value()==1){
fl_color(FL_GREEN);
fl_line_style(FL_SOLID,1);
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.
double click_label_x= ((x_click)-x0)/x_scale;
double click_label_y= (y0-y_click)/y_scale;
char lbl_point[20];
sprintf(lbl_point,"(%.2f,%.2f)",click_label_x, click_label_y);
fl_color(FL_RED);
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);
}
void TargetWindow::PlotPath(int ptype){
// We need to find the max and min y-values to determine our y scale and y-ticks
double _miny=0;
double _maxy=0;
double _minx=0;
double _maxx=0;
double minsize=20;
switch (cTargetType->value()) {
case 0: minsize=6;
break;
case 1: minsize=12;
break;
case 2: minsize=12;
break;
case 3: minsize=5;
break;
case 4: minsize=1.5;
break;
case 5:
case 6:
case 7: minsize=10;
break;
case 8: minsize=5;
break;
}
for (int e=0;e<sl_x->value();e++){
double v=gsln->GetPath(e);
if (v > _maxy) _maxy=v;
if (v < _miny) _miny=v;
v=gsln->GetWindage(e);
if (v > _maxx) _maxx=v;
if (v < _minx) _minx=v;
}
if (_minx > -minsize) _minx=-minsize;
else _minx *= 1.05;
if (_maxx < minsize) _maxx=minsize;
else _maxx *= 1.05;
x_range= _maxx - _minx;
_miny*=1.1;
if (_miny > -minsize) _miny=-minsize;
else _miny*=1.05;
if (_maxy < minsize) _maxy=minsize;
else _maxy*=1.05;
if (_maxy < fabs(_miny)/3) _maxy = fabs(_miny)/3;
else if (fabs(_miny) < (_maxy/3)) _miny = -_maxy/3;
y_range = _maxy - _miny;
// keep a fixed aspect ratio, and keep the target roughly central
if (y_range < x_range) y_range=x_range;
if (x_range < y_range) x_range=y_range;
t_range = (int)sl_x->value();
SetupPlot();
y0 = ymax + (_miny * y_scale);
x0 = xmin - (_minx * x_scale);
// Now do our custom drawing.
fl_draw_box(FL_FLAT_BOX,xmin,ymin,w()-10,h()-ymin-5,FL_WHITE);
fl_clip(5,100,w()-10,h()-105);
fl_line_style(FL_SOLID,1);
// Draw the score circles at 1 inch intervals
double r, aimring, *rings;
int i, score, aimring_drawn=0;
char txt[10];
#define mm(x) (x/25.4)
double aimrings[TARGET_COUNT]=
{ 3, 6, 12, // 1/2, 1, 2"
mm(59.5), mm(30.5), mm(500), mm(200), mm(200), // ISSF Pistol
mm(112.4), mm(600), // ISSF Rifle
6.35, 13, 19, 36, 44 // NRA
};
double rings_data[TARGET_COUNT][13] = {
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0 }, // 1/2" rings
{ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 0 }, // 1" rings
{ 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 0 }, // 2" rings
// ISSF 10m Air Pistol - 11.5mm 10 ring, increasing by 16mm
{ mm(6.5), mm(11.5), mm(27.5), mm(43.5), mm(59.5), mm(75.5), mm(91.5), mm(107.5), mm(123.5), mm(139.5), mm(155), 0 },
// ISSF 10m Air Rifle
{ -0.01, mm(0.5), mm(5.5), mm(10.5), mm(15.5), mm(20.5), mm(25.5), mm(30.5), mm(35.5), mm(40.5), mm(45.5), 0 },
// ISSF 25m Rapid Pistol - 100mm 10 ring, increasing by 100mm
{ mm(50), mm(100), mm(180), mm(260), mm(340), mm(420), mm(500), 0 },
// ISSF 25m Precision Pistol - 50mm 10 ring, increasing by 50mm
{ mm(25), mm(50), mm(100), mm(150), mm(200), mm(250), mm(300), mm(350), mm(400), mm(450), mm(500), 0 },
// ISSF 50m Precision Pistol - 50mm 10 ring, increasing by 50mm
{ mm(25), mm(50), mm(100), mm(150), mm(200), mm(250), mm(300), mm(350), mm(400), mm(450), mm(500), 0 },
// ISSF 50m Rifle
{ mm(5.2), mm(10.4), mm(26.4), mm(42.4), mm(58.4), mm(74.4), mm(90.4), mm(106.4), mm(122.4), mm(138.4), mm(154.4), 0 },
// ISSF 300m Rifle
{ mm(50), mm(100), mm(200), mm(300), mm(400), mm(500), mm(600), mm(700), mm(800), mm(900), mm(1000), 0 },