mirror of
https://github.com/dancojocaru2000/logic-circuits-simulator.git
synced 2025-06-19 10:32:28 +03:00
Compare commits
No commits in common. "95ef25214ad283711411680d09803313c9f7dfd1" and "4a6caee70257a302c1d323cde774e109c60a1839" have entirely different histories.
95ef25214a
...
4a6caee702
12 changed files with 99 additions and 2036 deletions
|
@ -1,296 +0,0 @@
|
||||||
import 'dart:math';
|
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
|
|
||||||
class VisualComponent extends HookWidget {
|
|
||||||
final String name;
|
|
||||||
final List<String> inputs;
|
|
||||||
final List<String> outputs;
|
|
||||||
final Map<String, Color?> inputColors;
|
|
||||||
final Map<String, Color?> outputColors;
|
|
||||||
|
|
||||||
VisualComponent({super.key, required this.name, required this.inputs, required this.outputs, Map<String, Color?>? inputColors, Map<String, Color?>? outputColors})
|
|
||||||
: inputColors = inputColors ?? {}
|
|
||||||
, outputColors = outputColors ?? {};
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final hovered = useState(false);
|
|
||||||
|
|
||||||
final inputsWidth = inputs.map((input) => IOLabel.getNeededWidth(context, input)).fold<double>(0, (previousValue, element) => max(previousValue, element));
|
|
||||||
final outputsWidth = outputs.map((output) => IOLabel.getNeededWidth(context, output)).fold<double>(0, (previousValue, element) => max(previousValue, element));
|
|
||||||
|
|
||||||
return MouseRegion(
|
|
||||||
onEnter: (event) => hovered.value = true,
|
|
||||||
onExit: (event) => hovered.value = false,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
for (final input in inputs) Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
|
||||||
child: IOLabel(
|
|
||||||
label: input,
|
|
||||||
input: true,
|
|
||||||
lineColor: hovered.value
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: inputColors[input] ?? Colors.black,
|
|
||||||
width: inputsWidth,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
width: 100,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(
|
|
||||||
color: hovered.value ? Theme.of(context).colorScheme.primary : Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
name,
|
|
||||||
softWrap: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
for (final output in outputs) Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
|
||||||
child: IOLabel(
|
|
||||||
label: output,
|
|
||||||
input: false,
|
|
||||||
lineColor: hovered.value
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: outputColors[output] ?? Colors.black,
|
|
||||||
width: outputsWidth,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static double getNeededWidth(BuildContext context, List<String> inputs, List<String> outputs, [TextStyle? textStyle]) {
|
|
||||||
final inputsWidth = inputs.map((input) => IOLabel.getNeededWidth(context, input, textStyle)).fold<double>(0, (previousValue, element) => max(previousValue, element));
|
|
||||||
final outputsWidth = outputs.map((output) => IOLabel.getNeededWidth(context, output, textStyle)).fold<double>(0, (previousValue, element) => max(previousValue, element));
|
|
||||||
return inputsWidth + outputsWidth + 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double getHeightOfIO(BuildContext context, List<String> options, int index, [TextStyle? textStyle]) {
|
|
||||||
assert(index < options.length);
|
|
||||||
getHeightOfText(String text) {
|
|
||||||
final textPainter = TextPainter(
|
|
||||||
text: TextSpan(
|
|
||||||
text: text,
|
|
||||||
style: (textStyle ?? DefaultTextStyle.of(context).style).merge(
|
|
||||||
const TextStyle(
|
|
||||||
inherit: true,
|
|
||||||
fontFeatures: [
|
|
||||||
FontFeature.tabularFigures(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
maxLines: 1,
|
|
||||||
)..layout();
|
|
||||||
return textPainter.height;
|
|
||||||
}
|
|
||||||
var result = 0.0;
|
|
||||||
for (var i = 0; i < index; i++) {
|
|
||||||
result += 5.0 + getHeightOfText(options[i]) + 5.0;
|
|
||||||
}
|
|
||||||
result += 5.0 + getHeightOfText(options[index]);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IOComponent extends HookWidget {
|
|
||||||
final bool input;
|
|
||||||
final String name;
|
|
||||||
final double width;
|
|
||||||
final double circleDiameter;
|
|
||||||
final Color? color;
|
|
||||||
final void Function()? onTap;
|
|
||||||
|
|
||||||
const IOComponent({super.key, required this.name, required this.input, this.width = 100, this.circleDiameter = 20, this.color, this.onTap});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final hovered = useState(false);
|
|
||||||
|
|
||||||
return MouseRegion(
|
|
||||||
onEnter: (event) => hovered.value = true,
|
|
||||||
onExit: (event) => hovered.value = false,
|
|
||||||
hitTestBehavior: HitTestBehavior.translucent,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Builder(
|
|
||||||
builder: (context) {
|
|
||||||
final lineColor = hovered.value ? Theme.of(context).colorScheme.primary : color ?? Colors.black;
|
|
||||||
|
|
||||||
return Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (input) Container(
|
|
||||||
width: circleDiameter,
|
|
||||||
height: circleDiameter,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(color: lineColor),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(bottom: circleDiameter - 2),
|
|
||||||
child: IOLabel(
|
|
||||||
label: name,
|
|
||||||
input: !input,
|
|
||||||
lineColor: lineColor,
|
|
||||||
width: width - circleDiameter,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (!input) Container(
|
|
||||||
width: circleDiameter,
|
|
||||||
height: circleDiameter,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(color: lineColor),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static double getNeededWidth(BuildContext context, String name, {double circleDiameter = 20, TextStyle? textStyle}) {
|
|
||||||
return circleDiameter + IOLabel.getNeededWidth(context, name, textStyle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IOLabel extends StatelessWidget {
|
|
||||||
final bool input;
|
|
||||||
final String label;
|
|
||||||
final Color lineColor;
|
|
||||||
final double width;
|
|
||||||
|
|
||||||
const IOLabel({super.key, required this.lineColor, required this.label, required this.input, this.width = 80});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
width: width,
|
|
||||||
height: 20,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(
|
|
||||||
bottom: BorderSide(color: lineColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Align(
|
|
||||||
alignment: input ? Alignment.bottomRight : Alignment.bottomLeft,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
|
||||||
child: Text(
|
|
||||||
label,
|
|
||||||
style: const TextStyle(
|
|
||||||
inherit: true,
|
|
||||||
fontFeatures: [
|
|
||||||
FontFeature.tabularFigures(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static double getNeededWidth(BuildContext context, String text, [TextStyle? textStyle]) {
|
|
||||||
final textPainter = TextPainter(
|
|
||||||
text: TextSpan(
|
|
||||||
text: text,
|
|
||||||
style: (textStyle ?? DefaultTextStyle.of(context).style).merge(
|
|
||||||
const TextStyle(
|
|
||||||
inherit: true,
|
|
||||||
fontFeatures: [
|
|
||||||
FontFeature.tabularFigures(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
maxLines: 1,
|
|
||||||
)..layout();
|
|
||||||
return textPainter.width + 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class WireWidget extends StatelessWidget {
|
|
||||||
final Offset from;
|
|
||||||
final Offset to;
|
|
||||||
final Color color;
|
|
||||||
|
|
||||||
const WireWidget({
|
|
||||||
required this.from,
|
|
||||||
required this.to,
|
|
||||||
this.color = Colors.black,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return CustomPaint(
|
|
||||||
painter: _WireCustomPainter(
|
|
||||||
color: color,
|
|
||||||
primaryDiagonal:
|
|
||||||
(from.dx < to.dx && from.dy < to.dy) ||
|
|
||||||
(from.dx > to.dx && from.dy > to.dy),
|
|
||||||
),
|
|
||||||
child: SizedBox(
|
|
||||||
height: (to - from).dy.abs(),
|
|
||||||
width: (to - from).dx.abs(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _WireCustomPainter extends CustomPainter {
|
|
||||||
final Color color;
|
|
||||||
final bool primaryDiagonal;
|
|
||||||
|
|
||||||
const _WireCustomPainter({required this.color, required this.primaryDiagonal});
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
final paint = Paint()
|
|
||||||
..color = color;
|
|
||||||
if (primaryDiagonal) {
|
|
||||||
canvas.drawLine(
|
|
||||||
Offset.zero,
|
|
||||||
Offset(size.width, size.height),
|
|
||||||
paint,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
canvas.drawLine(
|
|
||||||
Offset(size.width, 0),
|
|
||||||
Offset(0, size.height),
|
|
||||||
paint,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(covariant CustomPainter oldDelegate) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,4 @@
|
||||||
export 'package:logic_circuits_simulator/models/component.dart';
|
|
||||||
export 'package:logic_circuits_simulator/models/design.dart';
|
|
||||||
export 'package:logic_circuits_simulator/models/project.dart';
|
|
||||||
export 'package:logic_circuits_simulator/models/projects.dart';
|
export 'package:logic_circuits_simulator/models/projects.dart';
|
||||||
|
export 'package:logic_circuits_simulator/models/project.dart';
|
||||||
|
export 'package:logic_circuits_simulator/models/component.dart';
|
||||||
export 'package:logic_circuits_simulator/models/wiring.dart';
|
export 'package:logic_circuits_simulator/models/wiring.dart';
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
|
|
||||||
part 'design.freezed.dart';
|
|
||||||
part 'design.g.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class Design with _$Design {
|
|
||||||
const factory Design({
|
|
||||||
required List<DesignComponent> components,
|
|
||||||
required List<DesignWire> wires,
|
|
||||||
required List<DesignInput> inputs,
|
|
||||||
required List<DesignOutput> outputs,
|
|
||||||
}) = _Design;
|
|
||||||
|
|
||||||
factory Design.fromJson(Map<String, dynamic> json) => _$DesignFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class DesignComponent with _$DesignComponent {
|
|
||||||
const factory DesignComponent({
|
|
||||||
required String instanceId,
|
|
||||||
required double x,
|
|
||||||
required double y,
|
|
||||||
}) = _DesignComponent;
|
|
||||||
|
|
||||||
factory DesignComponent.fromJson(Map<String, dynamic> json) => _$DesignComponentFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class DesignWire with _$DesignWire {
|
|
||||||
const factory DesignWire({
|
|
||||||
required String wireId,
|
|
||||||
required double x,
|
|
||||||
required double y,
|
|
||||||
}) = _DesignWire;
|
|
||||||
|
|
||||||
factory DesignWire.fromJson(Map<String, dynamic> json) => _$DesignWireFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class DesignInput with _$DesignInput {
|
|
||||||
const factory DesignInput({
|
|
||||||
required String name,
|
|
||||||
required double x,
|
|
||||||
required double y,
|
|
||||||
}) = _DesignInput;
|
|
||||||
|
|
||||||
factory DesignInput.fromJson(Map<String, dynamic> json) => _$DesignInputFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class DesignOutput with _$DesignOutput {
|
|
||||||
const factory DesignOutput({
|
|
||||||
required String name,
|
|
||||||
required double x,
|
|
||||||
required double y,
|
|
||||||
}) = _DesignOutput;
|
|
||||||
|
|
||||||
factory DesignOutput.fromJson(Map<String, dynamic> json) => _$DesignOutputFromJson(json);
|
|
||||||
}
|
|
|
@ -1,908 +0,0 @@
|
||||||
// coverage:ignore-file
|
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
|
||||||
|
|
||||||
part of 'design.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// FreezedGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
T _$identity<T>(T value) => value;
|
|
||||||
|
|
||||||
final _privateConstructorUsedError = UnsupportedError(
|
|
||||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
|
||||||
|
|
||||||
Design _$DesignFromJson(Map<String, dynamic> json) {
|
|
||||||
return _Design.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$Design {
|
|
||||||
List<DesignComponent> get components => throw _privateConstructorUsedError;
|
|
||||||
List<DesignWire> get wires => throw _privateConstructorUsedError;
|
|
||||||
List<DesignInput> get inputs => throw _privateConstructorUsedError;
|
|
||||||
List<DesignOutput> get outputs => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
$DesignCopyWith<Design> get copyWith => throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $DesignCopyWith<$Res> {
|
|
||||||
factory $DesignCopyWith(Design value, $Res Function(Design) then) =
|
|
||||||
_$DesignCopyWithImpl<$Res>;
|
|
||||||
$Res call(
|
|
||||||
{List<DesignComponent> components,
|
|
||||||
List<DesignWire> wires,
|
|
||||||
List<DesignInput> inputs,
|
|
||||||
List<DesignOutput> outputs});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$DesignCopyWithImpl<$Res> implements $DesignCopyWith<$Res> {
|
|
||||||
_$DesignCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
final Design _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function(Design) _then;
|
|
||||||
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? components = freezed,
|
|
||||||
Object? wires = freezed,
|
|
||||||
Object? inputs = freezed,
|
|
||||||
Object? outputs = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
components: components == freezed
|
|
||||||
? _value.components
|
|
||||||
: components // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<DesignComponent>,
|
|
||||||
wires: wires == freezed
|
|
||||||
? _value.wires
|
|
||||||
: wires // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<DesignWire>,
|
|
||||||
inputs: inputs == freezed
|
|
||||||
? _value.inputs
|
|
||||||
: inputs // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<DesignInput>,
|
|
||||||
outputs: outputs == freezed
|
|
||||||
? _value.outputs
|
|
||||||
: outputs // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<DesignOutput>,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$_DesignCopyWith<$Res> implements $DesignCopyWith<$Res> {
|
|
||||||
factory _$$_DesignCopyWith(_$_Design value, $Res Function(_$_Design) then) =
|
|
||||||
__$$_DesignCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
$Res call(
|
|
||||||
{List<DesignComponent> components,
|
|
||||||
List<DesignWire> wires,
|
|
||||||
List<DesignInput> inputs,
|
|
||||||
List<DesignOutput> outputs});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$_DesignCopyWithImpl<$Res> extends _$DesignCopyWithImpl<$Res>
|
|
||||||
implements _$$_DesignCopyWith<$Res> {
|
|
||||||
__$$_DesignCopyWithImpl(_$_Design _value, $Res Function(_$_Design) _then)
|
|
||||||
: super(_value, (v) => _then(v as _$_Design));
|
|
||||||
|
|
||||||
@override
|
|
||||||
_$_Design get _value => super._value as _$_Design;
|
|
||||||
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? components = freezed,
|
|
||||||
Object? wires = freezed,
|
|
||||||
Object? inputs = freezed,
|
|
||||||
Object? outputs = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(_$_Design(
|
|
||||||
components: components == freezed
|
|
||||||
? _value._components
|
|
||||||
: components // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<DesignComponent>,
|
|
||||||
wires: wires == freezed
|
|
||||||
? _value._wires
|
|
||||||
: wires // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<DesignWire>,
|
|
||||||
inputs: inputs == freezed
|
|
||||||
? _value._inputs
|
|
||||||
: inputs // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<DesignInput>,
|
|
||||||
outputs: outputs == freezed
|
|
||||||
? _value._outputs
|
|
||||||
: outputs // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<DesignOutput>,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _$_Design implements _Design {
|
|
||||||
const _$_Design(
|
|
||||||
{required final List<DesignComponent> components,
|
|
||||||
required final List<DesignWire> wires,
|
|
||||||
required final List<DesignInput> inputs,
|
|
||||||
required final List<DesignOutput> outputs})
|
|
||||||
: _components = components,
|
|
||||||
_wires = wires,
|
|
||||||
_inputs = inputs,
|
|
||||||
_outputs = outputs;
|
|
||||||
|
|
||||||
factory _$_Design.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$$_DesignFromJson(json);
|
|
||||||
|
|
||||||
final List<DesignComponent> _components;
|
|
||||||
@override
|
|
||||||
List<DesignComponent> get components {
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(_components);
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<DesignWire> _wires;
|
|
||||||
@override
|
|
||||||
List<DesignWire> get wires {
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(_wires);
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<DesignInput> _inputs;
|
|
||||||
@override
|
|
||||||
List<DesignInput> get inputs {
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(_inputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<DesignOutput> _outputs;
|
|
||||||
@override
|
|
||||||
List<DesignOutput> get outputs {
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(_outputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'Design(components: $components, wires: $wires, inputs: $inputs, outputs: $outputs)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(dynamic other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$_Design &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other._components, _components) &&
|
|
||||||
const DeepCollectionEquality().equals(other._wires, _wires) &&
|
|
||||||
const DeepCollectionEquality().equals(other._inputs, _inputs) &&
|
|
||||||
const DeepCollectionEquality().equals(other._outputs, _outputs));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
const DeepCollectionEquality().hash(_components),
|
|
||||||
const DeepCollectionEquality().hash(_wires),
|
|
||||||
const DeepCollectionEquality().hash(_inputs),
|
|
||||||
const DeepCollectionEquality().hash(_outputs));
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
_$$_DesignCopyWith<_$_Design> get copyWith =>
|
|
||||||
__$$_DesignCopyWithImpl<_$_Design>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$$_DesignToJson(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _Design implements Design {
|
|
||||||
const factory _Design(
|
|
||||||
{required final List<DesignComponent> components,
|
|
||||||
required final List<DesignWire> wires,
|
|
||||||
required final List<DesignInput> inputs,
|
|
||||||
required final List<DesignOutput> outputs}) = _$_Design;
|
|
||||||
|
|
||||||
factory _Design.fromJson(Map<String, dynamic> json) = _$_Design.fromJson;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<DesignComponent> get components => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
List<DesignWire> get wires => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
List<DesignInput> get inputs => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
List<DesignOutput> get outputs => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
_$$_DesignCopyWith<_$_Design> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
DesignComponent _$DesignComponentFromJson(Map<String, dynamic> json) {
|
|
||||||
return _DesignComponent.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$DesignComponent {
|
|
||||||
String get instanceId => throw _privateConstructorUsedError;
|
|
||||||
double get x => throw _privateConstructorUsedError;
|
|
||||||
double get y => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
$DesignComponentCopyWith<DesignComponent> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $DesignComponentCopyWith<$Res> {
|
|
||||||
factory $DesignComponentCopyWith(
|
|
||||||
DesignComponent value, $Res Function(DesignComponent) then) =
|
|
||||||
_$DesignComponentCopyWithImpl<$Res>;
|
|
||||||
$Res call({String instanceId, double x, double y});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$DesignComponentCopyWithImpl<$Res>
|
|
||||||
implements $DesignComponentCopyWith<$Res> {
|
|
||||||
_$DesignComponentCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
final DesignComponent _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function(DesignComponent) _then;
|
|
||||||
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? instanceId = freezed,
|
|
||||||
Object? x = freezed,
|
|
||||||
Object? y = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
instanceId: instanceId == freezed
|
|
||||||
? _value.instanceId
|
|
||||||
: instanceId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
x: x == freezed
|
|
||||||
? _value.x
|
|
||||||
: x // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
y: y == freezed
|
|
||||||
? _value.y
|
|
||||||
: y // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$_DesignComponentCopyWith<$Res>
|
|
||||||
implements $DesignComponentCopyWith<$Res> {
|
|
||||||
factory _$$_DesignComponentCopyWith(
|
|
||||||
_$_DesignComponent value, $Res Function(_$_DesignComponent) then) =
|
|
||||||
__$$_DesignComponentCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
$Res call({String instanceId, double x, double y});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$_DesignComponentCopyWithImpl<$Res>
|
|
||||||
extends _$DesignComponentCopyWithImpl<$Res>
|
|
||||||
implements _$$_DesignComponentCopyWith<$Res> {
|
|
||||||
__$$_DesignComponentCopyWithImpl(
|
|
||||||
_$_DesignComponent _value, $Res Function(_$_DesignComponent) _then)
|
|
||||||
: super(_value, (v) => _then(v as _$_DesignComponent));
|
|
||||||
|
|
||||||
@override
|
|
||||||
_$_DesignComponent get _value => super._value as _$_DesignComponent;
|
|
||||||
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? instanceId = freezed,
|
|
||||||
Object? x = freezed,
|
|
||||||
Object? y = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(_$_DesignComponent(
|
|
||||||
instanceId: instanceId == freezed
|
|
||||||
? _value.instanceId
|
|
||||||
: instanceId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
x: x == freezed
|
|
||||||
? _value.x
|
|
||||||
: x // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
y: y == freezed
|
|
||||||
? _value.y
|
|
||||||
: y // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _$_DesignComponent implements _DesignComponent {
|
|
||||||
const _$_DesignComponent(
|
|
||||||
{required this.instanceId, required this.x, required this.y});
|
|
||||||
|
|
||||||
factory _$_DesignComponent.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$$_DesignComponentFromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String instanceId;
|
|
||||||
@override
|
|
||||||
final double x;
|
|
||||||
@override
|
|
||||||
final double y;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'DesignComponent(instanceId: $instanceId, x: $x, y: $y)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(dynamic other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$_DesignComponent &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other.instanceId, instanceId) &&
|
|
||||||
const DeepCollectionEquality().equals(other.x, x) &&
|
|
||||||
const DeepCollectionEquality().equals(other.y, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
const DeepCollectionEquality().hash(instanceId),
|
|
||||||
const DeepCollectionEquality().hash(x),
|
|
||||||
const DeepCollectionEquality().hash(y));
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
_$$_DesignComponentCopyWith<_$_DesignComponent> get copyWith =>
|
|
||||||
__$$_DesignComponentCopyWithImpl<_$_DesignComponent>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$$_DesignComponentToJson(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _DesignComponent implements DesignComponent {
|
|
||||||
const factory _DesignComponent(
|
|
||||||
{required final String instanceId,
|
|
||||||
required final double x,
|
|
||||||
required final double y}) = _$_DesignComponent;
|
|
||||||
|
|
||||||
factory _DesignComponent.fromJson(Map<String, dynamic> json) =
|
|
||||||
_$_DesignComponent.fromJson;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get instanceId => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
double get x => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
double get y => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
_$$_DesignComponentCopyWith<_$_DesignComponent> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
DesignWire _$DesignWireFromJson(Map<String, dynamic> json) {
|
|
||||||
return _DesignWire.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$DesignWire {
|
|
||||||
String get wireId => throw _privateConstructorUsedError;
|
|
||||||
double get x => throw _privateConstructorUsedError;
|
|
||||||
double get y => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
$DesignWireCopyWith<DesignWire> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $DesignWireCopyWith<$Res> {
|
|
||||||
factory $DesignWireCopyWith(
|
|
||||||
DesignWire value, $Res Function(DesignWire) then) =
|
|
||||||
_$DesignWireCopyWithImpl<$Res>;
|
|
||||||
$Res call({String wireId, double x, double y});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$DesignWireCopyWithImpl<$Res> implements $DesignWireCopyWith<$Res> {
|
|
||||||
_$DesignWireCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
final DesignWire _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function(DesignWire) _then;
|
|
||||||
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? wireId = freezed,
|
|
||||||
Object? x = freezed,
|
|
||||||
Object? y = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
wireId: wireId == freezed
|
|
||||||
? _value.wireId
|
|
||||||
: wireId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
x: x == freezed
|
|
||||||
? _value.x
|
|
||||||
: x // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
y: y == freezed
|
|
||||||
? _value.y
|
|
||||||
: y // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$_DesignWireCopyWith<$Res>
|
|
||||||
implements $DesignWireCopyWith<$Res> {
|
|
||||||
factory _$$_DesignWireCopyWith(
|
|
||||||
_$_DesignWire value, $Res Function(_$_DesignWire) then) =
|
|
||||||
__$$_DesignWireCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
$Res call({String wireId, double x, double y});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$_DesignWireCopyWithImpl<$Res> extends _$DesignWireCopyWithImpl<$Res>
|
|
||||||
implements _$$_DesignWireCopyWith<$Res> {
|
|
||||||
__$$_DesignWireCopyWithImpl(
|
|
||||||
_$_DesignWire _value, $Res Function(_$_DesignWire) _then)
|
|
||||||
: super(_value, (v) => _then(v as _$_DesignWire));
|
|
||||||
|
|
||||||
@override
|
|
||||||
_$_DesignWire get _value => super._value as _$_DesignWire;
|
|
||||||
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? wireId = freezed,
|
|
||||||
Object? x = freezed,
|
|
||||||
Object? y = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(_$_DesignWire(
|
|
||||||
wireId: wireId == freezed
|
|
||||||
? _value.wireId
|
|
||||||
: wireId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
x: x == freezed
|
|
||||||
? _value.x
|
|
||||||
: x // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
y: y == freezed
|
|
||||||
? _value.y
|
|
||||||
: y // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _$_DesignWire implements _DesignWire {
|
|
||||||
const _$_DesignWire({required this.wireId, required this.x, required this.y});
|
|
||||||
|
|
||||||
factory _$_DesignWire.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$$_DesignWireFromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String wireId;
|
|
||||||
@override
|
|
||||||
final double x;
|
|
||||||
@override
|
|
||||||
final double y;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'DesignWire(wireId: $wireId, x: $x, y: $y)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(dynamic other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$_DesignWire &&
|
|
||||||
const DeepCollectionEquality().equals(other.wireId, wireId) &&
|
|
||||||
const DeepCollectionEquality().equals(other.x, x) &&
|
|
||||||
const DeepCollectionEquality().equals(other.y, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
const DeepCollectionEquality().hash(wireId),
|
|
||||||
const DeepCollectionEquality().hash(x),
|
|
||||||
const DeepCollectionEquality().hash(y));
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
_$$_DesignWireCopyWith<_$_DesignWire> get copyWith =>
|
|
||||||
__$$_DesignWireCopyWithImpl<_$_DesignWire>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$$_DesignWireToJson(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _DesignWire implements DesignWire {
|
|
||||||
const factory _DesignWire(
|
|
||||||
{required final String wireId,
|
|
||||||
required final double x,
|
|
||||||
required final double y}) = _$_DesignWire;
|
|
||||||
|
|
||||||
factory _DesignWire.fromJson(Map<String, dynamic> json) =
|
|
||||||
_$_DesignWire.fromJson;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get wireId => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
double get x => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
double get y => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
_$$_DesignWireCopyWith<_$_DesignWire> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
DesignInput _$DesignInputFromJson(Map<String, dynamic> json) {
|
|
||||||
return _DesignInput.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$DesignInput {
|
|
||||||
String get name => throw _privateConstructorUsedError;
|
|
||||||
double get x => throw _privateConstructorUsedError;
|
|
||||||
double get y => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
$DesignInputCopyWith<DesignInput> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $DesignInputCopyWith<$Res> {
|
|
||||||
factory $DesignInputCopyWith(
|
|
||||||
DesignInput value, $Res Function(DesignInput) then) =
|
|
||||||
_$DesignInputCopyWithImpl<$Res>;
|
|
||||||
$Res call({String name, double x, double y});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$DesignInputCopyWithImpl<$Res> implements $DesignInputCopyWith<$Res> {
|
|
||||||
_$DesignInputCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
final DesignInput _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function(DesignInput) _then;
|
|
||||||
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? name = freezed,
|
|
||||||
Object? x = freezed,
|
|
||||||
Object? y = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
name: name == freezed
|
|
||||||
? _value.name
|
|
||||||
: name // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
x: x == freezed
|
|
||||||
? _value.x
|
|
||||||
: x // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
y: y == freezed
|
|
||||||
? _value.y
|
|
||||||
: y // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$_DesignInputCopyWith<$Res>
|
|
||||||
implements $DesignInputCopyWith<$Res> {
|
|
||||||
factory _$$_DesignInputCopyWith(
|
|
||||||
_$_DesignInput value, $Res Function(_$_DesignInput) then) =
|
|
||||||
__$$_DesignInputCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
$Res call({String name, double x, double y});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$_DesignInputCopyWithImpl<$Res> extends _$DesignInputCopyWithImpl<$Res>
|
|
||||||
implements _$$_DesignInputCopyWith<$Res> {
|
|
||||||
__$$_DesignInputCopyWithImpl(
|
|
||||||
_$_DesignInput _value, $Res Function(_$_DesignInput) _then)
|
|
||||||
: super(_value, (v) => _then(v as _$_DesignInput));
|
|
||||||
|
|
||||||
@override
|
|
||||||
_$_DesignInput get _value => super._value as _$_DesignInput;
|
|
||||||
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? name = freezed,
|
|
||||||
Object? x = freezed,
|
|
||||||
Object? y = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(_$_DesignInput(
|
|
||||||
name: name == freezed
|
|
||||||
? _value.name
|
|
||||||
: name // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
x: x == freezed
|
|
||||||
? _value.x
|
|
||||||
: x // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
y: y == freezed
|
|
||||||
? _value.y
|
|
||||||
: y // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _$_DesignInput implements _DesignInput {
|
|
||||||
const _$_DesignInput({required this.name, required this.x, required this.y});
|
|
||||||
|
|
||||||
factory _$_DesignInput.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$$_DesignInputFromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String name;
|
|
||||||
@override
|
|
||||||
final double x;
|
|
||||||
@override
|
|
||||||
final double y;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'DesignInput(name: $name, x: $x, y: $y)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(dynamic other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$_DesignInput &&
|
|
||||||
const DeepCollectionEquality().equals(other.name, name) &&
|
|
||||||
const DeepCollectionEquality().equals(other.x, x) &&
|
|
||||||
const DeepCollectionEquality().equals(other.y, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
const DeepCollectionEquality().hash(name),
|
|
||||||
const DeepCollectionEquality().hash(x),
|
|
||||||
const DeepCollectionEquality().hash(y));
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
_$$_DesignInputCopyWith<_$_DesignInput> get copyWith =>
|
|
||||||
__$$_DesignInputCopyWithImpl<_$_DesignInput>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$$_DesignInputToJson(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _DesignInput implements DesignInput {
|
|
||||||
const factory _DesignInput(
|
|
||||||
{required final String name,
|
|
||||||
required final double x,
|
|
||||||
required final double y}) = _$_DesignInput;
|
|
||||||
|
|
||||||
factory _DesignInput.fromJson(Map<String, dynamic> json) =
|
|
||||||
_$_DesignInput.fromJson;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get name => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
double get x => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
double get y => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
_$$_DesignInputCopyWith<_$_DesignInput> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
DesignOutput _$DesignOutputFromJson(Map<String, dynamic> json) {
|
|
||||||
return _DesignOutput.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$DesignOutput {
|
|
||||||
String get name => throw _privateConstructorUsedError;
|
|
||||||
double get x => throw _privateConstructorUsedError;
|
|
||||||
double get y => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
$DesignOutputCopyWith<DesignOutput> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $DesignOutputCopyWith<$Res> {
|
|
||||||
factory $DesignOutputCopyWith(
|
|
||||||
DesignOutput value, $Res Function(DesignOutput) then) =
|
|
||||||
_$DesignOutputCopyWithImpl<$Res>;
|
|
||||||
$Res call({String name, double x, double y});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$DesignOutputCopyWithImpl<$Res> implements $DesignOutputCopyWith<$Res> {
|
|
||||||
_$DesignOutputCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
final DesignOutput _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function(DesignOutput) _then;
|
|
||||||
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? name = freezed,
|
|
||||||
Object? x = freezed,
|
|
||||||
Object? y = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
name: name == freezed
|
|
||||||
? _value.name
|
|
||||||
: name // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
x: x == freezed
|
|
||||||
? _value.x
|
|
||||||
: x // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
y: y == freezed
|
|
||||||
? _value.y
|
|
||||||
: y // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$_DesignOutputCopyWith<$Res>
|
|
||||||
implements $DesignOutputCopyWith<$Res> {
|
|
||||||
factory _$$_DesignOutputCopyWith(
|
|
||||||
_$_DesignOutput value, $Res Function(_$_DesignOutput) then) =
|
|
||||||
__$$_DesignOutputCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
$Res call({String name, double x, double y});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$_DesignOutputCopyWithImpl<$Res>
|
|
||||||
extends _$DesignOutputCopyWithImpl<$Res>
|
|
||||||
implements _$$_DesignOutputCopyWith<$Res> {
|
|
||||||
__$$_DesignOutputCopyWithImpl(
|
|
||||||
_$_DesignOutput _value, $Res Function(_$_DesignOutput) _then)
|
|
||||||
: super(_value, (v) => _then(v as _$_DesignOutput));
|
|
||||||
|
|
||||||
@override
|
|
||||||
_$_DesignOutput get _value => super._value as _$_DesignOutput;
|
|
||||||
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? name = freezed,
|
|
||||||
Object? x = freezed,
|
|
||||||
Object? y = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(_$_DesignOutput(
|
|
||||||
name: name == freezed
|
|
||||||
? _value.name
|
|
||||||
: name // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
x: x == freezed
|
|
||||||
? _value.x
|
|
||||||
: x // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
y: y == freezed
|
|
||||||
? _value.y
|
|
||||||
: y // ignore: cast_nullable_to_non_nullable
|
|
||||||
as double,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _$_DesignOutput implements _DesignOutput {
|
|
||||||
const _$_DesignOutput({required this.name, required this.x, required this.y});
|
|
||||||
|
|
||||||
factory _$_DesignOutput.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$$_DesignOutputFromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String name;
|
|
||||||
@override
|
|
||||||
final double x;
|
|
||||||
@override
|
|
||||||
final double y;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'DesignOutput(name: $name, x: $x, y: $y)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(dynamic other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$_DesignOutput &&
|
|
||||||
const DeepCollectionEquality().equals(other.name, name) &&
|
|
||||||
const DeepCollectionEquality().equals(other.x, x) &&
|
|
||||||
const DeepCollectionEquality().equals(other.y, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
const DeepCollectionEquality().hash(name),
|
|
||||||
const DeepCollectionEquality().hash(x),
|
|
||||||
const DeepCollectionEquality().hash(y));
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
_$$_DesignOutputCopyWith<_$_DesignOutput> get copyWith =>
|
|
||||||
__$$_DesignOutputCopyWithImpl<_$_DesignOutput>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$$_DesignOutputToJson(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _DesignOutput implements DesignOutput {
|
|
||||||
const factory _DesignOutput(
|
|
||||||
{required final String name,
|
|
||||||
required final double x,
|
|
||||||
required final double y}) = _$_DesignOutput;
|
|
||||||
|
|
||||||
factory _DesignOutput.fromJson(Map<String, dynamic> json) =
|
|
||||||
_$_DesignOutput.fromJson;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get name => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
double get x => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
double get y => throw _privateConstructorUsedError;
|
|
||||||
@override
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
_$$_DesignOutputCopyWith<_$_DesignOutput> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'design.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// JsonSerializableGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
_$_Design _$$_DesignFromJson(Map<String, dynamic> json) => _$_Design(
|
|
||||||
components: (json['components'] as List<dynamic>)
|
|
||||||
.map((e) => DesignComponent.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
wires: (json['wires'] as List<dynamic>)
|
|
||||||
.map((e) => DesignWire.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
inputs: (json['inputs'] as List<dynamic>)
|
|
||||||
.map((e) => DesignInput.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
outputs: (json['outputs'] as List<dynamic>)
|
|
||||||
.map((e) => DesignOutput.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$$_DesignToJson(_$_Design instance) => <String, dynamic>{
|
|
||||||
'components': instance.components,
|
|
||||||
'wires': instance.wires,
|
|
||||||
'inputs': instance.inputs,
|
|
||||||
'outputs': instance.outputs,
|
|
||||||
};
|
|
||||||
|
|
||||||
_$_DesignComponent _$$_DesignComponentFromJson(Map<String, dynamic> json) =>
|
|
||||||
_$_DesignComponent(
|
|
||||||
instanceId: json['instanceId'] as String,
|
|
||||||
x: (json['x'] as num).toDouble(),
|
|
||||||
y: (json['y'] as num).toDouble(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$$_DesignComponentToJson(_$_DesignComponent instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'instanceId': instance.instanceId,
|
|
||||||
'x': instance.x,
|
|
||||||
'y': instance.y,
|
|
||||||
};
|
|
||||||
|
|
||||||
_$_DesignWire _$$_DesignWireFromJson(Map<String, dynamic> json) =>
|
|
||||||
_$_DesignWire(
|
|
||||||
wireId: json['wireId'] as String,
|
|
||||||
x: (json['x'] as num).toDouble(),
|
|
||||||
y: (json['y'] as num).toDouble(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$$_DesignWireToJson(_$_DesignWire instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'wireId': instance.wireId,
|
|
||||||
'x': instance.x,
|
|
||||||
'y': instance.y,
|
|
||||||
};
|
|
||||||
|
|
||||||
_$_DesignInput _$$_DesignInputFromJson(Map<String, dynamic> json) =>
|
|
||||||
_$_DesignInput(
|
|
||||||
name: json['name'] as String,
|
|
||||||
x: (json['x'] as num).toDouble(),
|
|
||||||
y: (json['y'] as num).toDouble(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$$_DesignInputToJson(_$_DesignInput instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'name': instance.name,
|
|
||||||
'x': instance.x,
|
|
||||||
'y': instance.y,
|
|
||||||
};
|
|
||||||
|
|
||||||
_$_DesignOutput _$$_DesignOutputFromJson(Map<String, dynamic> json) =>
|
|
||||||
_$_DesignOutput(
|
|
||||||
name: json['name'] as String,
|
|
||||||
x: (json['x'] as num).toDouble(),
|
|
||||||
y: (json['y'] as num).toDouble(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$$_DesignOutputToJson(_$_DesignOutput instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'name': instance.name,
|
|
||||||
'x': instance.x,
|
|
||||||
'y': instance.y,
|
|
||||||
};
|
|
|
@ -1,19 +1,12 @@
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:logic_circuits_simulator/components/visual_component.dart';
|
|
||||||
import 'package:logic_circuits_simulator/models.dart';
|
import 'package:logic_circuits_simulator/models.dart';
|
||||||
import 'package:logic_circuits_simulator/pages_arguments/design_component.dart';
|
import 'package:logic_circuits_simulator/pages_arguments/design_component.dart';
|
||||||
import 'package:logic_circuits_simulator/state/component.dart';
|
import 'package:logic_circuits_simulator/state/component.dart';
|
||||||
import 'package:logic_circuits_simulator/utils/future_call_debounce.dart';
|
|
||||||
import 'package:logic_circuits_simulator/utils/iterable_extension.dart';
|
|
||||||
import 'package:logic_circuits_simulator/utils/provider_hook.dart';
|
import 'package:logic_circuits_simulator/utils/provider_hook.dart';
|
||||||
import 'package:logic_circuits_simulator/utils/stack_canvas_controller_hook.dart';
|
import 'package:logic_circuits_simulator/utils/stack_canvas_controller_hook.dart';
|
||||||
import 'package:stack_canvas/stack_canvas.dart';
|
import 'package:stack_canvas/stack_canvas.dart';
|
||||||
|
|
||||||
Key canvasKey = GlobalKey();
|
|
||||||
|
|
||||||
class DesignComponentPage extends HookWidget {
|
class DesignComponentPage extends HookWidget {
|
||||||
final ComponentEntry component;
|
final ComponentEntry component;
|
||||||
|
|
||||||
|
@ -26,342 +19,23 @@ class DesignComponentPage extends HookWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final componentState = useProvider<ComponentState>();
|
final componentState = useProvider<ComponentState>();
|
||||||
final canvasController = useStackCanvasController(
|
final canvasController = useStackCanvasController();
|
||||||
offsetReference: Reference.Center,
|
final widgets = useState(<CanvasObject<Widget>>[]);
|
||||||
);
|
|
||||||
|
|
||||||
// Simulation vars
|
|
||||||
final isSimulating = useState(false);
|
|
||||||
final simulatePartially = useState(false);
|
|
||||||
|
|
||||||
useListenable(componentState.partialVisualSimulation!);
|
|
||||||
|
|
||||||
final movingWidgetUpdater = useState<void Function(double dx, double dy)?>(null);
|
|
||||||
final movingWidget = useState<dynamic>(null);
|
|
||||||
final widgets = useMemoized(() => [
|
|
||||||
for (final subcomponent in componentState.designDraft.components)
|
|
||||||
CanvasObject(
|
|
||||||
dx: subcomponent.x,
|
|
||||||
dy: subcomponent.y,
|
|
||||||
width: VisualComponent.getNeededWidth(
|
|
||||||
context,
|
|
||||||
componentState
|
|
||||||
.getMetaByInstance(subcomponent.instanceId)
|
|
||||||
.item2
|
|
||||||
.inputs,
|
|
||||||
componentState
|
|
||||||
.getMetaByInstance(subcomponent.instanceId)
|
|
||||||
.item2
|
|
||||||
.outputs,
|
|
||||||
Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
height: max(
|
|
||||||
componentState.getMetaByInstance(subcomponent.instanceId).item2.inputs.length,
|
|
||||||
componentState.getMetaByInstance(subcomponent.instanceId).item2.outputs.length,
|
|
||||||
) * 30 + 10,
|
|
||||||
child: Listener(
|
|
||||||
behavior: HitTestBehavior.translucent,
|
|
||||||
onPointerDown: (event) {
|
|
||||||
final debouncer = FutureCallDebounce<List<double>>(
|
|
||||||
futureCall: (xyList) {
|
|
||||||
final dx = xyList[0];
|
|
||||||
final dy = xyList[1];
|
|
||||||
return componentState.updateDesign(componentState.designDraft.copyWith(
|
|
||||||
components: componentState.designDraft.components.map(
|
|
||||||
(e) => e.instanceId == subcomponent.instanceId ? e.copyWith(
|
|
||||||
x: subcomponent.x + dx,
|
|
||||||
y: subcomponent.y + dy,
|
|
||||||
) : e,
|
|
||||||
).toList(),
|
|
||||||
), commit: false);
|
|
||||||
},
|
|
||||||
combiner: (oldParams, newParams) {
|
|
||||||
return oldParams.zipWith([newParams], (deltas) => deltas.fold<double>(0.0, (prev, elem) => prev + elem)).toList();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
movingWidgetUpdater.value = (dx, dy) {
|
|
||||||
debouncer.call([dx, dy]);
|
|
||||||
};
|
|
||||||
movingWidget.value = subcomponent;
|
|
||||||
},
|
|
||||||
onPointerUp: (event) {
|
|
||||||
componentState.updateDesign(componentState.designDraft);
|
|
||||||
movingWidgetUpdater.value = null;
|
|
||||||
movingWidget.value = null;
|
|
||||||
},
|
|
||||||
child: MouseRegion(
|
|
||||||
cursor: movingWidget.value == subcomponent ? SystemMouseCursors.move : MouseCursor.defer,
|
|
||||||
child: VisualComponent(
|
|
||||||
name: componentState.getMetaByInstance(subcomponent.instanceId).item2.componentName,
|
|
||||||
inputs: componentState.getMetaByInstance(subcomponent.instanceId).item2.inputs,
|
|
||||||
outputs: componentState.getMetaByInstance(subcomponent.instanceId).item2.outputs,
|
|
||||||
inputColors: isSimulating.value ? {
|
|
||||||
for (final input in componentState.getMetaByInstance(subcomponent.instanceId).item2.inputs)
|
|
||||||
input: componentState.partialVisualSimulation!.inputsValues['${subcomponent.instanceId}/$input'] == true ? Colors.green
|
|
||||||
: componentState.partialVisualSimulation!.inputsValues['${subcomponent.instanceId}/$input'] == false ? Colors.red
|
|
||||||
: Colors.black,
|
|
||||||
} : null,
|
|
||||||
outputColors: isSimulating.value ? {
|
|
||||||
for (final output in componentState.getMetaByInstance(subcomponent.instanceId).item2.outputs)
|
|
||||||
output: componentState.partialVisualSimulation!.outputsValues['${subcomponent.instanceId}/$output'] == true ? Colors.green
|
|
||||||
: componentState.partialVisualSimulation!.outputsValues['${subcomponent.instanceId}/$output'] == false ? Colors.red
|
|
||||||
: Colors.black,
|
|
||||||
} : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
for (final input in componentState.designDraft.inputs)
|
|
||||||
CanvasObject(
|
|
||||||
dx: input.x,
|
|
||||||
dy: input.y,
|
|
||||||
width: IOComponent.getNeededWidth(context, input.name, textStyle: Theme.of(context).textTheme.bodyMedium),
|
|
||||||
height: 40,
|
|
||||||
child: Listener(
|
|
||||||
behavior: HitTestBehavior.translucent,
|
|
||||||
onPointerDown: (event) {
|
|
||||||
final debouncer = FutureCallDebounce<List<double>>(
|
|
||||||
futureCall: (xyList) {
|
|
||||||
final dx = xyList[0];
|
|
||||||
final dy = xyList[1];
|
|
||||||
return componentState.updateDesign(componentState.designDraft.copyWith(
|
|
||||||
inputs: componentState.designDraft.inputs.map(
|
|
||||||
(e) => e.name == input.name ? e.copyWith(
|
|
||||||
x: input.x + dx,
|
|
||||||
y: input.y + dy,
|
|
||||||
) : e,
|
|
||||||
).toList(),
|
|
||||||
), commit: false);
|
|
||||||
},
|
|
||||||
combiner: (oldParams, newParams) {
|
|
||||||
return oldParams.zipWith([newParams], (deltas) => deltas.fold<double>(0.0, (prev, elem) => prev + elem)).toList();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
movingWidgetUpdater.value = (dx, dy) {
|
|
||||||
debouncer.call([dx, dy]);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
onPointerUp: (event) {
|
|
||||||
componentState.updateDesign(componentState.designDraft);
|
|
||||||
movingWidgetUpdater.value = null;
|
|
||||||
},
|
|
||||||
child: IOComponent(
|
|
||||||
input: true,
|
|
||||||
name: input.name,
|
|
||||||
width: IOComponent.getNeededWidth(context, input.name, textStyle: Theme.of(context).textTheme.bodyMedium),
|
|
||||||
color: isSimulating.value
|
|
||||||
? (componentState.partialVisualSimulation!.outputsValues['self/${input.name}']!
|
|
||||||
? Colors.green
|
|
||||||
: Colors.red)
|
|
||||||
: null,
|
|
||||||
onTap: isSimulating.value ? () {
|
|
||||||
componentState.partialVisualSimulation!.toggleInput(input.name);
|
|
||||||
} : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
for (final output in componentState.designDraft.outputs)
|
|
||||||
CanvasObject(
|
|
||||||
dx: output.x,
|
|
||||||
dy: output.y,
|
|
||||||
width: IOComponent.getNeededWidth(context, output.name, textStyle: Theme.of(context).textTheme.bodyMedium),
|
|
||||||
height: 40,
|
|
||||||
child: Listener(
|
|
||||||
behavior: HitTestBehavior.translucent,
|
|
||||||
onPointerDown: (event) {
|
|
||||||
final debouncer = FutureCallDebounce<List<double>>(
|
|
||||||
futureCall: (xyList) {
|
|
||||||
final dx = xyList[0];
|
|
||||||
final dy = xyList[1];
|
|
||||||
return componentState.updateDesign(componentState.designDraft.copyWith(
|
|
||||||
outputs: componentState.designDraft.outputs.map(
|
|
||||||
(e) => e.name == output.name ? e.copyWith(
|
|
||||||
x: output.x + dx,
|
|
||||||
y: output.y + dy,
|
|
||||||
) : e,
|
|
||||||
).toList(),
|
|
||||||
), commit: false);
|
|
||||||
},
|
|
||||||
combiner: (oldParams, newParams) {
|
|
||||||
return oldParams.zipWith([newParams], (deltas) => deltas.fold<double>(0.0, (prev, elem) => prev + elem)).toList();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
movingWidgetUpdater.value = (dx, dy) {
|
|
||||||
debouncer.call([dx, dy]);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
onPointerUp: (event) {
|
|
||||||
componentState.updateDesign(componentState.designDraft);
|
|
||||||
movingWidgetUpdater.value = null;
|
|
||||||
},
|
|
||||||
child: IOComponent(
|
|
||||||
input: false,
|
|
||||||
name: output.name,
|
|
||||||
width: IOComponent.getNeededWidth(context, output.name, textStyle: Theme.of(context).textTheme.bodyMedium),
|
|
||||||
color: isSimulating.value
|
|
||||||
? (componentState.partialVisualSimulation!.inputsValues['self/${output.name}'] == true ? Colors.green
|
|
||||||
: componentState.partialVisualSimulation!.inputsValues['self/${output.name}'] == false ? Colors.red
|
|
||||||
: null)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
for (final wire in componentState.wiring.wires)
|
|
||||||
(() {
|
|
||||||
const ioCircleDiameter = 20;
|
|
||||||
|
|
||||||
Offset from, to;
|
|
||||||
if (wire.output.split('/')[0] == 'self') {
|
|
||||||
// It's a component input
|
|
||||||
|
|
||||||
// Find input
|
|
||||||
final inputName = wire.output.split('/')[1];
|
|
||||||
final design = componentState.designDraft.inputs.where((i) => i.name == inputName).first;
|
|
||||||
|
|
||||||
from = Offset(
|
|
||||||
// Take into account widget length
|
|
||||||
design.x +
|
|
||||||
IOComponent.getNeededWidth(
|
|
||||||
context,
|
|
||||||
inputName,
|
|
||||||
textStyle: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
design.y + ioCircleDiameter + 1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// It's a subcomponent output
|
|
||||||
|
|
||||||
// Find subcomponent
|
|
||||||
final split = wire.output.split('/');
|
|
||||||
final subcomponentId = split[0];
|
|
||||||
final outputName = split[1];
|
|
||||||
final design = componentState.designDraft.components.where((c) => c.instanceId == subcomponentId).first;
|
|
||||||
final subcomponent = componentState.getMetaByInstance(subcomponentId).item2;
|
|
||||||
|
|
||||||
from = Offset(
|
|
||||||
// Take into account widget length
|
|
||||||
design.x +
|
|
||||||
VisualComponent.getNeededWidth(
|
|
||||||
context,
|
|
||||||
subcomponent.inputs,
|
|
||||||
subcomponent.outputs,
|
|
||||||
Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
design.y +
|
|
||||||
VisualComponent.getHeightOfIO(
|
|
||||||
context,
|
|
||||||
subcomponent.outputs,
|
|
||||||
subcomponent.outputs.indexOf(outputName),
|
|
||||||
Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wire.input.split('/')[0] == 'self') {
|
|
||||||
// It's a component output
|
|
||||||
|
|
||||||
// Find output
|
|
||||||
final outputName = wire.input.split('/')[1];
|
|
||||||
final design = componentState.designDraft.outputs.where((o) => o.name == outputName).first;
|
|
||||||
|
|
||||||
to = Offset(
|
|
||||||
design.x,
|
|
||||||
design.y + ioCircleDiameter + 1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// It's a subcomponent input
|
|
||||||
|
|
||||||
// Find subcomponent
|
|
||||||
final split = wire.input.split('/');
|
|
||||||
final subcomponentId = split[0];
|
|
||||||
final inputName = split[1];
|
|
||||||
final design = componentState.designDraft.components.where((c) => c.instanceId == subcomponentId).first;
|
|
||||||
final subcomponent = componentState.getMetaByInstance(subcomponentId).item2;
|
|
||||||
|
|
||||||
to = Offset(
|
|
||||||
// Take into account widget length
|
|
||||||
design.x,
|
|
||||||
design.y +
|
|
||||||
VisualComponent.getHeightOfIO(
|
|
||||||
context,
|
|
||||||
subcomponent.inputs,
|
|
||||||
subcomponent.inputs.indexOf(inputName),
|
|
||||||
Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var wireColor = Colors.black;
|
|
||||||
if (isSimulating.value) {
|
|
||||||
final wireValue = componentState.partialVisualSimulation!.outputsValues[wire.output];
|
|
||||||
if (wireValue == true) {
|
|
||||||
wireColor = Colors.green;
|
|
||||||
}
|
|
||||||
else if (wireValue == false) {
|
|
||||||
wireColor = Colors.red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CanvasObject(
|
|
||||||
dx: min(from.dx, to.dx),
|
|
||||||
dy: min(from.dy, to.dy),
|
|
||||||
width: (to - from).dx.abs(),
|
|
||||||
height: (to - from).dy.abs(),
|
|
||||||
child: IgnorePointer(
|
|
||||||
child: WireWidget(
|
|
||||||
from: from,
|
|
||||||
to: to,
|
|
||||||
color: wireColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
})(),
|
|
||||||
], [componentState.designDraft, isSimulating.value, componentState.partialVisualSimulation!.outputsValues]);
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
final wList = widgets;
|
canvasController.addCanvasObjects(widgets.value);
|
||||||
canvasController.addCanvasObjects(wList);
|
|
||||||
|
|
||||||
return () {
|
return () {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
for (final obj in wList) {
|
canvasController.clearCanvas();
|
||||||
canvasController.removeCanvasObject(obj);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}, [widgets]);
|
}, [widgets]);
|
||||||
useEffect(() {
|
|
||||||
if (isSimulating.value && !simulatePartially.value && componentState.partialVisualSimulation!.nextToSimulate.isNotEmpty) {
|
|
||||||
componentState.partialVisualSimulation!.nextStep();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}, [componentState.partialVisualSimulation!.outputsValues.entries.map((e) => '${e.key}:${e.value}').join(';'), simulatePartially.value, isSimulating.value]);
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
title: Text('${isSimulating.value ? 'Simulation' : 'Design'} - ${component.componentName}'),
|
title: Text('Design - ${component.componentName}'),
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(isSimulating.value ? Icons.stop : Icons.start),
|
|
||||||
tooltip: isSimulating.value ? 'Stop Simulation' : 'Start Simulation',
|
|
||||||
onPressed: () {
|
|
||||||
isSimulating.value = !isSimulating.value;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
body: OrientationBuilder(
|
||||||
),
|
|
||||||
body: GestureDetector(
|
|
||||||
onPanUpdate: (update) {
|
|
||||||
final hw = movingWidgetUpdater.value;
|
|
||||||
if (hw == null || isSimulating.value) {
|
|
||||||
canvasController.offset = canvasController.offset.translate(update.delta.dx, update.delta.dy);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
hw(update.delta.dx, update.delta.dy);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: OrientationBuilder(
|
|
||||||
builder: (context, orientation) {
|
builder: (context, orientation) {
|
||||||
if (orientation == Orientation.portrait) {
|
if (orientation == Orientation.portrait) {
|
||||||
return Column(
|
return Column(
|
||||||
|
@ -369,10 +43,7 @@ class DesignComponentPage extends HookWidget {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StackCanvas(
|
child: StackCanvas(
|
||||||
key: canvasKey,
|
|
||||||
canvasController: canvasController,
|
canvasController: canvasController,
|
||||||
animationDuration: const Duration(milliseconds: 50),
|
|
||||||
// disposeController: false,
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.background,
|
backgroundColor: Theme.of(context).colorScheme.background,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -385,11 +56,7 @@ class DesignComponentPage extends HookWidget {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StackCanvas(
|
child: StackCanvas(
|
||||||
key: canvasKey,
|
|
||||||
canvasController: canvasController,
|
canvasController: canvasController,
|
||||||
animationDuration: const Duration(milliseconds: 50),
|
|
||||||
// disposeController: false,
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.background,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -397,7 +64,6 @@ class DesignComponentPage extends HookWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -45,7 +45,6 @@ class ProjectsPage extends StatelessWidget {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
foregroundColor: MaterialStateProperty.all(Colors.white),
|
|
||||||
backgroundColor: MaterialStateProperty.all(Colors.red),
|
backgroundColor: MaterialStateProperty.all(Colors.red),
|
||||||
),
|
),
|
||||||
child: const Text('Delete'),
|
child: const Text('Delete'),
|
||||||
|
|
|
@ -12,41 +12,13 @@ class ComponentState extends ChangeNotifier {
|
||||||
ProjectEntry? _currentProject;
|
ProjectEntry? _currentProject;
|
||||||
ComponentEntry? _currentComponent;
|
ComponentEntry? _currentComponent;
|
||||||
Wiring _wiring = const Wiring(instances: [], wires: []);
|
Wiring _wiring = const Wiring(instances: [], wires: []);
|
||||||
Wiring? _wiringDraft;
|
|
||||||
Design _design = const Design(components: [], wires: [], inputs: [], outputs: []);
|
|
||||||
Design? _designDraft;
|
|
||||||
SimulatedComponent? _simulatedComponent;
|
SimulatedComponent? _simulatedComponent;
|
||||||
PartialVisualSimulation? _partialVisualSimulation;
|
|
||||||
|
|
||||||
final Map<String, Tuple2<ProjectEntry, ComponentEntry>> _dependenciesMap = {};
|
final Map<String, Tuple2<ProjectEntry, ComponentEntry>> _dependenciesMap = {};
|
||||||
|
|
||||||
ProjectEntry? get currentProject => _currentProject;
|
ProjectEntry? get currentProject => _currentProject;
|
||||||
ComponentEntry? get currentComponent => _currentComponent;
|
ComponentEntry? get currentComponent => _currentComponent;
|
||||||
Wiring get wiring => _wiring;
|
Wiring get wiring => _wiring;
|
||||||
Wiring get wiringDraft => _wiringDraft ?? _wiring;
|
|
||||||
Design get design => _design;
|
|
||||||
Design get designDraft => _designDraft ?? _design;
|
|
||||||
PartialVisualSimulation? get partialVisualSimulation => _partialVisualSimulation;
|
|
||||||
|
|
||||||
Future<SimulatedComponent> _onRequiredDependency(String depId) async {
|
|
||||||
final t = _dependenciesMap[depId]!;
|
|
||||||
final proj = t.item1;
|
|
||||||
final comp = t.item2;
|
|
||||||
final state = comp.visualDesigned ? ComponentState() : null;
|
|
||||||
if (state != null) {
|
|
||||||
await state.setCurrentComponent(
|
|
||||||
project: proj,
|
|
||||||
component: comp,
|
|
||||||
onDependencyNeeded: (projId, compId) async => _dependenciesMap['$projId/$compId'],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return SimulatedComponent(
|
|
||||||
project: proj,
|
|
||||||
component: comp,
|
|
||||||
onRequiredDependency: _onRequiredDependency,
|
|
||||||
state: state,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Directory> _getComponentDir() async {
|
Future<Directory> _getComponentDir() async {
|
||||||
if (_currentProject == null) {
|
if (_currentProject == null) {
|
||||||
|
@ -68,11 +40,6 @@ class ComponentState extends ChangeNotifier {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<File> _getDesignFile() async {
|
|
||||||
final result = File(path.join((await _getComponentDir()).path, 'design.json'));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadComponentFiles() async {
|
Future<void> _loadComponentFiles() async {
|
||||||
final wiringFile = await _getWiringFile();
|
final wiringFile = await _getWiringFile();
|
||||||
if (!await wiringFile.exists()) {
|
if (!await wiringFile.exists()) {
|
||||||
|
@ -82,17 +49,6 @@ class ComponentState extends ChangeNotifier {
|
||||||
else {
|
else {
|
||||||
_wiring = Wiring.fromJson(jsonDecode(await wiringFile.readAsString()));
|
_wiring = Wiring.fromJson(jsonDecode(await wiringFile.readAsString()));
|
||||||
}
|
}
|
||||||
_wiringDraft = null;
|
|
||||||
|
|
||||||
final designFile = await _getDesignFile();
|
|
||||||
if (!await designFile.exists()) {
|
|
||||||
_design = const Design(components: [], wires: [], inputs: [], outputs: []);
|
|
||||||
await designFile.writeAsString(jsonEncode(_design));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_design = Design.fromJson(jsonDecode(await designFile.readAsString()));
|
|
||||||
}
|
|
||||||
_designDraft = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setCurrentComponent({
|
Future<void> setCurrentComponent({
|
||||||
|
@ -122,18 +78,7 @@ class ComponentState extends ChangeNotifier {
|
||||||
throw DependenciesNotSatisfiedException(dependencies: unsatisfiedDependencies);
|
throw DependenciesNotSatisfiedException(dependencies: unsatisfiedDependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _loadComponentFiles();
|
return _loadComponentFiles().then((_) => notifyListeners());
|
||||||
|
|
||||||
if (component.visualDesigned) {
|
|
||||||
_partialVisualSimulation = await PartialVisualSimulation.init(
|
|
||||||
project: project,
|
|
||||||
component: component,
|
|
||||||
state: this,
|
|
||||||
onRequiredDependency: _onRequiredDependency,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void noComponent() {
|
void noComponent() {
|
||||||
|
@ -141,63 +86,41 @@ class ComponentState extends ChangeNotifier {
|
||||||
_currentProject = null;
|
_currentProject = null;
|
||||||
_currentComponent = null;
|
_currentComponent = null;
|
||||||
_wiring = const Wiring(instances: [], wires: []);
|
_wiring = const Wiring(instances: [], wires: []);
|
||||||
_design = const Design(components: [], wires: [], inputs: [], outputs: []);
|
|
||||||
_wiringDraft = _designDraft = null;
|
|
||||||
_simulatedComponent = null;
|
_simulatedComponent = null;
|
||||||
_partialVisualSimulation = null;
|
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple2<ProjectEntry, ComponentEntry> getMetaByInstance(String instanceId) {
|
|
||||||
for (final instance in wiring.instances) {
|
|
||||||
if (instance.instanceId == instanceId) {
|
|
||||||
return _dependenciesMap[instance.componentId]!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Exception('Instance $instanceId not found in the dependencies map');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Map<String, bool>> simulate(Map<String, bool> inputs) async {
|
Future<Map<String, bool>> simulate(Map<String, bool> inputs) async {
|
||||||
|
Future<SimulatedComponent> onRequiredDependency(String depId) async {
|
||||||
|
final t = _dependenciesMap[depId]!;
|
||||||
|
final proj = t.item1;
|
||||||
|
final comp = t.item2;
|
||||||
|
final state = comp.visualDesigned ? ComponentState() : null;
|
||||||
|
if (state != null) {
|
||||||
|
await state.setCurrentComponent(
|
||||||
|
project: proj,
|
||||||
|
component: comp,
|
||||||
|
onDependencyNeeded: (projId, compId) async => _dependenciesMap['$projId/$compId'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return SimulatedComponent(
|
||||||
|
project: proj,
|
||||||
|
component: comp,
|
||||||
|
onRequiredDependency: onRequiredDependency,
|
||||||
|
state: state,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_simulatedComponent ??= SimulatedComponent(
|
_simulatedComponent ??= SimulatedComponent(
|
||||||
project: _currentProject!,
|
project: _currentProject!,
|
||||||
component: _currentComponent!,
|
component: _currentComponent!,
|
||||||
onRequiredDependency: _onRequiredDependency,
|
onRequiredDependency: onRequiredDependency,
|
||||||
state: this,
|
state: this,
|
||||||
);
|
);
|
||||||
|
|
||||||
return _simulatedComponent!.simulate(inputs);
|
return _simulatedComponent!.simulate(inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Design> updateDesign(Design newDesign, {bool commit = true}) async {
|
|
||||||
if (commit) {
|
|
||||||
_design = newDesign;
|
|
||||||
_designDraft = null;
|
|
||||||
final designFile = await _getDesignFile();
|
|
||||||
await designFile.writeAsString(jsonEncode(newDesign));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_designDraft = newDesign;
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
return designDraft;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Wiring> updateWiring(Wiring newWiring, {bool commit = true}) async {
|
|
||||||
if (commit) {
|
|
||||||
_wiring = newWiring;
|
|
||||||
_wiringDraft = null;
|
|
||||||
final wiringFile = await _getWiringFile();
|
|
||||||
await wiringFile.writeAsString(jsonEncode(newWiring));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_wiringDraft = newWiring;
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
return wiringDraft;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DependenciesNotSatisfiedException with Exception {
|
class DependenciesNotSatisfiedException with Exception {
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
class FutureCallDebounce<TParams extends Object> {
|
|
||||||
TParams? _params;
|
|
||||||
Future? _awaited;
|
|
||||||
final Future Function(TParams) futureCall;
|
|
||||||
final TParams Function(TParams oldParams, TParams newParams) combiner;
|
|
||||||
|
|
||||||
static TParams _defaultCombiner<TParams>(TParams _, TParams newParams) => newParams;
|
|
||||||
|
|
||||||
FutureCallDebounce({required this.futureCall, required this.combiner});
|
|
||||||
FutureCallDebounce.replaceCombiner({required this.futureCall}) : combiner = _defaultCombiner;
|
|
||||||
|
|
||||||
void call(TParams newParams) {
|
|
||||||
if (_params != null) {
|
|
||||||
_params = combiner(_params!, newParams);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_params = newParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
_awaited ??= futureCall(_params!).then((value) => _awaited = null);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:logic_circuits_simulator/models.dart';
|
import 'package:logic_circuits_simulator/models.dart';
|
||||||
import 'package:logic_circuits_simulator/state/component.dart';
|
import 'package:logic_circuits_simulator/state/component.dart';
|
||||||
import 'package:logic_circuits_simulator/utils/iterable_extension.dart';
|
import 'package:logic_circuits_simulator/utils/iterable_extension.dart';
|
||||||
|
@ -12,18 +10,19 @@ class SimulatedComponent {
|
||||||
final Future<SimulatedComponent> Function(String depId) onRequiredDependency;
|
final Future<SimulatedComponent> Function(String depId) onRequiredDependency;
|
||||||
final _instances = <String, SimulatedComponent>{};
|
final _instances = <String, SimulatedComponent>{};
|
||||||
|
|
||||||
SimulatedComponent(
|
SimulatedComponent({
|
||||||
{required this.project,
|
required this.project,
|
||||||
required this.component,
|
required this.component,
|
||||||
required this.onRequiredDependency,
|
required this.onRequiredDependency,
|
||||||
this.state});
|
this.state
|
||||||
|
});
|
||||||
|
|
||||||
Future<SimulatedComponent> _getInstance(
|
Future<SimulatedComponent> _getInstance(String instanceId, String? depId) async {
|
||||||
String instanceId, String? depId) async {
|
|
||||||
if (!_instances.containsKey(instanceId)) {
|
if (!_instances.containsKey(instanceId)) {
|
||||||
if (depId != null) {
|
if (depId != null) {
|
||||||
_instances[instanceId] = await onRequiredDependency(depId);
|
_instances[instanceId] = await onRequiredDependency(depId);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
throw Exception('Attempted to get instance of unknown component');
|
throw Exception('Attempted to get instance of unknown component');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,11 +37,13 @@ class SimulatedComponent {
|
||||||
if (component.truthTable != null) {
|
if (component.truthTable != null) {
|
||||||
final output = component.truthTable![input];
|
final output = component.truthTable![input];
|
||||||
return {
|
return {
|
||||||
for (final it in component.outputs
|
for (final it in component.outputs.indexedMap(
|
||||||
.indexedMap((index, outName) => [outName, output[index]]))
|
(index, outName) => [outName, output[index]]
|
||||||
it[0]: it[1] == '1'
|
))
|
||||||
|
it[0] : it[1] == '1'
|
||||||
};
|
};
|
||||||
} else if (component.logicExpression != null) {
|
}
|
||||||
|
else if (component.logicExpression != null) {
|
||||||
// Somehow?
|
// Somehow?
|
||||||
// A truth table should be automatically generated for every logic expression component.
|
// A truth table should be automatically generated for every logic expression component.
|
||||||
// Might as well handle cases where that isn't done anyway.
|
// Might as well handle cases where that isn't done anyway.
|
||||||
|
@ -54,10 +55,15 @@ class SimulatedComponent {
|
||||||
return [output, le.evaluate(inputs)];
|
return [output, le.evaluate(inputs)];
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return {for (final it in results) it[0] as String: it[1] as bool};
|
return {
|
||||||
} else if (state == null) {
|
for (final it in results)
|
||||||
|
it[0] as String : it[1] as bool
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (state == null) {
|
||||||
throw Exception('Cannot simulate designed component without its state');
|
throw Exception('Cannot simulate designed component without its state');
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// Create instances
|
// Create instances
|
||||||
final wiring = state!.wiring;
|
final wiring = state!.wiring;
|
||||||
for (final instance in wiring.instances) {
|
for (final instance in wiring.instances) {
|
||||||
|
@ -69,7 +75,8 @@ class SimulatedComponent {
|
||||||
...component.outputs.map((output) => 'self/$output'),
|
...component.outputs.map((output) => 'self/$output'),
|
||||||
];
|
];
|
||||||
final knownSources = {
|
final knownSources = {
|
||||||
for (final entry in inputs.entries) 'self/${entry.key}': entry.value
|
for (final entry in inputs.entries)
|
||||||
|
'self/${entry.key}': entry.value
|
||||||
};
|
};
|
||||||
final knownSinks = <String, bool>{};
|
final knownSinks = <String, bool>{};
|
||||||
|
|
||||||
|
@ -79,40 +86,37 @@ class SimulatedComponent {
|
||||||
if (knownSinks.containsKey(sink)) {
|
if (knownSinks.containsKey(sink)) {
|
||||||
// Requirement satisfied
|
// Requirement satisfied
|
||||||
continue;
|
continue;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// Find wire that provides sink
|
// Find wire that provides sink
|
||||||
final wire = wiring.wires.where((wire) => wire.input == sink).first;
|
final wire = wiring.wires.where((wire) => wire.input == sink).first;
|
||||||
if (knownSources.containsKey(wire.output)) {
|
if (knownSources.containsKey(wire.output)) {
|
||||||
// If we know the output provided through the wire,
|
// If we know the output provided through the wire,
|
||||||
// we know the input provided to the sink
|
// we know the input provided to the sink
|
||||||
knownSinks[sink] = knownSources[wire.output]!;
|
knownSinks[sink] = knownSources[wire.output]!;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// The instance providing the source for the wire has not been simulated.
|
// The instance providing the source for the wire has not been simulated.
|
||||||
// See if all its sinks are known:
|
// See if all its sinks are known:
|
||||||
final instanceId = wire.output.split('/')[0];
|
final instanceId = wire.output.split('/')[0];
|
||||||
final instance = await _getInstance(instanceId, null);
|
final instance = await _getInstance(instanceId, null);
|
||||||
final depSinks = instance.component.inputs
|
final depSinks = instance.component.inputs.map((input) => '$instanceId/$input').toList();
|
||||||
.map((input) => '$instanceId/$input')
|
if (depSinks.map((depSink) => !knownSinks.containsKey(depSink)).where((cond) => cond).isEmpty) {
|
||||||
.toList();
|
|
||||||
if (depSinks
|
|
||||||
.map((depSink) => !knownSinks.containsKey(depSink))
|
|
||||||
.where((cond) => cond)
|
|
||||||
.isEmpty) {
|
|
||||||
// If so, then simulate
|
// If so, then simulate
|
||||||
final results = await instance.simulate({
|
final results = await instance.simulate({
|
||||||
for (final depSink in depSinks)
|
for (final depSink in depSinks)
|
||||||
depSink.split('/')[1]: knownSinks[depSink]!
|
depSink.split('/')[1] : knownSinks[depSink]!
|
||||||
});
|
});
|
||||||
knownSources.addAll({
|
knownSources.addAll({
|
||||||
for (final result in results.entries)
|
for (final result in results.entries)
|
||||||
'$instanceId/${result.key}': result.value
|
'$instanceId/${result.key}' : result.value
|
||||||
});
|
});
|
||||||
// And resolve needed sink
|
// And resolve needed sink
|
||||||
knownSinks[sink] = knownSources[wire.output]!;
|
knownSinks[sink] = knownSources[wire.output]!;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// Otherwise, require the sinks and reschedule the current one
|
// Otherwise, require the sinks and reschedule the current one
|
||||||
requiredSinks.addAll(depSinks
|
requiredSinks.addAll(depSinks.where((depSink) => !knownSinks.containsKey(depSink)));
|
||||||
.where((depSink) => !knownSinks.containsKey(depSink)));
|
|
||||||
requiredSinks.add(sink);
|
requiredSinks.add(sink);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,162 +125,8 @@ class SimulatedComponent {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
for (final output in component.outputs)
|
for (final output in component.outputs)
|
||||||
output: knownSinks['self/$output']!
|
output : knownSinks['self/$output']!
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PartialVisualSimulation with ChangeNotifier {
|
|
||||||
final Map<String, bool?> _outputsValues = {};
|
|
||||||
final List<String> nextToSimulate = [];
|
|
||||||
final List<String> _alreadySimulated = [];
|
|
||||||
|
|
||||||
UnmodifiableMapView<String, bool?> get outputsValues => UnmodifiableMapView(_outputsValues);
|
|
||||||
UnmodifiableMapView<String, bool?> get inputsValues => UnmodifiableMapView({
|
|
||||||
for (final entry in outputsValues.entries)
|
|
||||||
if (entry.value != null)
|
|
||||||
for (final wire in state.wiringDraft.wires.where((w) => w.output == entry.key))
|
|
||||||
wire.input: entry.value
|
|
||||||
});
|
|
||||||
|
|
||||||
final ProjectEntry project;
|
|
||||||
final ComponentEntry component;
|
|
||||||
final ComponentState state;
|
|
||||||
final Future<SimulatedComponent> Function(String depId) onRequiredDependency;
|
|
||||||
final _instances = <String, SimulatedComponent>{};
|
|
||||||
|
|
||||||
PartialVisualSimulation._(
|
|
||||||
{required this.project,
|
|
||||||
required this.component,
|
|
||||||
required this.state,
|
|
||||||
required this.onRequiredDependency});
|
|
||||||
|
|
||||||
Future<SimulatedComponent> _getInstance(
|
|
||||||
String instanceId, String? depId) async {
|
|
||||||
if (!_instances.containsKey(instanceId)) {
|
|
||||||
if (depId != null) {
|
|
||||||
_instances[instanceId] = await onRequiredDependency(depId);
|
|
||||||
} else {
|
|
||||||
throw Exception('Attempted to get instance of unknown component');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _instances[instanceId]!;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<PartialVisualSimulation> init({
|
|
||||||
required ProjectEntry project,
|
|
||||||
required ComponentEntry component,
|
|
||||||
required ComponentState state,
|
|
||||||
required Future<SimulatedComponent> Function(String depId) onRequiredDependency,
|
|
||||||
Map<String, bool>? inputs,
|
|
||||||
}) async {
|
|
||||||
final sim = PartialVisualSimulation._(project: project, component: component, state: state, onRequiredDependency: onRequiredDependency);
|
|
||||||
|
|
||||||
// Create instances
|
|
||||||
final wiring = state.wiring;
|
|
||||||
for (final instance in wiring.instances) {
|
|
||||||
await sim._getInstance(instance.instanceId, instance.componentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate inputs
|
|
||||||
inputs ??= {};
|
|
||||||
for (final input in component.inputs) {
|
|
||||||
if (!inputs.containsKey(input)) {
|
|
||||||
inputs[input] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await sim.provideInputs(inputs);
|
|
||||||
|
|
||||||
return sim;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> toggleInput(String inputName) {
|
|
||||||
final inputValue = _outputsValues['self/$inputName']!;
|
|
||||||
return modifyInput(inputName, !inputValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> modifyInput(String inputName, bool newValue) {
|
|
||||||
_outputsValues['self/$inputName'] = newValue;
|
|
||||||
for (final key in _outputsValues.keys.toList()) {
|
|
||||||
if (!key.startsWith('self/')) {
|
|
||||||
_outputsValues.remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_alreadySimulated.clear();
|
|
||||||
return reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> provideInputs(Map<String, bool> inputs) {
|
|
||||||
_alreadySimulated.clear();
|
|
||||||
_outputsValues.clear();
|
|
||||||
for (final entry in inputs.entries) {
|
|
||||||
_outputsValues['self/${entry.key}'] = entry.value;
|
|
||||||
}
|
|
||||||
return reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> reset() async {
|
|
||||||
nextToSimulate.clear();
|
|
||||||
|
|
||||||
final neededToBeNext = <String, List<String>>{};
|
|
||||||
|
|
||||||
for (final wire in state.wiringDraft.wires) {
|
|
||||||
if (_outputsValues.containsKey(wire.output)) {
|
|
||||||
final subcomponentId = wire.input.split('/')[0];
|
|
||||||
|
|
||||||
// Ignore component outputs, they require no computation
|
|
||||||
if (subcomponentId == 'self') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip already simulated subcomponents
|
|
||||||
if (_alreadySimulated.contains(subcomponentId)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (neededToBeNext.containsKey(subcomponentId)) {
|
|
||||||
neededToBeNext[subcomponentId]!.remove(wire.input.split('/')[1]);
|
|
||||||
if (neededToBeNext[subcomponentId]!.isEmpty) {
|
|
||||||
nextToSimulate.add(subcomponentId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
neededToBeNext[subcomponentId] =
|
|
||||||
(await _getInstance(subcomponentId, null))
|
|
||||||
.component
|
|
||||||
.inputs
|
|
||||||
.whereNot((e) => e == wire.input.split('/')[1])
|
|
||||||
.toList();
|
|
||||||
if (neededToBeNext[subcomponentId]!.isEmpty) {
|
|
||||||
nextToSimulate.add(subcomponentId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> nextStep() async {
|
|
||||||
if (nextToSimulate.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final currentlySimulating = nextToSimulate.toList();
|
|
||||||
|
|
||||||
for (final subcomponentId in currentlySimulating) {
|
|
||||||
final sim = await _getInstance(subcomponentId, null);
|
|
||||||
final outputs = await sim.simulate({
|
|
||||||
for (final input in sim.component.inputs)
|
|
||||||
input: inputsValues['$subcomponentId/$input']!
|
|
||||||
});
|
|
||||||
for (final entry in outputs.entries) {
|
|
||||||
_outputsValues['$subcomponentId/${entry.key}'] = entry.value;
|
|
||||||
}
|
|
||||||
_alreadySimulated.add(subcomponentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -633,11 +633,9 @@ packages:
|
||||||
stack_canvas:
|
stack_canvas:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
name: stack_canvas
|
||||||
ref: HEAD
|
url: "https://pub.dartlang.org"
|
||||||
resolved-ref: "83e1032940e9424572c60ddad397f38320ee9cd4"
|
source: hosted
|
||||||
url: "https://github.com/dancojocaru2000/stack_canvas.git"
|
|
||||||
source: git
|
|
||||||
version: "0.2.0+2"
|
version: "0.2.0+2"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
|
|
@ -38,8 +38,7 @@ dependencies:
|
||||||
archive: ^3.3.0
|
archive: ^3.3.0
|
||||||
file_picker: ^4.6.1
|
file_picker: ^4.6.1
|
||||||
share_plus: ^4.0.8
|
share_plus: ^4.0.8
|
||||||
stack_canvas:
|
stack_canvas: ^0.2.0+2
|
||||||
git: https://github.com/dancojocaru2000/stack_canvas.git
|
|
||||||
tuple: ^2.0.0
|
tuple: ^2.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
Loading…
Add table
Reference in a new issue