Recalculate EV on ND change

Recalculate EV on ND change

`PhotographyValue` -> `PhotographyStopValue`
This commit is contained in:
Vadim 2022-12-04 22:12:52 +03:00
parent dbbc5738ce
commit 1c344da29f
17 changed files with 134 additions and 34 deletions

View file

@ -29,6 +29,8 @@ class MessageLookup extends MessageLookupByLibrary {
"iso": MessageLookupByLibrary.simpleMessage("ISO"),
"keepsScreenOn":
MessageLookupByLibrary.simpleMessage("Keeps screen on"),
"nd": MessageLookupByLibrary.simpleMessage("ND"),
"none": MessageLookupByLibrary.simpleMessage("None"),
"openSettings": MessageLookupByLibrary.simpleMessage("Open settings"),
"permissionNeeded":
MessageLookupByLibrary.simpleMessage("Permission needed"),

View file

@ -110,6 +110,26 @@ class S {
);
}
/// `ND`
String get nd {
return Intl.message(
'ND',
name: 'nd',
desc: '',
args: [],
);
}
/// `None`
String get none {
return Intl.message(
'None',
name: 'none',
desc: '',
args: [],
);
}
/// `Cancel`
String get cancel {
return Intl.message(

View file

@ -6,6 +6,8 @@
"fastestExposurePair": "Fastest",
"slowestExposurePair": "Slowest",
"iso": "ISO",
"nd": "ND",
"none": "None",
"cancel": "Cancel",
"select": "Select",
"settings": "Settings",

View file

@ -1,11 +1,8 @@
part of 'photography_value.dart';
import 'photography_value.dart';
class ApertureValue extends PhotographyValue<double> {
class ApertureValue extends PhotographyStopValue<double> {
const ApertureValue(super.rawValue, super.stopType);
@override
double get value => rawValue;
@override
String toString() {
final buffer = StringBuffer("f/");

View file

@ -1,4 +1,5 @@
import 'photography_value.dart';
import 'aperture_value.dart';
import 'shutter_speed_value.dart';
class ExposurePair {
final ApertureValue aperture;

View file

@ -1,11 +1,8 @@
part of 'photography_value.dart';
import 'photography_value.dart';
class IsoValue extends PhotographyValue<int> {
class IsoValue extends PhotographyStopValue<int> {
const IsoValue(super.rawValue, super.stopType);
@override
int get value => rawValue;
@override
String toString() => value.toString();
}

31
lib/models/nd_value.dart Normal file
View file

@ -0,0 +1,31 @@
import 'package:lightmeter/utils/log_2.dart';
import 'photography_value.dart';
class NdValue extends PhotographyValue<int> {
const NdValue(super.rawValue);
double get stopReduction => value == 0 ? 0.0 : log2(value);
@override
String toString() => 'ND$value';
}
/// https://shuttermuse.com/neutral-density-filter-numbers-names/
const List<NdValue> ndValues = [
NdValue(0),
NdValue(2),
NdValue(4),
NdValue(8),
NdValue(16),
NdValue(32),
NdValue(64),
NdValue(128),
NdValue(256),
NdValue(512),
NdValue(1024),
NdValue(2048),
NdValue(4096),
NdValue(8192),
NdValue(10000),
];

View file

@ -1,19 +1,20 @@
part 'aperture_value.dart';
part 'iso_value.dart';
part 'shutter_speed_value.dart';
enum StopType { full, half, third }
abstract class PhotographyValue<T> {
final T rawValue;
final StopType stopType;
const PhotographyValue(this.rawValue, this.stopType);
const PhotographyValue(this.rawValue);
T get value;
T get value => rawValue;
}
extension PhotographyValues<T extends PhotographyValue> on List<T> {
abstract class PhotographyStopValue<T> extends PhotographyValue<T> {
final StopType stopType;
const PhotographyStopValue(super.rawValue, this.stopType);
}
extension PhotographyStopValues<T extends PhotographyStopValue> on List<T> {
List<T> whereStopType(StopType stopType) {
switch (stopType) {
case StopType.full:

View file

@ -1,6 +1,6 @@
part of 'photography_value.dart';
import 'photography_value.dart';
class ShutterSpeedValue extends PhotographyValue<double> {
class ShutterSpeedValue extends PhotographyStopValue<double> {
final bool isFraction;
const ShutterSpeedValue(super.rawValue, this.isFraction, super.stopType);

View file

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:lightmeter/models/photography_value.dart';
import 'package:lightmeter/res/dimens.dart';
class ExposurePaitListItem<T extends PhotographyValue> extends StatelessWidget {
class ExposurePaitListItem<T extends PhotographyStopValue> extends StatelessWidget {
final T value;
final bool tickOnTheLeft;

View file

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/models/exposure_pair.dart';
import 'package:lightmeter/models/iso_value.dart';
import 'package:lightmeter/models/nd_value.dart';
import 'package:lightmeter/models/photography_value.dart';
import 'package:lightmeter/res/dimens.dart';
@ -12,13 +14,15 @@ import 'models/reading_value.dart';
class MeteringTopBar extends StatelessWidget {
static const _columnsCount = 3;
final _isoDialogKey = GlobalKey<AnimatedDialogState>();
final _ndDialogKey = GlobalKey<AnimatedDialogState>();
final ExposurePair? fastest;
final ExposurePair? slowest;
final double ev;
final IsoValue iso;
final double nd;
final NdValue nd;
final ValueChanged<IsoValue> onIsoChanged;
final ValueChanged<NdValue> onNdChanged;
MeteringTopBar({
required this.fastest,
@ -27,6 +31,7 @@ class MeteringTopBar extends StatelessWidget {
required this.iso,
required this.nd,
required this.onIsoChanged,
required this.onNdChanged,
super.key,
});
@ -137,10 +142,28 @@ class MeteringTopBar extends StatelessWidget {
),
),
const _InnerPadding(),
ReadingContainer.singleValue(
value: ReadingValue(
label: 'ND',
value: nd.toString(),
AnimatedDialog(
key: _ndDialogKey,
closedChild: ReadingContainer.singleValue(
value: ReadingValue(
label: S.of(context).nd,
value: nd.value.toString(),
),
),
openedChild: MeteringScreenDialogPicker(
title: S.of(context).nd,
initialValue: nd,
values: ndValues,
itemTitleBuilder: (context, value) => Text(
value.value == 0 ? S.of(context).none : value.value.toString(),
style: Theme.of(context).textTheme.bodyLarge,
),
onCancel: () {
_ndDialogKey.currentState?.close();
},
onSelect: (value) {
_ndDialogKey.currentState?.close().then((_) => onNdChanged(value));
},
),
),
],

View file

@ -1,8 +1,12 @@
import 'dart:math';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lightmeter/models/aperture_value.dart';
import 'package:lightmeter/models/exposure_pair.dart';
import 'package:lightmeter/models/iso_value.dart';
import 'package:lightmeter/models/nd_value.dart';
import 'package:lightmeter/models/photography_value.dart';
import 'package:lightmeter/models/shutter_speed_value.dart';
import 'package:lightmeter/utils/log_2.dart';
import 'metering_event.dart';
@ -21,11 +25,12 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
iso: isoValues.where((element) => element.value == 100).first,
ev: 21.3,
evCompensation: 0.0,
nd: 0.0,
nd: ndValues.first,
exposurePairs: [],
),
) {
on<IsoChangedEvent>(_onIsoChanged);
on<NdChangedEvent>(_onNdChanged);
on<MeasureEvent>(_onMeasure);
add(const MeasureEvent());
@ -42,10 +47,19 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
));
}
void _onNdChanged(NdChangedEvent event, Emitter emit) {
final ev = state.ev - event.ndValue.stopReduction + state.nd.stopReduction;
emit(MeteringState(
iso: state.iso,
ev: ev,
evCompensation: state.evCompensation,
nd: event.ndValue,
exposurePairs: _buildExposureValues(ev),
));
}
/// https://www.scantips.com/lights/exposurecalc.html
void _onMeasure(_, Emitter emit) {
double log2(double x) => log(x) / log(2);
final aperture = _apertureValues[_random.nextInt(_apertureValues.length)];
final shutterSpeed = _shutterSpeedValues[_random.nextInt(_shutterSpeedValues.thirdStops().length)];
final iso = _isoValues[_random.nextInt(_isoValues.thirdStops().length)];

View file

@ -1,4 +1,5 @@
import 'package:lightmeter/models/photography_value.dart';
import 'package:lightmeter/models/iso_value.dart';
import 'package:lightmeter/models/nd_value.dart';
abstract class MeteringEvent {
const MeteringEvent();
@ -10,6 +11,12 @@ class IsoChangedEvent extends MeteringEvent {
const IsoChangedEvent(this.isoValue);
}
class NdChangedEvent extends MeteringEvent {
final NdValue ndValue;
const NdChangedEvent(this.ndValue);
}
class MeasureEvent extends MeteringEvent {
const MeasureEvent();
}

View file

@ -113,6 +113,7 @@ class _MeteringScreenState extends State<MeteringScreen> {
iso: state.iso,
nd: state.nd,
onIsoChanged: (value) => context.read<MeteringBloc>().add(IsoChangedEvent(value)),
onNdChanged: (value) => context.read<MeteringBloc>().add(NdChangedEvent(value)),
);
}

View file

@ -1,11 +1,12 @@
import 'package:lightmeter/models/exposure_pair.dart';
import 'package:lightmeter/models/photography_value.dart';
import 'package:lightmeter/models/iso_value.dart';
import 'package:lightmeter/models/nd_value.dart';
class MeteringState {
final double ev;
final double evCompensation;
final IsoValue iso;
final double nd;
final NdValue nd;
final List<ExposurePair> exposurePairs;
const MeteringState({

View file

@ -1,3 +1,3 @@
import 'dart:math';
double log2(double x) => log(x) / log(2);
double log2(num x) => log(x) / log(2);

View file

@ -1,4 +1,7 @@
import 'package:lightmeter/models/aperture_value.dart';
import 'package:lightmeter/models/iso_value.dart';
import 'package:lightmeter/models/photography_value.dart';
import 'package:lightmeter/models/shutter_speed_value.dart';
import 'package:test/test.dart';
void main() {