Skip to content

Instantly share code, notes, and snippets.

@trey
Last active January 15, 2019 03:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trey/10c1513cef52d91e84086723a654b8ae to your computer and use it in GitHub Desktop.
Save trey/10c1513cef52d91e84086723a654b8ae to your computer and use it in GitHub Desktop.
Crop Factor
const CropFactor = React.createClass({
getInitialState() {
return {
height: 24,
width: 36,
focalLength: 50,
fStop: 2.0,
diagonal: this.diagonal(24, 36),
cropFactor: this.cropFactor(24, 36),
equivalentFocalLength: 50,
equivalentFStop: 2.0,
preset: 'full',
aspectRatioDecimal: this.aspectRatioDecimal(24, 36),
aspectRatio: this.aspectRatio(24, 36),
};
},
cropFactor(height, width) {
const fullFrame = this.diagonal(24, 36);
return fullFrame / this.diagonal(height, width);
},
diagonal(height, width) {
return Math.sqrt(Math.pow(height, 2) + Math.pow(width, 2));
},
showAspectRatio(height, width) {
const aspectRatio = this.aspectRatio(height, width);
const numeratorLength = aspectRatio[0].toString().length;
const denominatorLength = aspectRatio[1].toString().length;
return (numeratorLength <= 3 && denominatorLength <= 3);
},
aspectRatioDecimal(height, width) {
if (width > height) {
return width / height;
} else {
return height / width;
}
},
aspectRatio(height, width) {
// https://stackoverflow.com/a/4652513/96257
// https://en.wikipedia.org/wiki/Euclidean_algorithm
function reduce(numerator, denominator){
var findGcd = function findGcd(a, b){
return b ? findGcd(b, a % b) : a;
};
gcd = findGcd(numerator, denominator);
return [numerator / gcd, denominator / gcd];
}
return reduce(height, width);
},
equivalentFStop(height, width, fStop) {
const wideSide = (width > height) ? width : height;
return fStop * 36 / wideSide;
},
handleChange(e, ref) {
const cropFactor = this.cropFactor(this.refs.height.value, this.refs.width.value);
const equivalentFocalLength = cropFactor * this.refs.focalLength.value;
const equivalentFStop = this.equivalentFStop(this.refs.height.value, this.refs.width.value, this.refs.fStop.value);
const diagonal = this.diagonal(this.refs.height.value, this.refs.width.value);
const aspectRatio = this.aspectRatio(this.refs.height.value, this.refs.width.value);
const aspectRatioDecimal = this.aspectRatioDecimal(this.refs.height.value, this.refs.width.value);
this.setState({
preset: (ref !== 'focalLength' && ref !== 'fStop') ? 'blank' : this.state.preset,
height: this.refs.height.value,
width: this.refs.width.value,
focalLength: this.refs.focalLength.value,
fStop: this.refs.fStop.value,
diagonal,
cropFactor,
equivalentFocalLength,
equivalentFStop,
aspectRatio,
aspectRatioDecimal,
});
},
handlePresetChange() {
let height;
let width;
const preset = this.refs.preset.value;
const focalLength = this.refs.focalLength.value;
const fStop = this.refs.fStop.value;
switch(preset) {
case 'blank':
width = this.refs.width.value;
height = this.refs.height.value;
break;
case 'nikon-apsc':
width = 23.5;
height = 15.7;
break;
case 'canon-apsc':
width = 22.3;
height = 14.9;
break;
case 'sony-apsc':
width = 23.5;
height = 15.6;
break;
case 'fuji-apsc':
width = 23.6;
height = 15.6;
break;
case 'fuji-mf':
width = 43.8;
height = 32.9;
break;
case 'hasselblad-xpan':
width = 65;
height = 24;
break;
case '645':
width = 45;
height = 60;
break;
case '66':
width = 60;
height = 60;
break;
case '67':
width = 70;
height = 60;
break;
case '68':
width = 80;
height = 60;
break;
case '69':
width = 90;
height = 60;
break;
case 'full':
width = 36;
height = 24;
break;
case 'm43':
width=18;
height=13.5;
break;
case '45':
width=127;
height=101.6;
break;
case '810':
width=254;
height=203.2;
break;
case 'polaroid':
// https://support.polaroidoriginals.com/hc/en-us/articles/115012363647-Polaroid-Originals-Photo-Dimensions
width=78.94;
height=76.801;
break;
case 'polaroid-spectra':
width=73.41;
height=90.78;
break;
case 'instax-mini':
// http://www.fujifilm.com/products/instant_photo/films/instax_mini/
width=46;
height=62;
break;
case 'instax-wide':
// http://www.fujifilm.com/products/instant_photo/films/instax/
width=99;
height=62;
break;
case 'instax-square':
// http://www.fujifilm.com/products/instant_photo/films/instax_square/
width=62;
height=62;
break;
case 'packfilm-100':
// http://www.fujifilm.com/products/instant_photo/films/fp_100c/
width=73;
height=95;
break;
}
const cropFactor = this.cropFactor(height, width);
const equivalentFocalLength = cropFactor * focalLength;
const equivalentFStop = this.equivalentFStop(height, width, fStop);
const diagonal = this.diagonal(height, width);
const aspectRatio = this.aspectRatio(height, width);
const aspectRatioDecimal = this.aspectRatioDecimal(height, width);
this.setState({
height,
width,
diagonal,
cropFactor,
equivalentFocalLength,
equivalentFStop,
preset,
aspectRatio,
aspectRatioDecimal,
});
},
render() {
return (
<div>
<fieldset>
<legend>1</legend>
<div>
<label>Select format:&nbsp;
<select ref="preset" value={this.state.preset} onChange={this.handlePresetChange}>
<option value="blank">Choose a format</option>
<option value="full">Full Frame</option>
<optgroup label="APS-C">
<option value="nikon-apsc">Nikon DX</option>
<option value="canon-apsc">Canon APS-C</option>
<option value="sony-apsc">Sony APS-C</option>
<option value="fuji-apsc">Fuji X-Trans</option>
</optgroup>
<optgroup label="&ldquo;Medium Format&rdquo; Digital">
<option value="fuji-mf">Fuji G Mount / Hasselblad X1D</option>
</optgroup>
<optgroup label="Smaller than APS-C">
<option value="m43">Micro Four Thirds</option>
</optgroup>
<optgroup label="Panoramic">
<option value="hasselblad-xpan">Hasselblad XPan / Fuji TX-1</option>
</optgroup>
<optgroup label="Medium Format">
<option value="645">6x4.5</option>
<option value="66">6x6</option>
<option value="67">6x7</option>
<option value="68">6x8</option>
<option value="69">6x9</option>
</optgroup>
<optgroup label="Large Format">
<option value="45">4x5</option>
<option value="810">8x10</option>
</optgroup>
<optgroup label="Instant">
<option value="polaroid">Polaroid</option>
<option value="polaroid-spectra">Polaroid Spectra</option>
<option value="instax-mini">Instax Mini</option>
<option value="instax-wide">Instax Wide</option>
<option value="instax-square">Instax Square</option>
<option value="packfilm-100">Type 100 Packfilm (FP-100C)</option>
</optgroup>
</select></label>
</div>
<div>
<div className="subdued">or</div>
<div>
enter <label>height:&nbsp;
<input ref="height" value={this.state.height} onChange={this.handleChange} size="4" /> <span className="subdued">mm</span></label>&nbsp;
&times; <label>width: <input ref="width" value={this.state.width} onChange={this.handleChange} size="4" /> <span className="subdued">mm</span></label>
</div>
<div className="subdued">(diagonal = {(this.state.diagonal).toFixed(2)}mm)</div>
</div>
</fieldset>
<fieldset>
<legend>2</legend>
<div><label>Enter focal length: <input ref="focalLength" value={this.state.focalLength} onChange={(e) => this.handleChange(e, 'focalLength')} size="4" /> <span className="subdued">mm</span></label></div>
<div><label>Enter ƒ/stop: <span className="subdued">ƒ/</span> <input ref="fStop" value={this.state.fStop} onChange={(e) => this.handleChange(e, 'fStop')} size="4" /></label></div>
</fieldset>
<fieldset>
<legend>3</legend>
<table>
<tr>
<td><a target="_blank" href="https://www.bhphotovideo.com/explora/photography/tips-and-solutions/understanding-crop-factor">Crop factor</a></td>
<td>&times; <b>{(this.state.cropFactor).toFixed(2)}</b></td>
</tr>
<tr>
<td><a target="_blank" href="https://en.wikipedia.org/wiki/135_film#Image_format">8-perf 35mm</a> equivalent focal length</td>
<td><b>{(this.state.equivalentFocalLength).toFixed(2)}</b>mm</td>
</tr>
<tr>
<td><a target="_blank" href="https://en.wikipedia.org/wiki/135_film#Image_format">8-perf 35mm</a> equivalent ƒ/stop (same <a target="_blank" href="http://yedlin.net/lens_blur.html">blur circles</a>)</td>
<td>ƒ/<b>{this.state.equivalentFStop.toFixed(1)}</b></td>
</tr>
<tr>
<td><a target="_blank" href="https://en.wikipedia.org/wiki/Aspect_ratio_(image)">Aspect ratio</a></td>
<td>
{(this.showAspectRatio(this.state.height, this.state.width)) ?
<span>
<b>{`${this.state.aspectRatio[0]}:${this.state.aspectRatio[1]}`}</b>
&nbsp;or&nbsp;
</span>
:null}
<b>{(this.state.aspectRatioDecimal).toFixed(2)}</b>
</td>
</tr>
</table>
</fieldset>
{/*(this.showAspectRatio(this.state.height, this.state.width)) ?
<div style={{
height: this.state.aspectRatio[0] * 20,
width: this.state.aspectRatio[1] * 20,
background: 'red',
}} />
:null*/}
<hr />
<p>Made by <a target="_blank" href="https://treylabs.com">Trey</a>.</p>
</div>
);
}
});
ReactDOM.render(
<CropFactor />,
document.body
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
body {
padding: 30px;
font-size: 14px;
}
fieldset {
margin-bottom: 10px;
padding: 10px 15px 18px;
max-width: 450px;
b, input, select { font-size: 16px; }
div {
margin: 5px 0 0;
&:first-of-type { margin: 0; }
}
}
.subdued {
color: #888;
font-size: 12px;
}
hr { margin: 50px 0; }
table {
border-spacing: 0;
width: 100%;
}
td {
border-bottom: 1px solid #ccc;
padding: 8px 0;
line-height: 1.5;
&:last-child { padding-left: 10px; }
}
tr:last-child td { border: 0; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment