mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-21 15:00:40 +00:00
ML-46 Add reciprocity failure formulas for some films (#47)
* added `Film` model with reciprocity formulas * added `FeaturesConfig` * added film picker * unused import * get ISO and ND from equipment profile * udpate iso on film changed * typo
This commit is contained in:
parent
6bf059ed4d
commit
be0617a99c
17 changed files with 381 additions and 54 deletions
228
lib/data/models/film.dart
Normal file
228
lib/data/models/film.dart
Normal file
|
@ -0,0 +1,228 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
double log10(double x) => log(x) / log(10);
|
||||
|
||||
double log10polynomian(
|
||||
double x,
|
||||
double a,
|
||||
double b,
|
||||
double c,
|
||||
) =>
|
||||
a * pow(log10(x), 2) + b * log10(x) + c;
|
||||
|
||||
/// Only Ilford films have reciprocity formulas provided by the manufacturer:
|
||||
/// https://www.ilfordphoto.com/wp/wp-content/uploads/2017/06/Reciprocity-Failure-Compensation.pdf
|
||||
///
|
||||
/// Reciprocity formulas for Fomapan films and Kodak films are from here:
|
||||
/// https://www.flickr.com/groups/86738082@N00/discuss/72157626050157470/
|
||||
///
|
||||
/// Cinema films like Kodak 5222/7222 Double-X and respective CineStill films (cause they are basically a modification of Kodak)
|
||||
/// do not have any reciprocity failure information, as these films are ment to be used in cinema
|
||||
/// with appropriate light and pretty short shutter speeds.
|
||||
///
|
||||
class Film {
|
||||
final String name;
|
||||
final int iso;
|
||||
|
||||
const Film(this.name, this.iso);
|
||||
|
||||
const Film.other()
|
||||
: name = '',
|
||||
iso = 0;
|
||||
|
||||
@override
|
||||
String toString() => name;
|
||||
|
||||
ShutterSpeedValue reciprocityFailure(ShutterSpeedValue shutterSpeed) {
|
||||
if (shutterSpeed.isFraction) {
|
||||
return shutterSpeed;
|
||||
} else {
|
||||
return ShutterSpeedValue(
|
||||
reciprocityFormula(shutterSpeed.rawValue),
|
||||
shutterSpeed.isFraction,
|
||||
shutterSpeed.stopType,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
double reciprocityFormula(double t) => t;
|
||||
|
||||
static const List<Film> values = [
|
||||
Film.other(),
|
||||
FomapanFilm.creative100(),
|
||||
FomapanFilm.creative200(),
|
||||
FomapanFilm.action400(),
|
||||
IlfordFilm.ortho(),
|
||||
IlfordFilm.delta100(),
|
||||
IlfordFilm.delta400(),
|
||||
IlfordFilm.delta3200(),
|
||||
IlfordFilm.fp4(),
|
||||
IlfordFilm.hp5(),
|
||||
IlfordFilm.panf(),
|
||||
IlfordFilm.sfx200(),
|
||||
IlfordFilm.xp2super(),
|
||||
IlfordFilm.pan100(),
|
||||
IlfordFilm.pan400(),
|
||||
KodakFilm.tmax100(),
|
||||
KodakFilm.tmax400(),
|
||||
KodakFilm.tmax3200(),
|
||||
KodakFilm.trix320(),
|
||||
KodakFilm.trix400(),
|
||||
];
|
||||
}
|
||||
|
||||
/// https://www.tate.org.uk/documents/598/page_6_7_agfa_stocks_0.pdf
|
||||
/// https://www.filmwasters.com/forum/index.php?topic=5298.0
|
||||
// {{1,1.87},{2,3.73},{3,8.06},{4,13.93},{5,21.28},{6,23.00},{7,30.12},{8,38.05},{9,44.75},{10,50.12},{20,117},{30,202},{40,293},{50,413},{60,547},{70,694},{80,853},{90,1022},{100,1202}};
|
||||
class AgfaFilm extends Film {
|
||||
final double a;
|
||||
final double b;
|
||||
final double c;
|
||||
|
||||
const AgfaFilm.apx100()
|
||||
: a = 1,
|
||||
b = 5,
|
||||
c = 2,
|
||||
super('Agfa APX 100', 100);
|
||||
|
||||
const AgfaFilm.apx400()
|
||||
: a = 1.5,
|
||||
b = 4.5,
|
||||
c = 3,
|
||||
super('Agfa APX 400', 400);
|
||||
|
||||
@override
|
||||
double reciprocityFormula(double t) => t * log10polynomian(t, a, b, c);
|
||||
}
|
||||
|
||||
class FomapanFilm extends Film {
|
||||
final double a;
|
||||
final double b;
|
||||
final double c;
|
||||
|
||||
/// https://www.foma.cz/en/fomapan-100
|
||||
const FomapanFilm.creative100()
|
||||
: a = 1,
|
||||
b = 5,
|
||||
c = 2,
|
||||
super('Fomapan CREATIVE 100', 100);
|
||||
|
||||
/// https://www.foma.cz/en/fomapan-200
|
||||
const FomapanFilm.creative200()
|
||||
: a = 1.5,
|
||||
b = 4.5,
|
||||
c = 3,
|
||||
super('Fomapan CREATIVE 200', 400);
|
||||
|
||||
/// https://www.foma.cz/en/fomapan-100
|
||||
const FomapanFilm.action400()
|
||||
: a = -1.25,
|
||||
b = 5.75,
|
||||
c = 1.5,
|
||||
super('Fomapan ACTION 400', 400);
|
||||
}
|
||||
|
||||
class IlfordFilm extends Film {
|
||||
final double reciprocityPower;
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1948/product/1650/
|
||||
const IlfordFilm.ortho()
|
||||
: reciprocityPower = 1.25,
|
||||
super('Ilford ORTHO+', 80);
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1919/product/686/
|
||||
const IlfordFilm.fp4()
|
||||
: reciprocityPower = 1.26,
|
||||
super('Ilford FP4+', 125);
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1903/product/691/
|
||||
const IlfordFilm.hp5()
|
||||
: reciprocityPower = 1.31,
|
||||
super('Ilford HP5+', 400);
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/3/product/679/
|
||||
const IlfordFilm.delta100()
|
||||
: reciprocityPower = 1.26,
|
||||
super('Ilford DELTA 100', 100);
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1915/product/684/
|
||||
const IlfordFilm.delta400()
|
||||
: reciprocityPower = 1.41,
|
||||
super('Ilford DELTA 400', 400);
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1913/product/682/
|
||||
const IlfordFilm.delta3200()
|
||||
: reciprocityPower = 1.33,
|
||||
super('Ilford DELTA 3200', 3200);
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1905/product/699/
|
||||
const IlfordFilm.panf()
|
||||
: reciprocityPower = 1.33,
|
||||
super('Ilford Pan F+', 50);
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1907/product/701/
|
||||
const IlfordFilm.sfx200()
|
||||
: reciprocityPower = 1.31,
|
||||
super('Ilford SFX 200', 200);
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1909/product/703/
|
||||
const IlfordFilm.xp2super()
|
||||
: reciprocityPower = 1.31,
|
||||
super('Ilford XP2 SUPER', 400);
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1958/product/696/
|
||||
const IlfordFilm.pan100()
|
||||
: reciprocityPower = 1.26,
|
||||
super('Kentemere 100', 100);
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1959/product/697/
|
||||
const IlfordFilm.pan400()
|
||||
: reciprocityPower = 1.30,
|
||||
super('Kentemere 400', 100);
|
||||
|
||||
@override
|
||||
double reciprocityFormula(double t) => pow(t, reciprocityPower).toDouble();
|
||||
}
|
||||
|
||||
class KodakFilm extends Film {
|
||||
final double a;
|
||||
final double b;
|
||||
final double c;
|
||||
|
||||
const KodakFilm.tmax100()
|
||||
: a = 1 / 6,
|
||||
b = 0,
|
||||
c = 4 / 3,
|
||||
super('Kodak T-MAX 100', 100);
|
||||
|
||||
const KodakFilm.tmax400()
|
||||
: a = 2 / 3,
|
||||
b = -1 / 2,
|
||||
c = 4 / 3,
|
||||
super('Kodak T-MAX 400', 400);
|
||||
|
||||
const KodakFilm.tmax3200()
|
||||
: a = 7 / 6,
|
||||
b = -1,
|
||||
c = 4 / 3,
|
||||
super('Kodak T-MAX 3200', 3200);
|
||||
|
||||
const KodakFilm.trix320()
|
||||
: a = 2,
|
||||
b = 1,
|
||||
c = 2,
|
||||
super('Kodak TRI-X 320', 320);
|
||||
|
||||
const KodakFilm.trix400()
|
||||
: a = 2,
|
||||
b = 1,
|
||||
c = 2,
|
||||
super('Kodak TRI-X 400', 400);
|
||||
|
||||
@override
|
||||
double reciprocityFormula(double t) => t * log10polynomian(t, a, b, c);
|
||||
}
|
|
@ -4,6 +4,7 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
|||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'models/ev_source_type.dart';
|
||||
import 'models/film.dart';
|
||||
import 'models/theme_type.dart';
|
||||
|
||||
class UserPreferencesService {
|
||||
|
@ -13,6 +14,7 @@ class UserPreferencesService {
|
|||
static const _evSourceTypeKey = "evSourceType";
|
||||
static const _cameraEvCalibrationKey = "cameraEvCalibration";
|
||||
static const _lightSensorEvCalibrationKey = "lightSensorEvCalibration";
|
||||
static const _filmKey = "film";
|
||||
|
||||
static const _caffeineKey = "caffeine";
|
||||
static const _hapticsKey = "haptics";
|
||||
|
@ -105,6 +107,12 @@ class UserPreferencesService {
|
|||
bool get dynamicColor => _sharedPreferences.getBool(_dynamicColorKey) ?? false;
|
||||
set dynamicColor(bool value) => _sharedPreferences.setBool(_dynamicColorKey, value);
|
||||
|
||||
Film get film => Film.values.firstWhere(
|
||||
(e) => e.name == _sharedPreferences.getString(_filmKey),
|
||||
orElse: () => Film.values.first,
|
||||
);
|
||||
set film(Film value) => _sharedPreferences.setString(_filmKey, value.name);
|
||||
|
||||
String get selectedEquipmentProfileId => '';
|
||||
set selectedEquipmentProfileId(String id) {}
|
||||
|
||||
|
|
3
lib/features.dart
Normal file
3
lib/features.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
class FeaturesConfig {
|
||||
static const bool equipmentProfilesEnabled = false;
|
||||
}
|
|
@ -34,6 +34,7 @@
|
|||
"calibrationMessageCameraOnly": "The accuracy of the readings measured by this application depends entirely on the rear camera of the device. Therefore, consider testing this application and setting up an EV calibration value that will give you the desired measurement results.",
|
||||
"camera": "Camera",
|
||||
"lightSensor": "Light sensor",
|
||||
"film": "Film",
|
||||
"equipment": "Equipment",
|
||||
"equipmentProfileName": "Equipment profile name",
|
||||
"equipmentProfileNameHint": "Praktica MTL5B",
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"calibrationMessageCameraOnly": "La précision des lectures mesurées par cette application dépend entièrement de la caméra arrière de l'appareil. Par conséquent, envisagez de tester cette application et de configurer une valeur d'étalonnage EV qui vous donnera les résultats de mesure souhaités.",
|
||||
"camera": "Caméra",
|
||||
"lightSensor": "Capteur de lumière",
|
||||
"film": "Pellicule",
|
||||
"equipment": "Équipement",
|
||||
"equipmentProfileName": "Nom du profil de l'équipement",
|
||||
"equipmentProfileNameHint": "Praktica MTL5B",
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"calibrationMessageCameraOnly": "Точность измерений данного приложения полностью зависит от точности камеры вашего устройства. Поэтому рекомендуется самостоятельно подобрать калибровочное значение, которое даст желаемый результат измерений.",
|
||||
"camera": "Камера",
|
||||
"lightSensor": "Датчик освещённости",
|
||||
"film": "Пленка",
|
||||
"equipment": "Оборудование",
|
||||
"equipmentProfileName": "Название профиля",
|
||||
"equipmentProfileNameHint": "Praktica MTL5B",
|
||||
|
|
|
@ -32,7 +32,7 @@ class Dimens {
|
|||
// TopBar
|
||||
/// Probably this is a bad practice, but with text size locked, the height is always 212
|
||||
static const double readingContainerSingleValueHeight = 76;
|
||||
static const double readingContainerDefaultHeight = 212;
|
||||
static const double readingContainerDefaultHeight = 288;
|
||||
|
||||
// `CenteredSlider`
|
||||
static const double cameraSliderTrackHeight = grid4;
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:math';
|
|||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart'
|
||||
|
@ -31,6 +32,7 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
|
||||
late IsoValue _iso = _userPreferencesService.iso;
|
||||
late NdValue _nd = _userPreferencesService.ndFilter;
|
||||
late Film _film = _userPreferencesService.film;
|
||||
double _ev = 0.0;
|
||||
bool _isMeteringInProgress = false;
|
||||
|
||||
|
@ -42,10 +44,11 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
this.stopType,
|
||||
) : super(
|
||||
MeteringEndedState(
|
||||
iso: _userPreferencesService.iso,
|
||||
ev: 0.0,
|
||||
film: _userPreferencesService.film,
|
||||
iso: _userPreferencesService.iso,
|
||||
nd: _userPreferencesService.ndFilter,
|
||||
exposurePairs: [],
|
||||
exposurePairs: const [],
|
||||
),
|
||||
) {
|
||||
_communicationSubscription = _communicationBloc.stream
|
||||
|
@ -55,6 +58,7 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
|
||||
on<EquipmentProfileChangedEvent>(_onEquipmentProfileChanged);
|
||||
on<StopTypeChangedEvent>(_onStopTypeChanged);
|
||||
on<FilmChangedEvent>(_onFilmChanged);
|
||||
on<IsoChangedEvent>(_onIsoChanged);
|
||||
on<NdChangedEvent>(_onNdChanged);
|
||||
on<MeasureEvent>(_onMeasure);
|
||||
|
@ -100,7 +104,23 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
_emitMeasuredState(emit);
|
||||
}
|
||||
|
||||
void _onFilmChanged(FilmChangedEvent event, Emitter emit) {
|
||||
if (_iso.value != event.data.iso) {
|
||||
final newIso = isoValues.firstWhere(
|
||||
(e) => e.value == event.data.iso,
|
||||
orElse: () => _iso,
|
||||
);
|
||||
add(IsoChangedEvent(newIso));
|
||||
}
|
||||
_film = event.data;
|
||||
_userPreferencesService.film = event.data;
|
||||
_emitMeasuredState(emit);
|
||||
}
|
||||
|
||||
void _onIsoChanged(IsoChangedEvent event, Emitter emit) {
|
||||
if (event.isoValue.value != _film.iso) {
|
||||
_film = Film.values.first;
|
||||
}
|
||||
_userPreferencesService.iso = event.isoValue;
|
||||
_ev = _ev + log2(event.isoValue.value / _iso.value);
|
||||
_iso = event.isoValue;
|
||||
|
@ -130,14 +150,16 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
void _emitMeasuredState(Emitter emit) {
|
||||
emit(_isMeteringInProgress
|
||||
? MeteringInProgressState(
|
||||
iso: _iso,
|
||||
ev: _ev,
|
||||
film: _film,
|
||||
iso: _iso,
|
||||
nd: _nd,
|
||||
exposurePairs: _buildExposureValues(_ev),
|
||||
)
|
||||
: MeteringEndedState(
|
||||
iso: _iso,
|
||||
ev: _ev,
|
||||
film: _film,
|
||||
iso: _iso,
|
||||
nd: _nd,
|
||||
exposurePairs: _buildExposureValues(_ev),
|
||||
));
|
||||
|
@ -188,7 +210,7 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
itemsCount,
|
||||
(index) => ExposurePair(
|
||||
_apertureValues[index + apertureOffset],
|
||||
_shutterSpeedValues[index + shutterSpeedOffset],
|
||||
_film.reciprocityFailure(_shutterSpeedValues[index + shutterSpeedOffset]),
|
||||
),
|
||||
growable: false,
|
||||
);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
|
@ -12,8 +12,10 @@ import 'widget_container_camera.dart';
|
|||
class CameraContainerProvider extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
@ -21,8 +23,10 @@ class CameraContainerProvider extends StatelessWidget {
|
|||
const CameraContainerProvider({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
required this.exposurePairs,
|
||||
|
@ -40,10 +44,10 @@ class CameraContainerProvider extends StatelessWidget {
|
|||
child: CameraContainer(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
isoValues: EquipmentProfile.of(context).isoValues,
|
||||
film: film,
|
||||
iso: iso,
|
||||
ndValues: EquipmentProfile.of(context).ndValues,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
exposurePairs: exposurePairs,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/features.dart';
|
||||
import 'package:lightmeter/platform_config.dart';
|
||||
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/metering/components/camera_container/components/camera_view/widget_camera_view.dart';
|
||||
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
|
||||
|
@ -21,10 +22,10 @@ import 'state_container_camera.dart';
|
|||
class CameraContainer extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final List<IsoValue> isoValues;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final List<NdValue> ndValues;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
@ -32,10 +33,10 @@ class CameraContainer extends StatelessWidget {
|
|||
const CameraContainer({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.isoValues,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.ndValues,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
required this.exposurePairs,
|
||||
|
@ -49,7 +50,7 @@ class CameraContainer extends StatelessWidget {
|
|||
PlatformConfig.cameraPreviewAspectRatio;
|
||||
|
||||
double topBarOverflow = Dimens.readingContainerDefaultHeight - cameraViewHeight;
|
||||
if (EquipmentProfiles.of(context).isNotEmpty) {
|
||||
if (FeaturesConfig.equipmentProfilesEnabled) {
|
||||
topBarOverflow += Dimens.readingContainerSingleValueHeight;
|
||||
topBarOverflow += Dimens.paddingS;
|
||||
}
|
||||
|
@ -60,10 +61,10 @@ class CameraContainer extends StatelessWidget {
|
|||
readingsContainer: ReadingsContainer(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
isoValues: isoValues,
|
||||
film: film,
|
||||
iso: iso,
|
||||
ndValues: ndValues,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
|
@ -12,8 +12,10 @@ import 'widget_container_light_sensor.dart';
|
|||
class LightSensorContainerProvider extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
@ -21,8 +23,10 @@ class LightSensorContainerProvider extends StatelessWidget {
|
|||
const LightSensorContainerProvider({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
required this.exposurePairs,
|
||||
|
@ -40,10 +44,10 @@ class LightSensorContainerProvider extends StatelessWidget {
|
|||
child: LightSensorContainer(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
isoValues: EquipmentProfile.of(context).isoValues,
|
||||
film: film,
|
||||
iso: iso,
|
||||
ndValues: EquipmentProfile.of(context).ndValues,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
exposurePairs: exposurePairs,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/widget_top_bar_metering.dart';
|
||||
|
@ -9,10 +10,10 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
|||
class LightSensorContainer extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final List<IsoValue> isoValues;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final List<NdValue> ndValues;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
@ -20,10 +21,10 @@ class LightSensorContainer extends StatelessWidget {
|
|||
const LightSensorContainer({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.isoValues,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.ndValues,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
required this.exposurePairs,
|
||||
|
@ -38,10 +39,10 @@ class LightSensorContainer extends StatelessWidget {
|
|||
readingsContainer: ReadingsContainer(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
isoValues: isoValues,
|
||||
film: film,
|
||||
iso: iso,
|
||||
ndValues: ndValues,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
),
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/features.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
|
@ -12,20 +14,20 @@ import 'components/reading_value_container/widget_container_reading_value.dart';
|
|||
class ReadingsContainer extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final List<IsoValue> isoValues;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final List<NdValue> ndValues;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
|
||||
const ReadingsContainer({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.isoValues,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.ndValues,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
super.key,
|
||||
|
@ -36,12 +38,8 @@ class ReadingsContainer extends StatelessWidget {
|
|||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (EquipmentProfiles.of(context).isNotEmpty) ...[
|
||||
_EquipmentProfilePicker(
|
||||
selectedValue: EquipmentProfile.of(context),
|
||||
values: EquipmentProfiles.of(context),
|
||||
onChanged: EquipmentProfileProvider.of(context).setProfile,
|
||||
),
|
||||
if (FeaturesConfig.equipmentProfilesEnabled) ...[
|
||||
const _EquipmentProfilePicker(),
|
||||
const _InnerPadding(),
|
||||
],
|
||||
ReadingValueContainer(
|
||||
|
@ -57,12 +55,18 @@ class ReadingsContainer extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
const _InnerPadding(),
|
||||
_FilmPicker(
|
||||
values: Film.values,
|
||||
selectedValue: film,
|
||||
onChanged: onFilmChanged,
|
||||
),
|
||||
const _InnerPadding(),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _IsoValuePicker(
|
||||
selectedValue: iso,
|
||||
values: isoValues,
|
||||
values: EquipmentProfile.of(context).isoValues,
|
||||
onChanged: onIsoChanged,
|
||||
),
|
||||
),
|
||||
|
@ -70,7 +74,7 @@ class ReadingsContainer extends StatelessWidget {
|
|||
Expanded(
|
||||
child: _NdValuePicker(
|
||||
selectedValue: nd,
|
||||
values: ndValues,
|
||||
values: EquipmentProfile.of(context).ndValues,
|
||||
onChanged: onNdChanged,
|
||||
),
|
||||
),
|
||||
|
@ -86,28 +90,51 @@ class _InnerPadding extends SizedBox {
|
|||
}
|
||||
|
||||
class _EquipmentProfilePicker extends StatelessWidget {
|
||||
final List<EquipmentProfileData> values;
|
||||
final EquipmentProfileData selectedValue;
|
||||
final ValueChanged<EquipmentProfileData> onChanged;
|
||||
|
||||
const _EquipmentProfilePicker({
|
||||
required this.selectedValue,
|
||||
required this.values,
|
||||
required this.onChanged,
|
||||
});
|
||||
const _EquipmentProfilePicker();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedDialogPicker<EquipmentProfileData>(
|
||||
title: S.of(context).equipmentProfile,
|
||||
selectedValue: selectedValue,
|
||||
values: values,
|
||||
selectedValue: EquipmentProfile.of(context),
|
||||
values: EquipmentProfiles.of(context),
|
||||
itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name),
|
||||
onChanged: onChanged,
|
||||
onChanged: EquipmentProfileProvider.of(context).setProfile,
|
||||
closedChild: ReadingValueContainer.singleValue(
|
||||
value: ReadingValue(
|
||||
label: S.of(context).equipmentProfile,
|
||||
value: selectedValue.id.isEmpty ? S.of(context).none : selectedValue.name,
|
||||
value: EquipmentProfile.of(context).id.isEmpty
|
||||
? S.of(context).none
|
||||
: EquipmentProfile.of(context).name,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FilmPicker extends StatelessWidget {
|
||||
final List<Film> values;
|
||||
final Film selectedValue;
|
||||
final ValueChanged<Film> onChanged;
|
||||
|
||||
const _FilmPicker({
|
||||
required this.values,
|
||||
required this.selectedValue,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedDialogPicker<Film>(
|
||||
title: S.of(context).film,
|
||||
selectedValue: selectedValue,
|
||||
values: values,
|
||||
itemTitleBuilder: (_, value) => Text(value.name.isEmpty ? S.of(context).none : value.name),
|
||||
onChanged: onChanged,
|
||||
closedChild: ReadingValueContainer.singleValue(
|
||||
value: ReadingValue(
|
||||
label: S.of(context).film,
|
||||
value: selectedValue.name.isEmpty ? S.of(context).none : selectedValue.name,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
abstract class MeteringEvent {
|
||||
|
@ -16,6 +17,12 @@ class EquipmentProfileChangedEvent extends MeteringEvent {
|
|||
const EquipmentProfileChangedEvent(this.equipmentProfileData);
|
||||
}
|
||||
|
||||
class FilmChangedEvent extends MeteringEvent {
|
||||
final Film data;
|
||||
|
||||
const FilmChangedEvent(this.data);
|
||||
}
|
||||
|
||||
class IsoChangedEvent extends MeteringEvent {
|
||||
final IsoValue isoValue;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/models/ev_source_type.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/environment.dart';
|
||||
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||
import 'package:lightmeter/providers/ev_source_type_provider.dart';
|
||||
|
@ -44,8 +45,10 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
|||
? _MeteringContainerBuidler(
|
||||
fastest: state.fastest,
|
||||
slowest: state.slowest,
|
||||
film: state.film,
|
||||
iso: state.iso,
|
||||
nd: state.nd,
|
||||
onFilmChanged: (value) => _bloc.add(FilmChangedEvent(value)),
|
||||
onIsoChanged: (value) => _bloc.add(IsoChangedEvent(value)),
|
||||
onNdChanged: (value) => _bloc.add(NdChangedEvent(value)),
|
||||
exposurePairs: state.exposurePairs,
|
||||
|
@ -73,8 +76,10 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
|||
class _MeteringContainerBuidler extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
@ -82,8 +87,10 @@ class _MeteringContainerBuidler extends StatelessWidget {
|
|||
const _MeteringContainerBuidler({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
required this.exposurePairs,
|
||||
|
@ -95,8 +102,10 @@ class _MeteringContainerBuidler extends StatelessWidget {
|
|||
? CameraContainerProvider(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
film: film,
|
||||
iso: iso,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
exposurePairs: exposurePairs,
|
||||
|
@ -104,8 +113,10 @@ class _MeteringContainerBuidler extends StatelessWidget {
|
|||
: LightSensorContainerProvider(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
film: film,
|
||||
iso: iso,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
exposurePairs: exposurePairs,
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
@immutable
|
||||
abstract class MeteringState {
|
||||
const MeteringState();
|
||||
}
|
||||
|
@ -11,12 +14,14 @@ class LoadingState extends MeteringState {
|
|||
|
||||
abstract class MeteringDataState extends MeteringState {
|
||||
final double ev;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
||||
const MeteringDataState({
|
||||
required this.ev,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.exposurePairs,
|
||||
|
@ -27,8 +32,9 @@ abstract class MeteringDataState extends MeteringState {
|
|||
}
|
||||
|
||||
class MeteringInProgressState extends MeteringDataState {
|
||||
MeteringInProgressState({
|
||||
const MeteringInProgressState({
|
||||
required super.ev,
|
||||
required super.film,
|
||||
required super.iso,
|
||||
required super.nd,
|
||||
required super.exposurePairs,
|
||||
|
@ -36,8 +42,9 @@ class MeteringInProgressState extends MeteringDataState {
|
|||
}
|
||||
|
||||
class MeteringEndedState extends MeteringDataState {
|
||||
MeteringEndedState({
|
||||
const MeteringEndedState({
|
||||
required super.ev,
|
||||
required super.film,
|
||||
required super.iso,
|
||||
required super.nd,
|
||||
required super.exposurePairs,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/features.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
||||
|
||||
|
@ -16,7 +17,7 @@ class MeteringSettingsSection extends StatelessWidget {
|
|||
children: const [
|
||||
StopTypeListTile(),
|
||||
CalibrationListTile(),
|
||||
EquipmentProfilesListTile(),
|
||||
if (FeaturesConfig.equipmentProfilesEnabled) EquipmentProfilesListTile(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue