import 'dart:async'; import 'dart:math'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/photography_values/aperture_value.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; import 'package:lightmeter/data/models/photography_values/photography_value.dart'; import 'package:lightmeter/data/models/photography_values/shutter_speed_value.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; import 'package:lightmeter/interactors/haptics_interactor.dart'; import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_events; import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states; import 'package:lightmeter/utils/log_2.dart'; import 'communication/bloc_communication_metering.dart'; import 'event_metering.dart'; import 'state_metering.dart'; class MeteringBloc extends Bloc { final MeteringCommunicationBloc _communicationBloc; final UserPreferencesService _userPreferencesService; final HapticsInteractor _hapticsInteractor; late final StreamSubscription _communicationSubscription; List get _apertureValues => apertureValues.whereStopType(stopType); List get _shutterSpeedValues => shutterSpeedValues.whereStopType(stopType); StopType stopType; MeteringBloc( this._communicationBloc, this._userPreferencesService, this._hapticsInteractor, this.stopType, ) : super( MeteringState( iso: _userPreferencesService.iso, ev: 0.0, evCompensation: 0.0, nd: _userPreferencesService.ndFilter, exposurePairs: [], ), ) { _communicationSubscription = _communicationBloc.stream .where((state) => state is communication_states.ScreenState) .map((state) => state as communication_states.ScreenState) .listen(_onCommunicationState); on(_onStopTypeChanged); on(_onIsoChanged); on(_onNdChanged); on(_onMeasure); on(_onMeasured); add(const MeasureEvent()); } @override Future close() async { await _communicationSubscription.cancel(); return super.close(); } void _onCommunicationState(communication_states.ScreenState communicationState) { if (communicationState is communication_states.MeasuredState) { add(MeasuredEvent(communicationState.ev100)); } } void _onStopTypeChanged(StopTypeChangedEvent event, Emitter emit) { stopType = event.stopType; emit(MeteringState( iso: state.iso, ev: state.ev, evCompensation: state.evCompensation, nd: state.nd, exposurePairs: _buildExposureValues(state.ev), )); } void _onIsoChanged(IsoChangedEvent event, Emitter emit) { _userPreferencesService.iso = event.isoValue; final ev = state.ev + log2(event.isoValue.value / state.iso.value); emit(MeteringState( iso: event.isoValue, ev: ev, evCompensation: state.evCompensation, nd: state.nd, exposurePairs: _buildExposureValues(ev), )); } void _onNdChanged(NdChangedEvent event, Emitter emit) { _userPreferencesService.ndFilter = event.ndValue; 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), )); } void _onMeasure(_, __) { _hapticsInteractor.quickVibration(); _communicationBloc.add(const communication_events.MeasureEvent()); } void _onMeasured(MeasuredEvent event, Emitter emit) { _hapticsInteractor.responseVibration(); final ev = event.ev100 + log2(state.iso.value / 100); emit(MeteringState( iso: state.iso, ev: ev, evCompensation: state.evCompensation, nd: state.nd, exposurePairs: _buildExposureValues(ev), )); } List _buildExposureValues(double ev) { late final int evSteps; switch (stopType) { case StopType.full: evSteps = ev.floor(); break; case StopType.half: evSteps = (ev / 0.5).floor(); break; case StopType.third: evSteps = (ev / 0.3).floor(); break; } final evOffset = _shutterSpeedValues.indexOf(const ShutterSpeedValue(1, false, StopType.full)) - evSteps; late final int apertureOffset; late final int shutterSpeedOffset; if (evOffset >= 0) { apertureOffset = 0; shutterSpeedOffset = evOffset; } else { apertureOffset = -evOffset; shutterSpeedOffset = 0; } int itemsCount = min(_apertureValues.length + shutterSpeedOffset, _shutterSpeedValues.length + apertureOffset) - max(apertureOffset, shutterSpeedOffset); if (itemsCount < 0) { return List.empty(growable: false); } return List.generate( itemsCount, (index) => ExposurePair( _apertureValues[index + apertureOffset], _shutterSpeedValues[index + shutterSpeedOffset], ), growable: false, ); } }