Skip to content

Commit

Permalink
Render traces for transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
mfikes committed Jan 16, 2024
1 parent 270e409 commit 0d9e430
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 66 deletions.
34 changes: 30 additions & 4 deletions Impedance Converter/Impedance Converter/ImmittanceView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import SwiftUI
import Numerics

struct PolarParameterView<UnitType>: View where UnitType: RawRepresentable, UnitType.RawValue == String, UnitType: UnitWithPowerOfTen {
let viewModel: ViewModel
@Binding var complexValue: Complex<Double>
var magnitudeUnit: UnitType
var angleUnit: AngleUnit
Expand All @@ -16,14 +17,20 @@ struct PolarParameterView<UnitType>: View where UnitType: RawRepresentable, Unit
UnitInputView(value: Binding(
get: { self.complexValue.length },
set: {
self.complexValue = Complex.init(length: $0, phase: self.complexValue.phase)
viewModel.setValueRecordingTrace(from: self.complexValue.length, to: $0) {
intermediateValue in
self.complexValue = Complex.init(length: intermediateValue, phase: self.complexValue.phase)
}
}
), unit: magnitudeUnit, label: magnitudeLabel, description: magnitudeDescription)

UnitInputView(value: Binding(
get: { Angle(radians: self.complexValue.phase).degrees },
set: {
self.complexValue = Complex.init(length: self.complexValue.length, phase:Angle(degrees: $0).radians)
viewModel.setValueRecordingTrace(from: Angle(radians: self.complexValue.phase).degrees, to: $0) {
intermediateValue in
self.complexValue = Complex.init(length: self.complexValue.length, phase:Angle(degrees: intermediateValue).radians)
}
}
), unit: angleUnit, label: angleLabel, description: angleDescription, showNegationDecorator: true)
}
Expand All @@ -36,6 +43,7 @@ struct PolarImpedanceView: View {

var body: some View {
PolarParameterView<ResistanceUnit>(
viewModel: viewModel,
complexValue: $viewModel.impedance,
magnitudeUnit: .Ω,
angleUnit: .degree,
Expand All @@ -52,6 +60,7 @@ struct PolarAdmittanceView: View {

var body: some View {
PolarParameterView<ConductanceUnit>(
viewModel: viewModel,
complexValue: $viewModel.admittance,
magnitudeUnit: .S,
angleUnit: .degree,
Expand All @@ -68,6 +77,7 @@ struct PolarReflectionCoefficientView: View {

var body: some View {
PolarParameterView<ReflectionCoefficientUnit>(
viewModel: viewModel,
complexValue: $viewModel.reflectionCoefficient,
magnitudeUnit: .Γ,
angleUnit: .degree,
Expand All @@ -80,6 +90,7 @@ struct PolarReflectionCoefficientView: View {
}

struct RectangularParameterView<UnitType: UnitWithPowerOfTen>: View {
let viewModel: ViewModel
@Binding var complexValue: Complex<Double>
var realPartUnit: UnitType
var imaginaryPartUnit: UnitType
Expand All @@ -95,12 +106,24 @@ struct RectangularParameterView<UnitType: UnitWithPowerOfTen>: View {
HStack {
UnitInputView(value: Binding(
get: { self.complexValue.canonicalizedReal },
set: { self.complexValue = Complex($0, self.complexValue.canonicalizedImaginary) }
set: {
let imaginary = self.complexValue.canonicalizedImaginary
viewModel.setValueRecordingTrace(from: self.complexValue.canonicalizedReal, to: $0) {
intermediateValue in
self.complexValue = Complex(intermediateValue, imaginary)
}
}
), unit: realPartUnit, label: realPartLabel, description: realPartDescription,
showNegationDecorator: realCanBeNegative)
UnitInputView(value: Binding(
get: { self.complexValue.canonicalizedImaginary },
set: { self.complexValue = Complex(self.complexValue.canonicalizedReal, $0) }
set: {
let real = self.complexValue.canonicalizedReal
viewModel.setValueRecordingTrace(from: self.complexValue.canonicalizedImaginary, to: $0) {
intermediateValue in
self.complexValue = Complex(real, intermediateValue)
}
}
), unit: imaginaryPartUnit, label: imaginaryPartLabel, description: imaginaryPartDescription,
showNegationDecorator: imaginaryCanBeNegative)
}
Expand All @@ -113,6 +136,7 @@ struct RectangularImpedanceView: View {

var body: some View {
RectangularParameterView<ResistanceUnit>(
viewModel: viewModel,
complexValue: $viewModel.impedance,
realPartUnit: .Ω,
imaginaryPartUnit: .Ω,
Expand All @@ -131,6 +155,7 @@ struct RectangularAdmittanceView: View {

var body: some View {
RectangularParameterView<ConductanceUnit>(
viewModel: viewModel,
complexValue: $viewModel.admittance,
realPartUnit: .S,
imaginaryPartUnit: .S,
Expand All @@ -149,6 +174,7 @@ struct RectangularReflectionCoefficientView: View {

var body: some View {
RectangularParameterView<ReflectionCoefficientUnit>(
viewModel: viewModel,
complexValue: $viewModel.reflectionCoefficient,
realPartUnit: .Γ,
imaginaryPartUnit: .Γ,
Expand Down
7 changes: 6 additions & 1 deletion Impedance Converter/Impedance Converter/ParametersView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ struct ReferenceView: View {
if (viewModel.displayMode != .admittance) {
UnitInputView(value: Binding(
get: { viewModel.referenceImpedance.real },
set: { viewModel.referenceImpedance = Complex($0, 0)}
set: {
viewModel.setValueRecordingTrace(from: viewModel.referenceImpedance.real, to: $0) {
intermediateValue in
viewModel.referenceImpedance = Complex(intermediateValue, 0)
}
}
), unit: ResistanceUnit.Ω, label: "Z₀", description: "ref. impedance")
} else {
UnitInputView(value: Binding(
Expand Down
22 changes: 22 additions & 0 deletions Impedance Converter/Impedance Converter/Settings.bundle/Root.plist
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@
<string>Root</string>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>Type</key>
<string>PSMultiValueSpecifier</string>
<key>Title</key>
<string>Trace Persistence</string>
<key>Key</key>
<string>tracePersistence</string>
<key>DefaultValue</key>
<string>Normal</string>
<key>Titles</key>
<array>
<string>Normal</string>
<string>Long</string>
<string>Infinite</string>
</array>
<key>Values</key>
<array>
<string>Normal</string>
<string>Long</string>
<string>Infinite</string>
</array>
</dict>
<dict>
<key>Type</key>
<string>PSMultiValueSpecifier</string>
Expand Down
134 changes: 122 additions & 12 deletions Impedance Converter/Impedance Converter/SmithChartView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ extension Double {
}
}

struct ConstraintValues {
let resistance: Double
let reactance: Double
let conductance: Double
let susceptance: Double
let length: Double
let phase: Double
}

struct SmithChartContentView: View {

@AppStorage("scale") private var scalePreference = "Simple"
Expand Down Expand Up @@ -176,6 +185,8 @@ struct SmithChartContentView: View {
refAngleInterpolator = Angle(radians: start + shortestDifference * interpolatorValue)
}
}
.blur(radius: 0.5)
.brightness(0.1)

Canvas { context, size in

Expand Down Expand Up @@ -206,6 +217,8 @@ struct SmithChartContentView: View {
.onChange(of: viewModel.displayMode) { _ in
startAnimatingModeChange(target:SmithChartContentView.animationTarget(for: viewModel.displayMode))
}
.blur(radius: 0.5)
.brightness(0.1)

Canvas { context, size in

Expand All @@ -232,6 +245,8 @@ struct SmithChartContentView: View {
}

}
.blur(radius: 0.5)
.brightness(0.1)

Canvas { context, size in

Expand All @@ -255,6 +270,62 @@ struct SmithChartContentView: View {
.position(transformedPoint)
.blur(radius: 1.5*dotRadius)

Canvas { context, size in

let (center, radius) = createCenterAndRadius(size: size)

// Creating a path for the trace
let trace = viewModel.trace
var tracePath = Path()
if let firstPoint = trace.first {
let firstCGPoint = CGPoint(
x: firstPoint.real,
y: firstPoint.imaginary
)
let transformedFirstPoint = transformPoint(center: center, radius: radius, point: firstCGPoint)
tracePath.move(to: transformedFirstPoint)
}

for complexPoint in trace.dropFirst() {
let cgPoint = CGPoint(
x: complexPoint.real,
y: complexPoint.imaginary
)
let transformedPoint = transformPoint(center: center, radius: radius, point: cgPoint)
tracePath.addLine(to: transformedPoint)
}

context.stroke(tracePath, with: .color(Color.basePrimaryOrange.adjusted(brightness: 0.5)), lineWidth: 4)
}.blur(radius: 10)

Canvas { context, size in

let (center, radius) = createCenterAndRadius(size: size)

// Creating a path for the trace
let trace = viewModel.trace
var tracePath = Path()
if let firstPoint = trace.first {
let firstCGPoint = CGPoint(
x: firstPoint.real,
y: firstPoint.imaginary
)
let transformedFirstPoint = transformPoint(center: center, radius: radius, point: firstCGPoint)
tracePath.move(to: transformedFirstPoint)
}

for complexPoint in trace.dropFirst() {
let cgPoint = CGPoint(
x: complexPoint.real,
y: complexPoint.imaginary
)
let transformedPoint = transformPoint(center: center, radius: radius, point: cgPoint)
tracePath.addLine(to: transformedPoint)
}

context.stroke(tracePath, with: .color(Color.basePrimaryOrange.adjusted(brightness: 0.5)), lineWidth: 1)
}.blur(radius: 1)

Color.clear
.contentShape(Circle())
.gesture(
Expand Down Expand Up @@ -441,6 +512,8 @@ struct SmithChartContentView: View {
)
}

@State private var capturedConstraintValues: ConstraintValues?

private func handleDrag(at location: CGPoint, in size: CGSize) {
viewModel.isUndoCheckpointEnabled = false
let center = CGPoint(x: size.width / 2, y: size.height / 2)
Expand All @@ -453,13 +526,17 @@ struct SmithChartContentView: View {
)

var reflectionCoefficient = Complex(tapPoint.x, -tapPoint.y)

let resistance = viewModel.resistance
let reactance = viewModel.reactance
let conductance = viewModel.conductance
let susceptance = viewModel.susceptance
let length = viewModel.reflectionCoefficient.length
let phase = viewModel.reflectionCoefficient.phase

if (viewModel.traceRecordingEnabled) {
capturedConstraintValues = ConstraintValues(
resistance: viewModel.resistance,
reactance: viewModel.reactance,
conductance: viewModel.conductance,
susceptance: viewModel.susceptance,
length: viewModel.reflectionCoefficient.length,
phase: viewModel.reflectionCoefficient.phase
)
}

if (constraintKind != .unset && constraintKind != .none) {
if (reflectionCoefficient - viewModel.reflectionCoefficient).length > 0.2 {
Expand All @@ -470,11 +547,43 @@ struct SmithChartContentView: View {

if (reflectionCoefficient.length > 1) {
reflectionCoefficient = Complex.init(length: 1, phase: reflectionCoefficient.phase)
viewModel.reflectionCoefficient = reflectionCoefficient
} else {
viewModel.reflectionCoefficient = reflectionCoefficient
}

switch viewModel.displayMode {
case .impedance:
viewModel.impedance = viewModel.referenceImpedance * (Complex.one + reflectionCoefficient) / (Complex.one - reflectionCoefficient)
case .admittance:
viewModel.admittance = viewModel.referenceAdmittance * (Complex.one - reflectionCoefficient) / (Complex.one + reflectionCoefficient)
case .reflectionCoefficient:
viewModel.setValueRecordingTrace(
from: viewModel.reflectionCoefficient,
to: reflectionCoefficient,
operation: { intermediateValue in
viewModel.reflectionCoefficient = intermediateValue
},
interpolationMethod: { (oldValue, newValue, fraction) in
return oldValue.polarInterpolated(to: newValue , fraction: fraction)
}
)
}

if (viewModel.traceRecordingEnabled) {
// Now that we have captured first drag transition, turn off trace recording
viewModel.traceRecordingEnabled = false
// Only on subsequent drag calls will we allow constraints to kick in return early
return
}

// We are now dragging, so clear any trace now
viewModel.startAnimatingTrace(delay: 0)

let resistance = capturedConstraintValues!.resistance
let reactance = capturedConstraintValues!.reactance
let conductance = capturedConstraintValues!.conductance
let susceptance = capturedConstraintValues!.susceptance
let length = capturedConstraintValues!.length
let phase = capturedConstraintValues!.phase

switch constraintKind {
case .unset:
switch viewModel.displayMode {
Expand Down Expand Up @@ -544,6 +653,7 @@ struct SmithChartContentView: View {

private func handleDragEnd() {
constraintKind = .unset
viewModel.traceRecordingEnabled = true
viewModel.isUndoCheckpointEnabled = true
viewModel.addCheckpoint()
}
Expand Down Expand Up @@ -591,8 +701,8 @@ struct SmithChartView: View {
.allowsHitTesting(false)

// First radial gradient for central brightness
RadialGradient(gradient: Gradient(colors: [Color.white.opacity(0.8), Color.white.opacity(0)]),
center: .center, startRadius: 10, endRadius: 200)
RadialGradient(gradient: Gradient(colors: [Color.white.opacity(0.8), Color.white.opacity(0.0)]),
center: .center, startRadius: 10, endRadius: 250)
.blendMode(.overlay)
.allowsHitTesting(false)

Expand Down
Loading

0 comments on commit 0d9e430

Please # to comment.