Skip to content

Commit

Permalink
Merge pull request #40 from jcw780/dispersion
Browse files Browse the repository at this point in the history
Dispersion
  • Loading branch information
jcw780 authored Jul 31, 2020
2 parents 17d3d18 + 6ca5579 commit 8511555
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 44 deletions.
14 changes: 14 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

Generated by [`auto-changelog`](/~https://github.com/CookPete/auto-changelog).

## [v2.2.0](/~https://github.com/jcw780/wows_ballistics/compare/v2.1.1...v2.2.0) - 2020-07-31

### Commits

- added dispersion charts [`41e6d00`](/~https://github.com/jcw780/wows_ballistics/commit/41e6d00610ce77b5a40c965510a22bbb81eeb92f)
- feature: added dispersion parameters; separated charts with <hr/> [`a4cc5bd`](/~https://github.com/jcw780/wows_ballistics/commit/a4cc5bd1e06ebe25800bc889ccd08ffe42d35a1b)
- refactored shellForms.getDefaultData; pass dispersion parameters into formData [`88fab0c`](/~https://github.com/jcw780/wows_ballistics/commit/88fab0c4ee54bc1ff909ac3c5b400097a4fb2575)
- feature: added tooltips for understood parameters [`bfe31ee`](/~https://github.com/jcw780/wows_ballistics/commit/bfe31eed7c6dab5de75ad7f7342b327eefc6f51d)
- update version [`17d3d18`](/~https://github.com/jcw780/wows_ballistics/commit/17d3d18a93c53a4304a7a058049553132df4288a)
- pass dispersion parameters to shellForms [`763e85b`](/~https://github.com/jcw780/wows_ballistics/commit/763e85b700f15f7bd0d00dd9f35345e7b32af9e8)
- fix: bug where dispersion charts do not update [`24081cf`](/~https://github.com/jcw780/wows_ballistics/commit/24081cfbc729fd079f88e2e30d6825653301cbd1)
- Update README.md [`9886684`](/~https://github.com/jcw780/wows_ballistics/commit/988668484526734574985cff2a099d339d204dbb)
- feature: added dispersion charts to initalData [`97d1ef4`](/~https://github.com/jcw780/wows_ballistics/commit/97d1ef4a2626dc34438b30779b470f560de15028)

## [v2.1.1](/~https://github.com/jcw780/wows_ballistics/compare/v2.1.0...v2.1.1) - 2020-07-30

### Commits
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
### Calculate AP performance post penetration
- Shell detonation distance after penetration
- Checking whether the armor is thick enough to arm the shell
### Predict Ship Dispersion *Experimental*
- Predicts maximum and standard deviation of horizontal dispersion
- Predicts maximum and standard deviation of vertical dispersion
## Development Status
- [Issues](/~https://github.com/jcw780/wows_ballistics/issues)
- Feel free to open an issue for questions, bugs, feature requests etc.
Expand All @@ -35,7 +38,6 @@
- Calculations may be refined if contradicting data is provided
### Future Features
- Multiple Targets
- Dispersion?



2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wows_ballistics",
"version": "2.1.1",
"version": "2.2.0",
"private": true,
"homepage": "http://jcw780.github.io/wows_ballistics",
"dependencies": {
Expand Down
67 changes: 66 additions & 1 deletion src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class App extends React.Component<{},{}> {
impact : [],
angle : [],
post : [],
dispersion: [],
});

// Wasm
Expand Down Expand Up @@ -90,6 +91,12 @@ class App extends React.Component<{},{}> {
notFused: [],
fused: [],
},
dispersion: {
horizontal: [],
horizontalStd: [],
vertical: [],
verticalStd: [],
},
numShells : 2, names : [], colors : [],
targets : Array<T.targetDataNoAngleT>(1), angles : [],
refAngles : [], refLabels : [],
Expand Down Expand Up @@ -199,18 +206,76 @@ class App extends React.Component<{},{}> {
for(let j=0; j<numShells; ++j){
this.initializePoint(calculatedData.impact, j);
this.initializePoint(calculatedData.angle, j);
this.initializePoint(calculatedData.dispersion, j);
for(let i=0; i<numAngles; ++i){
calculatedData.post.notFused[i+j*numAngles] = [];
calculatedData.post.fused[i+j*numAngles] = [];
}
}


let maxDist = 0; //Maximum Distance for shipWidth
// Converts flat array data format to {x, y} format for chart.js
for(let j=0; j<numShells; ++j){ // iterate through shells
const currentShell = shellData[j];
//Dispersion
const idealRadius = currentShell.idealRadius!, minRadius = currentShell.minRadius!,
taperDist = currentShell.taperDist! / 1000, sigma = currentShell.sigmaCount!,
radiusOnZero = currentShell.radiusOnZero!, radiusOnDelim = currentShell.radiusOnDelim!,
radiusOnMax = currentShell.radiusOnMax!, delim = currentShell.delim!;
//Horizontal
const hSlope = idealRadius - minRadius, hConst = minRadius * 30;
const taperSlope = (hSlope * (taperDist) + hConst) / taperDist!;
//Vertical
const delimSplit = delim * 30;
const zdSlope = (radiusOnDelim - radiusOnZero) / delimSplit;
const zdConst = radiusOnZero;

const dmSlope = (radiusOnMax - radiusOnDelim) / (30 - delimSplit);
const dmConst = radiusOnDelim - delimSplit * dmSlope;

for(let i=0; i<impactSize; ++i){ // iterate through points at each range
const dist : number = instance.getImpactPoint(i, arrayIndices.impactDataIndex.distance, j);
maxDist = Math.max(maxDist, dist);
this.makeImpactPoints(j, i, dist); this.makeAnglePoints(j, i, dist);
//Dispersion
//Note: Sigma correction is technically an approximation.
//It's just that it is very similar to the exact solution.
//And the exact solution is computationally intensive
//Exact Solution:
//std dev = 2 * k sqrt( erf(sigma / sqrt(2))/(4sigma^2) - e^(-sigma^2/2)/(2sqrt(2pi)sigma) + n.cdf(-sigma, 1)/12)
//Approximation:
//std dev = k / sigma

const distKm = dist / 1000;
let maxDispersion = 0;
if(distKm > taperDist){
maxDispersion = hSlope * distKm + hConst;
}else{
maxDispersion = taperSlope * distKm;
}
calculatedData.dispersion.horizontal[j].push({
x: dist, y: maxDispersion
});
calculatedData.dispersion.horizontalStd[j].push({
x: dist, y: maxDispersion / sigma
});
let maxVertical = maxDispersion /
Math.sin(this.instance.getImpactPoint(i, arrayIndices.impactDataIndex.impactAHR, j) * - 1);
if(distKm < delimSplit){
maxVertical *= (zdSlope * distKm + zdConst);
}else{
maxVertical *= (dmSlope * distKm + dmConst);
}
calculatedData.dispersion.vertical[j].push({
x: dist, y: maxVertical
});
calculatedData.dispersion.verticalStd[j].push({
x: dist, y: maxVertical / sigma
});


/*Impact*/this.makeImpactPoints(j, i, dist); /*Angle*/this.makeAnglePoints(j, i, dist);
//Post-Pen
for(let k=0; k<numAngles; ++k){
const detDist : number
= instance.getPostPenPoint(i, arrayIndices.postPenDataIndex.x, k, j);
Expand Down
96 changes: 80 additions & 16 deletions src/components/Charts/Charts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ export class ChartGroup extends React.Component<chartGroupProps>{
React.createRef<SingleChart>(), ''],
[{data: {datasets : Array<any>(),}, options: {}},
React.createRef<SingleChart>(), ''],
],
dispersion: [
[{data: {datasets : Array<any>(),}, options: {}},
React.createRef<SingleChart>(), '**Experimental** Horizontal Dispersion'],
[{data: {datasets : Array<any>(),}, options: {}},
React.createRef<SingleChart>(), '**Experimental** Vertical Dispersion'],
]
}
groupRefs = {
Expand All @@ -286,6 +292,7 @@ export class ChartGroup extends React.Component<chartGroupProps>{
Time: (x, y) => {return `(${x}m, ${y}s)`;},
angle: (x, y) => {return `(${x}m, ${y}°)`;},
detDist: (x, y) => {return `(${x}m, ${y}m)`;},
dispersion: (x, y) => {return `(${x}m, ${y}m)`;},
}

constructor(props){
Expand Down Expand Up @@ -356,7 +363,7 @@ export class ChartGroup extends React.Component<chartGroupProps>{
const targetInclination = `Vertical Inclination: ${graphData.targets[0].inclination}°`;

//Static Charts [Never changes in number]
const staticChartTypes = Object.freeze(['impact', 'angle']);
const staticChartTypes = Object.freeze(['impact', 'angle', 'dispersion']);
//Impact Charts
const configImpact = this.chartConfigs.impact;
const setupImpact = (row : configsT) => {
Expand Down Expand Up @@ -393,9 +400,24 @@ export class ChartGroup extends React.Component<chartGroupProps>{
}

const angleNameTemplate = (name: string) : string => `${name} | ${targetedArmor} | ${targetInclination}`;
//Dispersion Charts
const configDispersion = this.chartConfigs.dispersion;
const setupDispersion = (row : configsT) => {
return {
title: {display: true, text: row.title},
scales: {xAxes: xAxesDistance,
yAxes: [{id: "dispersion", postition: "left",
scaleLabel: {display: true, labelString: "Dispersion (m)",},
ticks:{min: 0}
}]
},
legend: legend,
tooltips: {callbacks: {label: this.callbackFunction, labelColor: this.callbackColor}},
}
}

//Colons are used to denote split between label and name
const staticOptionSetup : Record<'impact' | 'angle', [(configsT) => any, configsT[]]> = Object.freeze({
const staticOptionSetup : Record<'impact' | 'angle' | 'dispersion', [(configsT) => any, configsT[]]> = Object.freeze({
impact: [setupImpact,
[
{title: configImpact[0][singleChartIndex.name], axes: [
Expand Down Expand Up @@ -442,6 +464,24 @@ export class ChartGroup extends React.Component<chartGroupProps>{
]}
]
],
dispersion: [setupDispersion,
[
{title: configDispersion[0][singleChartIndex.name], axes: [
{id: 'dispersion',
lines: [
{lineLabel: 'Max: ', data: 'horizontal'},
{lineLabel: 'Std: ', data: 'horizontalStd'},
]},
]},
{title: configDispersion[1][singleChartIndex.name], axes: [
{id: 'dispersion',
lines: [
{lineLabel: 'Max: ', data: 'vertical'},
{lineLabel: 'Std: ', data: 'verticalStd'},
]},
]},
]
]
});

//Post-Penetration Charts
Expand Down Expand Up @@ -486,21 +526,23 @@ export class ChartGroup extends React.Component<chartGroupProps>{

const assignPredefined = (shellIndex: number, name: string, target, configs : configsT[],
graphData, colors : string[]) => {
for(const [rowIndex, chart] of target.entries()){
let counter = 0;
const CRA = configs[rowIndex].axes.entries();
for(const[, axis] of CRA){
const ALE = axis.lines.entries();
for(const[, line] of ALE){
chart[singleChartIndex.config].data.datasets.push(addLine(
graphData[line.data][shellIndex],
line.lineLabel + name,
axis.id,
colors[counter]));
counter++;
if(graphData !== undefined){
for(const [rowIndex, chart] of target.entries()){
let counter = 0;
const CRA = configs[rowIndex].axes.entries();
for(const[, axis] of CRA){
const ALE = axis.lines.entries();
for(const[, line] of ALE){
chart[singleChartIndex.config].data.datasets.push(addLine(
graphData[line.data][shellIndex],
line.lineLabel + name,
axis.id,
colors[counter]));
counter++;
}
}
}
}
}
}
}

const generateStatic = (i : number, name : string, colors : string[]) => {
Expand Down Expand Up @@ -641,11 +683,15 @@ export class ChartGroup extends React.Component<chartGroupProps>{
for(const [, chart] of this.chartConfigs.angle.entries()){
injectData(chart);
}
for(const [, chart] of this.chartConfigs.dispersion.entries()){
injectData(chart);
}

//Update Charts
if(forceUpdate){ //For disabling rerender in constructor [will be rendered anyways]
this.updateCharts('impact'); //Only need to update charts not surrounding components
this.updateCharts('angle');
this.updateCharts('dispersion');
if(updatePost){
this.updateCharts('post');
}else{
Expand Down Expand Up @@ -697,6 +743,7 @@ export class ChartGroup extends React.Component<chartGroupProps>{
</div>
</GeneralTooltip>
{this.addChart('impact')}
<hr/>
<GeneralTooltip title="Angle Charts" content={
<>
Shows at what target angles and ranges shells will: <br/>
Expand All @@ -712,6 +759,7 @@ export class ChartGroup extends React.Component<chartGroupProps>{
</div>
</GeneralTooltip>
{this.addChart('angle')}
<hr/>
<GeneralTooltip title="Post-Penetration Charts" content={
<>
Show how far shells would travel into a ship after penetrating armor. <br/>
Expand All @@ -728,6 +776,22 @@ export class ChartGroup extends React.Component<chartGroupProps>{
</div>
</GeneralTooltip>
{this.addChart('post')}
<hr/>
<GeneralTooltip title="Dispersion Charts" content={
<>
Predicts maximum and standard deviation of dispersion. <br/>
- Horizontal Axis <br/>
- Vertical Axis <br/>
*Note: Still experimental - results may differ from the game <br/>
and will be corrected if errors are reported
</>
}>
<div className="tooltip-target">
<h3 style={{textAlign: "center", display:"inline-block"}}><i style={{color: 'red'}}>**Experimental**</i> Dispersion Charts</h3>
<Icon name='question circle outline' color='grey'/>
</div>
</GeneralTooltip>
{this.addChart('dispersion')}
</div>
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/components/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ export class NavbarCustom extends React.Component<{links: T.linkT}>{
links={links['post']}
ref={this.navDropdownContainers[3]}
/>
<NavDropdownContainer
title="Dispersion Charts"
links={links['dispersion']}
ref={this.navDropdownContainers[3]}
/>
</Nav>
<Nav>
<Suspense fallback={<div>Loading...</div>}>
Expand Down
7 changes: 6 additions & 1 deletion src/components/ShellForms/DefaultForms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,12 @@ export class DefaultShips extends React.PureComponent
}
const sendData = () => {
const shellName = qDataS[nation][type][ship].artillery[artillery].shells[shellType];
this.props.sendDefault(dData.queriedData.shells[shellName], ship);
const dispersionData = {}
const artilleryData = qDataS[nation][type][ship].artillery[artillery];
for(const [k,v] of Object.entries(artilleryData)){
if(k !== 'shells') dispersionData[k] = v;
}
this.props.sendDefault({...dData.queriedData.shells[shellName], ...dispersionData}, ship);
}
const queries = [
queryNation, queryType, queryShip,
Expand Down
Loading

0 comments on commit 8511555

Please sign in to comment.