stroke start equalization and filtering
This commit is contained in:
@@ -59,6 +59,10 @@ typedef NS_ENUM(NSInteger, PencilHoverMode) {
|
||||
@property (nonatomic, assign) int controllerGyroSwitchHold;
|
||||
@property (nonatomic, assign) int controllerGyroSwitchToggle;
|
||||
@property NSArray<NSNumber *> *pressureCurvePoints;
|
||||
// @property NSArray<NSNumber *> *initialTouchPressureCurvePoints;
|
||||
@property (nonatomic, assign) int phase1StrokeSampleIndexEnd;
|
||||
@property (nonatomic, assign) int phase2StrokeSampleIndexEnd;
|
||||
@property (nonatomic, assign) CGFloat strokeEqualizationStrength;
|
||||
@property (nonatomic, assign) bool pressureCurveEnabled;
|
||||
@property (nonatomic, assign) bool doubleTapShorcutEnabled;
|
||||
@property NSString *brushShortcut;
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
[encoder encodeInt:self.controllerGyroSwitchHold forKey:@"controllerGyroSwitchHold"];
|
||||
[encoder encodeInt:self.controllerGyroSwitchToggle forKey:@"controllerGyroSwitchToggle"];
|
||||
[encoder encodeObject:self.pressureCurvePoints forKey:@"pressureCurvePoints"];
|
||||
// [encoder encodeObject:self.initialTouchPressureCurvePoints forKey:@"initialTouchPressureCurvePoints"];
|
||||
[encoder encodeInt:self.phase1StrokeSampleIndexEnd forKey:@"phase1StrokeSampleIndexEnd"];
|
||||
[encoder encodeInt:self.phase2StrokeSampleIndexEnd forKey:@"phase2StrokeSampleIndexEnd"];
|
||||
[encoder encodeDouble:self.strokeEqualizationStrength forKey:@"strokeEqualizationStrength"];
|
||||
[encoder encodeBool:self.pressureCurveEnabled forKey:@"pressureCurveEnabled"];
|
||||
[encoder encodeBool:self.doubleTapShorcutEnabled forKey:@"doubleTapShorcutEnabled"];
|
||||
[encoder encodeObject:self.brushShortcut forKey:@"brushShortcut"];
|
||||
@@ -108,6 +112,7 @@
|
||||
self.reverseGyroHoldButton = [decoder containsValueForKey:@"reverseGyroHoldButton"] ? [decoder decodeBoolForKey:@"reverseGyroHoldButton"] : false;
|
||||
self.controllerGyroSwitchHold = [decoder containsValueForKey:@"controllerGyroSwitchHold"] ? [decoder decodeIntForKey:@"controllerGyroSwitchHold"] : ControllerButtonNull;
|
||||
self.controllerGyroSwitchToggle = [decoder containsValueForKey:@"controllerGyroSwitchToggle"] ? [decoder decodeIntForKey:@"controllerGyroSwitchToggle"] : ControllerButtonNull;
|
||||
|
||||
self.pressureCurvePoints =
|
||||
[decoder containsValueForKey:@"pressureCurvePoints"]
|
||||
? [decoder decodeObjectOfClasses:
|
||||
@@ -117,6 +122,21 @@
|
||||
nil]
|
||||
forKey:@"pressureCurvePoints"]
|
||||
: @[@0.0, @0.0, @1.0, @1.0];
|
||||
|
||||
/* self.initialTouchPressureCurvePoints =
|
||||
[decoder containsValueForKey:@"initialTouchPressureCurvePoints"]
|
||||
? [decoder decodeObjectOfClasses:
|
||||
[NSSet setWithObjects:
|
||||
[NSArray class],
|
||||
[NSNumber class],
|
||||
nil]
|
||||
forKey:@"initialTouchPressureCurvePoints"]
|
||||
: @[@0.0, @0.0, @1.0, @1.0]; */
|
||||
|
||||
self.phase1StrokeSampleIndexEnd = [decoder containsValueForKey:@"phase1StrokeSampleIndexEnd"] ? [decoder decodeIntForKey:@"phase1StrokeSampleIndexEnd"] : 10;
|
||||
self.phase2StrokeSampleIndexEnd = [decoder containsValueForKey:@"phase2StrokeSampleIndexEnd"] ? [decoder decodeIntForKey:@"phase2StrokeSampleIndexEnd"] : 24;
|
||||
self.strokeEqualizationStrength = [decoder containsValueForKey:@"strokeEqualizationStrength"] ? [decoder decodeFloatForKey:@"strokeEqualizationStrength"] : 1.0;
|
||||
|
||||
self.pressureCurveEnabled = [decoder containsValueForKey:@"pressureCurveEnabled"] ? [decoder decodeBoolForKey:@"pressureCurveEnabled"] : false;
|
||||
|
||||
self.doubleTapShorcutEnabled = [decoder containsValueForKey:@"doubleTapShorcutEnabled"] ? [decoder decodeBoolForKey:@"doubleTapShorcutEnabled"] : false;
|
||||
@@ -190,6 +210,10 @@
|
||||
copy.controllerGyroSwitchHold = self.controllerGyroSwitchHold;
|
||||
copy.controllerGyroSwitchToggle = self.controllerGyroSwitchToggle;
|
||||
copy.pressureCurvePoints = [[NSMutableArray alloc] initWithArray:self.pressureCurvePoints copyItems:YES];
|
||||
// copy.initialTouchPressureCurvePoints = [[NSMutableArray alloc] initWithArray:self.initialTouchPressureCurvePoints copyItems:YES];
|
||||
copy.phase1StrokeSampleIndexEnd = self.phase1StrokeSampleIndexEnd;
|
||||
copy.phase2StrokeSampleIndexEnd = self.phase2StrokeSampleIndexEnd;
|
||||
copy.strokeEqualizationStrength = self.strokeEqualizationStrength;
|
||||
copy.pressureCurveEnabled = self.pressureCurveEnabled;
|
||||
copy.doubleTapShorcutEnabled = self.doubleTapShorcutEnabled;
|
||||
copy.brushShortcut = [self.brushShortcut mutableCopy]; // NSString → NSMutableString
|
||||
|
||||
@@ -27,7 +27,7 @@ import UIKit
|
||||
@objc static private(set) var hoverMode: PencilHoverMode = .HoverPencil
|
||||
private(set) var pencilProEnabled: Bool = false
|
||||
private var isFirstMove: Bool = false
|
||||
private var moveEventIndex: Int64 = 0
|
||||
private var strokeSampleIndex: Int32 = 0
|
||||
private var initialMoveEventIndexLimit: Int64
|
||||
private var touchBeganForce: Float = 0
|
||||
@objc static private(set) var isDrawing: Bool = false
|
||||
@@ -36,7 +36,27 @@ import UIKit
|
||||
// static private let oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
|
||||
static private var selectedProfile:OSCProfile?
|
||||
|
||||
private var pressureLUT = PressureCurveLUT(curve: PressureCurve())
|
||||
private var strokeLUT = PressureCurveLUT(curve: PressureCurve())
|
||||
// private var initialTouchLUT = PressureCurveLUT(curve: PressureCurve())
|
||||
private var phase1StrokeSampleIndexEnd:Int32 = 0
|
||||
private var phase2StrokeSampleIndexEnd:Int32 = 0
|
||||
private var phase2EqualizationStrength:Float = 0
|
||||
|
||||
enum StrokePhase: UInt8, CaseIterable {
|
||||
case phase1
|
||||
case phase2
|
||||
case phase3
|
||||
}
|
||||
|
||||
private func getStrokePhase(sampleIndex:Int32) -> StrokePhase {
|
||||
if sampleIndex > phase2StrokeSampleIndexEnd {
|
||||
return .phase3
|
||||
}
|
||||
if sampleIndex > phase1StrokeSampleIndexEnd {
|
||||
return .phase2
|
||||
}
|
||||
return .phase1
|
||||
}
|
||||
|
||||
@objc init(streamView: UIView, settings: TemporarySettings) {
|
||||
self.streamView = streamView
|
||||
@@ -96,12 +116,23 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
let persistedCurvePoints = PressureCurve.importCurvePoints(selectedProfile.pressureCurvePoints)
|
||||
let curve = PressureCurve()
|
||||
curve.polylinePoints = persistedCurvePoints
|
||||
curve.buildCurveSegments()
|
||||
let strokePressureCurvePoints = PressureCurve.importCurvePoints(selectedProfile.pressureCurvePoints)
|
||||
let strokePressureCurve = PressureCurve()
|
||||
strokePressureCurve.polylinePoints = strokePressureCurvePoints
|
||||
strokePressureCurve.buildCurveSegments()
|
||||
|
||||
// let initialTouchPressureCurvePoints = PressureCurve.importCurvePoints(selectedProfile.initialTouchPressureCurvePoints)
|
||||
// let initialTouchPressureCurve = PressureCurve()
|
||||
// initialTouchPressureCurve.polylinePoints = initialTouchPressureCurvePoints
|
||||
// initialTouchPressureCurve.buildCurveSegments()
|
||||
|
||||
phase1StrokeSampleIndexEnd = selectedProfile.phase1StrokeSampleIndexEnd
|
||||
phase2StrokeSampleIndexEnd = selectedProfile.phase2StrokeSampleIndexEnd
|
||||
phase2EqualizationStrength = Float(selectedProfile.strokeEqualizationStrength)
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
||||
self.pressureLUT = PressureCurveLUT(curve: curve)
|
||||
self.strokeLUT = PressureCurveLUT(curve: strokePressureCurve)
|
||||
// self.initialTouchLUT = PressureCurveLUT(curve: initialTouchPressureCurve)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,9 +140,11 @@ import UIKit
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
guard let event = event else { return }
|
||||
previousForce = 0
|
||||
previousTargetForce = 0
|
||||
PencilHandler.isDrawing = true
|
||||
// isFirstMove = true
|
||||
moveEventIndex = 0
|
||||
strokeSampleIndex = 0
|
||||
for touch in touches {
|
||||
let coalesced = event.coalescedTouches(for: touch) ?? []
|
||||
_ = self.sendStylusEvent(touchBatch: pencilTickEnabled ? coalesced : [touch])
|
||||
@@ -124,7 +157,6 @@ import UIKit
|
||||
let coalesced = event.coalescedTouches(for: touch) ?? []
|
||||
_ = self.sendStylusEvent(touchBatch: pencilTickEnabled ? coalesced : [touch])
|
||||
}
|
||||
moveEventIndex += 1
|
||||
}
|
||||
|
||||
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
@@ -194,6 +226,9 @@ import UIKit
|
||||
return UInt8(90 - min(90, Int(altitudeDegs)))
|
||||
}
|
||||
|
||||
var previousForce: Float = 0
|
||||
var previousTargetForce: Float = 0
|
||||
var phase2IndexCount: Int32 = 0
|
||||
private func sendStylusEvent(touchBatch: [UITouch], with event: UIEvent? = nil) -> DispatchTime {
|
||||
guard let streamView = streamView, touchBatch.count>0 else { return .now() }
|
||||
|
||||
@@ -201,6 +236,8 @@ import UIKit
|
||||
var previousTimeStamp: TimeInterval = 0
|
||||
var delay:TimeInterval = 0
|
||||
var dispatchMoment: DispatchTime = .now()
|
||||
|
||||
phase2IndexCount = phase2StrokeSampleIndexEnd - phase1StrokeSampleIndexEnd
|
||||
|
||||
for touch in touchBatch {
|
||||
let point = touch.preciseLocation(in: streamView)
|
||||
@@ -209,22 +246,50 @@ import UIKit
|
||||
let location = self.adjustCoordinatesForVideoArea(point: point)
|
||||
let videoSize = self.getVideoAreaSize()
|
||||
let normalizedLocation = CGPoint(x: location.x/videoSize.width, y: location.y/videoSize.height)
|
||||
let force = Float(touch.force/touch.maximumPossibleForce)/sin(Float(altitude))
|
||||
var targetForce = pressureCurveEnabled ? self.pressureLUT.value(at: force) : force
|
||||
var force = Float(touch.force/touch.maximumPossibleForce)/sin(Float(altitude))
|
||||
force = (self.pencilTickEnabled && force == 0) ? previousForce : force
|
||||
|
||||
self.strokeSampleIndex += 1
|
||||
|
||||
var targetForce:Float
|
||||
var equalizationStep:Float = 0
|
||||
var equalizedForce:Float = 0
|
||||
if(phase2IndexCount>0){
|
||||
equalizationStep = (phase2EqualizationStrength-1)/Float(phase2IndexCount-1)
|
||||
}
|
||||
|
||||
|
||||
let strokePhase = getStrokePhase(sampleIndex: self.strokeSampleIndex)
|
||||
switch strokePhase {
|
||||
case .phase1:
|
||||
targetForce = pencilTickEnabled ? 0 : self.strokeLUT.value(at: force)
|
||||
case .phase2:
|
||||
targetForce = self.strokeLUT.value(at: force)
|
||||
targetForce = max(targetForce,previousTargetForce)
|
||||
equalizedForce = targetForce*(phase2EqualizationStrength - equalizationStep*Float(strokeSampleIndex-phase1StrokeSampleIndexEnd-1))
|
||||
case .phase3:
|
||||
targetForce = self.strokeLUT.value(at: force)
|
||||
}
|
||||
targetForce = self.pressureCurveEnabled ? targetForce : force
|
||||
|
||||
previousForce = force
|
||||
previousTargetForce = targetForce
|
||||
|
||||
let eventType:UInt8
|
||||
|
||||
switch touch.phase {
|
||||
case .began:
|
||||
eventType = UInt8(manualHoverFlag ? LI_TOUCH_EVENT_HOVER : LI_TOUCH_EVENT_DOWN)
|
||||
touchBeganForce = targetForce
|
||||
case .moved:
|
||||
eventType = UInt8(manualHoverFlag ? LI_TOUCH_EVENT_HOVER : LI_TOUCH_EVENT_MOVE)
|
||||
targetForce = moveEventIndex < initialMoveEventIndexLimit ? touchBeganForce : targetForce
|
||||
case .ended:
|
||||
eventType = UInt8(manualHoverFlag ? LI_TOUCH_EVENT_HOVER_LEAVE : LI_TOUCH_EVENT_UP)
|
||||
previousForce = 0
|
||||
previousTargetForce = 0
|
||||
case .cancelled:
|
||||
eventType = UInt8(manualHoverFlag ? LI_TOUCH_EVENT_HOVER_LEAVE : LI_TOUCH_EVENT_UP)
|
||||
previousForce = 0
|
||||
previousTargetForce = 0
|
||||
default:
|
||||
eventType = UInt8(LI_TOUCH_EVENT_HOVER)
|
||||
}
|
||||
@@ -241,16 +306,19 @@ import UIKit
|
||||
tickMoment += (manualTick ? tickInterval : touch.timestamp - previousTimeStamp)
|
||||
}
|
||||
|
||||
let sendableForce = targetForce
|
||||
let sendableForce = strokePhase == .phase2 ? equalizedForce : targetForce
|
||||
|
||||
DispatchQueue.global().asyncAfter(deadline: dispatchMoment + tickMoment + delay) {
|
||||
|
||||
/*
|
||||
if PencilHandler.autoHoverEnabled, eventType == UInt8(LI_TOUCH_EVENT_DOWN) {
|
||||
LiSendPenEvent(UInt8(LI_TOUCH_EVENT_HOVER_LEAVE), UInt8(LI_TOOL_TYPE_PEN), 0, Float(normalizedLocation.x), Float(normalizedLocation.y), 0, 0, 0, self.getRotation(fromAzimuthAngle: Float(azimuth)), self.getTilt(fromAltitudeAngle: Float(altitude)))
|
||||
}
|
||||
*/
|
||||
|
||||
LiSendPenEvent(eventType, UInt8(LI_TOOL_TYPE_PEN), 0, Float(normalizedLocation.x), Float(normalizedLocation.y), sendableForce, 0, 0, self.getRotation(fromAzimuthAngle: Float(azimuth)), self.getTilt(fromAltitudeAngle: Float(altitude)))
|
||||
if strokePhase != .phase1 || !self.pencilTickEnabled {LiSendPenEvent(eventType, UInt8(LI_TOOL_TYPE_PEN), 0, Float(normalizedLocation.x), Float(normalizedLocation.y), sendableForce, 0, 0, self.getRotation(fromAzimuthAngle: Float(azimuth)), self.getTilt(fromAltitudeAngle: Float(altitude)))}
|
||||
else {
|
||||
LiSendPenEvent(UInt8(LI_TOUCH_EVENT_HOVER), UInt8(LI_TOOL_TYPE_PEN), 0, Float(normalizedLocation.x), Float(normalizedLocation.y), 0, 0, 0, self.getRotation(fromAzimuthAngle: Float(azimuth)), self.getTilt(fromAltitudeAngle: Float(altitude)))
|
||||
}
|
||||
|
||||
if eventType == UInt8(LI_TOUCH_EVENT_UP) {
|
||||
PencilHandler.isDrawing = false
|
||||
|
||||
@@ -476,16 +476,43 @@ extension UIView {
|
||||
}
|
||||
|
||||
// MARK: - ViewController
|
||||
class PressureCurveViewController: UIViewController {
|
||||
class PressureCurveViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
|
||||
private var navBar = UINavigationBar()
|
||||
private var saveButton: UIBarButtonItem = UIBarButtonItem()
|
||||
|
||||
private var container = UIView()
|
||||
private var curveView = PressureCurveView()
|
||||
// private var initialTouchCurvePoints: [CGPoint] = []
|
||||
private var strokePressureCurvePoints: [CGPoint] = []
|
||||
|
||||
private var phase1StrokeSampleIndexEnd: Int32 = 0
|
||||
private var phase2StrokeSampleIndexEnd: Int32 = 0
|
||||
|
||||
private var minPressure: CGFloat = 0
|
||||
private var maxPressure: CGFloat = 1
|
||||
private var isFirstLaunch: Bool = GenericUtils.isFirstLaunchPressureCurveTool()
|
||||
|
||||
// private var curvePhaseButton: UIBarButtonItem = UIBarButtonItem()
|
||||
private let hookFilterLabel = UILabel()
|
||||
private let hookFilterSlider = UISlider()
|
||||
private let strokeEqualizationDepthLabel = UILabel()
|
||||
private let strokeEqualizationDepthSlider = UISlider()
|
||||
private let strokeEqualizationStrengthLabel = UILabel()
|
||||
private let strokeEqualizationStrengthSlider = UISlider()
|
||||
|
||||
enum PressureCurvePhase: UInt8, CaseIterable {
|
||||
case initialTouch
|
||||
case stroke
|
||||
func next() -> PressureCurvePhase {
|
||||
let all = Self.allCases
|
||||
let index = all.firstIndex(of: self)!
|
||||
let nextIndex = (index + 1) % all.count
|
||||
return all[nextIndex]
|
||||
}
|
||||
}
|
||||
|
||||
private(set) var curvePhase: PressureCurvePhase = .initialTouch
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
@@ -496,34 +523,39 @@ class PressureCurveViewController: UIViewController {
|
||||
let curveWidth: CGFloat = 530
|
||||
let curveHeight: CGFloat = 530
|
||||
curveView = PressureCurveView(frame: CGRect(x: 0, y: 0, width: curveWidth, height: curveHeight))
|
||||
curveView.center = self.view.center
|
||||
curveView.center = CGPoint(x: self.view.center.x, y: self.view.center.y)
|
||||
curveView.backgroundColor = .white
|
||||
curveView.layer.cornerRadius = 0
|
||||
curveView.layer.masksToBounds = true
|
||||
|
||||
container = UIView(frame: CGRect(x: 0, y: 0, width: curveWidth+10, height: curveHeight+50))
|
||||
container.center = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.midY-20)
|
||||
container = UIView(frame: CGRect(x: 0, y: 0, width: curveWidth+10, height: curveHeight+148))
|
||||
container.center = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.midY+3)
|
||||
container.backgroundColor = .white
|
||||
container.layer.cornerRadius = 12
|
||||
container.layer.masksToBounds = true
|
||||
container.layer.borderWidth = 2
|
||||
container.layer.borderColor = ThemeManager.separatorColor.cgColor
|
||||
container.layer.borderWidth = 0.5
|
||||
container.layer.borderColor = UIColor.black.withAlphaComponent(0.15).cgColor
|
||||
container.addSubview(curveView)
|
||||
|
||||
self.setupNavigationBar()
|
||||
self.setupBottomSliders()
|
||||
self.loadPersistedCurve()
|
||||
self.curveView.showGraph = true
|
||||
|
||||
self.setupNavigationBar()
|
||||
|
||||
|
||||
self.view.addSubview(container)
|
||||
|
||||
curveView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
curveView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
|
||||
curveView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
|
||||
curveView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor,constant: -25),
|
||||
curveView.widthAnchor.constraint(equalToConstant: curveWidth),
|
||||
curveView.heightAnchor.constraint(equalToConstant: curveHeight)
|
||||
])
|
||||
|
||||
self.displayCurve()
|
||||
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(pencilProPurchaseAborted(_:)),
|
||||
@@ -536,6 +568,10 @@ class PressureCurveViewController: UIViewController {
|
||||
name: AddOnProduct.PencilProPack.purchaseSucceededNotification(),
|
||||
object: nil
|
||||
)
|
||||
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissSelf))
|
||||
tap.delegate = self
|
||||
view.addGestureRecognizer(tap)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
@@ -573,11 +609,123 @@ class PressureCurveViewController: UIViewController {
|
||||
|
||||
private func loadPersistedCurve() {
|
||||
let oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
|
||||
let persistedCurvePoints = PressureCurve.importCurvePoints(oscProfileMan.getSelectedProfile().pressureCurvePoints)
|
||||
curveView.curve.polylinePoints = persistedCurvePoints
|
||||
let oscProfile = oscProfileMan.getSelectedProfile()
|
||||
// initialTouchCurvePoints = PressureCurve.importCurvePoints(oscProfile.initialTouchPressureCurvePoints)
|
||||
strokePressureCurvePoints = PressureCurve.importCurvePoints(oscProfile.pressureCurvePoints)
|
||||
displayCurve()
|
||||
|
||||
hookFilterSlider.value = Float(oscProfile.phase1StrokeSampleIndexEnd)
|
||||
strokeEqualizationDepthSlider.value = Float(oscProfile.phase2StrokeSampleIndexEnd)
|
||||
strokeEqualizationStrengthSlider.value = Float(oscProfile.strokeEqualizationStrength)
|
||||
|
||||
strokeEqualizationDepthSliderMoved(strokeEqualizationDepthSlider)
|
||||
hookFilterDepthSliderMoved(hookFilterSlider)
|
||||
strokeEqualizationStrengthSliderMoved(strokeEqualizationStrengthSlider)
|
||||
}
|
||||
|
||||
private func displayCurve() {
|
||||
switch curvePhase {
|
||||
case .initialTouch:
|
||||
break
|
||||
// curveView.curve.polylinePoints = initialTouchCurvePoints
|
||||
case .stroke:
|
||||
curveView.curve.polylinePoints = strokePressureCurvePoints
|
||||
}
|
||||
curveView.setNeedsDisplay()
|
||||
}
|
||||
|
||||
private func setupBottomSliders() {
|
||||
|
||||
hookFilterLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
hookFilterSlider.translatesAutoresizingMaskIntoConstraints = false
|
||||
strokeEqualizationDepthLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
strokeEqualizationDepthSlider.translatesAutoresizingMaskIntoConstraints = false
|
||||
strokeEqualizationStrengthLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
strokeEqualizationStrengthSlider.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let traits = UITraitCollection(userInterfaceStyle: .light)
|
||||
|
||||
hookFilterLabel.font = UIFont.systemFont(ofSize: 14)
|
||||
hookFilterLabel.textColor = .black
|
||||
if GenericUtils.liquidGlassEnabled {
|
||||
if #available(iOS 13.0, *) {hookFilterSlider.maximumTrackTintColor = .tertiarySystemFill.resolvedColor(with: traits)}
|
||||
}
|
||||
hookFilterSlider.minimumValue = 1
|
||||
hookFilterSlider.maximumValue = 20
|
||||
hookFilterSlider.addTarget(self, action: #selector(hookFilterDepthSliderMoved(_:)), for: .valueChanged)
|
||||
|
||||
strokeEqualizationDepthLabel.font = UIFont.systemFont(ofSize: 14)
|
||||
strokeEqualizationDepthLabel.textColor = .black
|
||||
if GenericUtils.liquidGlassEnabled {
|
||||
if #available(iOS 13.0, *) {strokeEqualizationDepthSlider.maximumTrackTintColor = .tertiarySystemFill.resolvedColor(with: traits)}
|
||||
}
|
||||
strokeEqualizationDepthSlider.minimumValue = 1
|
||||
strokeEqualizationDepthSlider.maximumValue = 80
|
||||
strokeEqualizationDepthSlider.addTarget(self, action: #selector(strokeEqualizationDepthSliderMoved(_:)), for: .valueChanged)
|
||||
|
||||
strokeEqualizationStrengthLabel.font = UIFont.systemFont(ofSize: 14)
|
||||
strokeEqualizationStrengthLabel.textColor = .black
|
||||
if GenericUtils.liquidGlassEnabled {
|
||||
if #available(iOS 13.0, *) {strokeEqualizationStrengthSlider.maximumTrackTintColor = .tertiarySystemFill.resolvedColor(with: traits)}
|
||||
}
|
||||
strokeEqualizationStrengthSlider.minimumValue = 0.1
|
||||
strokeEqualizationStrengthSlider.maximumValue = 3.5
|
||||
strokeEqualizationStrengthSlider.addTarget(self, action: #selector(strokeEqualizationStrengthSliderMoved(_:)), for: .valueChanged)
|
||||
|
||||
container.addSubview(hookFilterLabel)
|
||||
container.addSubview(hookFilterSlider)
|
||||
container.addSubview(strokeEqualizationDepthLabel)
|
||||
container.addSubview(strokeEqualizationDepthSlider)
|
||||
container.addSubview(strokeEqualizationStrengthLabel)
|
||||
container.addSubview(strokeEqualizationStrengthSlider)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
|
||||
// Slider
|
||||
hookFilterLabel.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16),
|
||||
hookFilterLabel.widthAnchor.constraint(equalToConstant: 260),
|
||||
hookFilterSlider.leadingAnchor.constraint(equalTo: hookFilterLabel.trailingAnchor, constant: 12),
|
||||
hookFilterSlider.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -16),
|
||||
hookFilterSlider.topAnchor.constraint(equalTo: curveView.bottomAnchor, constant: 0),
|
||||
hookFilterLabel.centerYAnchor.constraint(equalTo: hookFilterSlider.centerYAnchor, constant:0),
|
||||
|
||||
strokeEqualizationDepthLabel.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16),
|
||||
strokeEqualizationDepthLabel.widthAnchor.constraint(equalToConstant: 260),
|
||||
strokeEqualizationDepthSlider.leadingAnchor.constraint(equalTo: strokeEqualizationDepthLabel.trailingAnchor, constant: 12),
|
||||
strokeEqualizationDepthSlider.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -16),
|
||||
strokeEqualizationDepthSlider.topAnchor.constraint(equalTo: hookFilterSlider.bottomAnchor, constant: 0),
|
||||
strokeEqualizationDepthLabel.centerYAnchor.constraint(equalTo: strokeEqualizationDepthSlider.centerYAnchor, constant:0),
|
||||
|
||||
strokeEqualizationStrengthLabel.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16),
|
||||
strokeEqualizationStrengthLabel.widthAnchor.constraint(equalToConstant: 260),
|
||||
strokeEqualizationStrengthSlider.leadingAnchor.constraint(equalTo: strokeEqualizationStrengthLabel.trailingAnchor, constant: 12),
|
||||
strokeEqualizationStrengthSlider.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -16),
|
||||
strokeEqualizationStrengthSlider.topAnchor.constraint(equalTo: strokeEqualizationDepthSlider.bottomAnchor, constant: 0),
|
||||
strokeEqualizationStrengthLabel.centerYAnchor.constraint(equalTo: strokeEqualizationStrengthSlider.centerYAnchor, constant:0),
|
||||
])
|
||||
}
|
||||
|
||||
@objc func hookFilterDepthSliderMoved(_ sender: UISlider) {
|
||||
phase1StrokeSampleIndexEnd = Int32(sender.value.rounded())
|
||||
hookFilterLabel.text = "\(SwiftLocalizationHelper.localizedString(forKey: "Stroke start hook filtering depth: "))\(String(format: "%d", Int32(sender.value)))";
|
||||
if sender.value > strokeEqualizationDepthSlider.value {
|
||||
strokeEqualizationDepthSlider.value = sender.value
|
||||
strokeEqualizationDepthSliderMoved(strokeEqualizationDepthSlider)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func strokeEqualizationDepthSliderMoved(_ sender: UISlider) {
|
||||
sender.value = max(sender.value, hookFilterSlider.value)
|
||||
phase2StrokeSampleIndexEnd = Int32(sender.value.rounded())
|
||||
strokeEqualizationDepthLabel.text = "\(SwiftLocalizationHelper.localizedString(forKey: "Stroke start equalization depth: "))\(String(format: "%d", Int32(sender.value)))";
|
||||
}
|
||||
|
||||
@objc func strokeEqualizationStrengthSliderMoved(_ sender: UISlider) {
|
||||
// sender.value = sender.value
|
||||
strokeEqualizationStrengthLabel.text = "\(SwiftLocalizationHelper.localizedString(forKey: "Stroke start force scaling: "))\(String(format: "%.2f", sender.value))";
|
||||
}
|
||||
|
||||
|
||||
public func setupNavigationBar() {
|
||||
// 1. 创建 UINavigationBar
|
||||
navBar.removeFromSuperview()
|
||||
@@ -589,9 +737,9 @@ class PressureCurveViewController: UIViewController {
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
let appearance = UINavigationBarAppearance()
|
||||
appearance.configureWithTransparentBackground() // 透明背景
|
||||
// appearance.configureWithTransparentBackground() // 透明背景
|
||||
appearance.shadowColor = nil // 去掉底部细线
|
||||
appearance.backgroundColor = .clear // 可以额外设置完全透明
|
||||
appearance.backgroundColor = .white // 可以额外设置完全透明
|
||||
|
||||
navBar.standardAppearance = appearance
|
||||
navBar.scrollEdgeAppearance = appearance
|
||||
@@ -602,30 +750,45 @@ class PressureCurveViewController: UIViewController {
|
||||
navBar.backgroundColor = .clear
|
||||
}
|
||||
|
||||
|
||||
// 2. 添加约束 (顶部,左右)
|
||||
NSLayoutConstraint.activate([
|
||||
navBar.topAnchor.constraint(equalTo: container.safeAreaLayoutGuide.topAnchor),
|
||||
navBar.leadingAnchor.constraint(equalTo: container.leadingAnchor),
|
||||
navBar.trailingAnchor.constraint(equalTo: container.trailingAnchor),
|
||||
navBar.heightAnchor.constraint(equalToConstant: 42) // 导航栏标准高度
|
||||
navBar.heightAnchor.constraint(equalToConstant: 50) // 导航栏标准高度
|
||||
])
|
||||
|
||||
|
||||
if curveView.testStage == .curveStage {
|
||||
let resetButton = UIBarButtonItem(title: SwiftLocalizationHelper.localizedString(forKey: "Reset"), style: .plain, target: self, action: #selector(resetTapped))
|
||||
let rangeTestButton = UIBarButtonItem(title: SwiftLocalizationHelper.localizedString(forKey: "Pressure-range-test"), style: .plain, target: self, action: #selector(pressureRangeTest))
|
||||
let saveButton = UIBarButtonItem(title: SwiftLocalizationHelper.localizedString(forKey: "Save"), style: .plain, target: self, action: #selector(saveTapped))
|
||||
let quitButton = UIBarButtonItem(title: SwiftLocalizationHelper.localizedString(forKey: "Exit"), style: .plain, target: self, action: #selector(exitTapped))
|
||||
// curvePhaseButton = UIBarButtonItem(title: SwiftLocalizationHelper.localizedString(forKey: "Initial-touch-curve"), style: .plain, target: self, action: #selector(curvePhaseButtonTapped))
|
||||
curvePhase = .stroke
|
||||
saveButton = UIBarButtonItem(title: SwiftLocalizationHelper.localizedString(forKey: "Save"), style: .plain, target: self, action: #selector(saveTapped))
|
||||
// let quitButton = UIBarButtonItem(title: SwiftLocalizationHelper.localizedString(forKey: "Exit"), style: .plain, target: self, action: #selector(exitTapped))
|
||||
navItem.leftBarButtonItems = [resetButton, rangeTestButton]
|
||||
navItem.rightBarButtonItems = [quitButton, saveButton]
|
||||
navItem.rightBarButtonItems = [saveButton]
|
||||
}
|
||||
|
||||
|
||||
if curveView.testStage == .drawingStage {
|
||||
navItem = UINavigationItem(title: SwiftLocalizationHelper.localizedString(forKey: "Pressure Range Test"))
|
||||
}
|
||||
|
||||
if #available(iOS 26.0, *) {
|
||||
if let buttons = navItem.leftBarButtonItems {
|
||||
for button in buttons {
|
||||
button.hidesSharedBackground = true
|
||||
button.tintColor = .tintColor
|
||||
}
|
||||
}
|
||||
if let buttons = navItem.rightBarButtonItems {
|
||||
for button in buttons {
|
||||
button.hidesSharedBackground = true
|
||||
button.tintColor = .tintColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 将 UINavigationItem 添加到导航栏
|
||||
navBar.items = [navItem]
|
||||
}
|
||||
|
||||
@@ -681,10 +844,43 @@ class PressureCurveViewController: UIViewController {
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
@objc private func curvePhaseButtonTapped() {
|
||||
switch curvePhase {
|
||||
case .initialTouch:
|
||||
// initialTouchCurvePoints = curveView.curve.polylinePoints
|
||||
curvePhaseButton.title = SwiftLocalizationHelper.localizedString(forKey:"Initial-touch-curve")
|
||||
case .stroke:
|
||||
strokeCurvePoints = curveView.curve.polylinePoints
|
||||
curvePhaseButton.title = SwiftLocalizationHelper.localizedString(forKey:"Stroke-curve")
|
||||
}
|
||||
curvePhase = curvePhase.next()
|
||||
displayCurve()
|
||||
} */
|
||||
|
||||
@objc private func persistCurve() {
|
||||
let oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
|
||||
let selectedProfile = oscProfileMan.getSelectedProfile()
|
||||
|
||||
switch curvePhase {
|
||||
case .initialTouch:
|
||||
break
|
||||
// initialTouchCurvePoints = curveView.curve.polylinePoints
|
||||
case .stroke:
|
||||
strokePressureCurvePoints = curveView.curve.polylinePoints
|
||||
}
|
||||
|
||||
curveView.curve.polylinePoints = strokePressureCurvePoints
|
||||
selectedProfile.pressureCurvePoints = PressureCurve.exportCurvePoints(curveView.curve)
|
||||
// curveView.curve.polylinePoints = initialTouchCurvePoints
|
||||
// selectedProfile.initialTouchPressureCurvePoints = PressureCurve.exportCurvePoints(curveView.curve)
|
||||
|
||||
displayCurve()
|
||||
|
||||
selectedProfile.phase1StrokeSampleIndexEnd = Int32(hookFilterSlider.value)
|
||||
selectedProfile.phase2StrokeSampleIndexEnd = Int32(strokeEqualizationDepthSlider.value)
|
||||
selectedProfile.strokeEqualizationStrength = CGFloat(strokeEqualizationStrengthSlider.value)
|
||||
|
||||
oscProfileMan.replaceSelectedProfile(with: selectedProfile, overwriteDefault: true)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
@@ -718,7 +914,9 @@ class PressureCurveViewController: UIViewController {
|
||||
}
|
||||
|
||||
@objc private func saveTapped() {
|
||||
saveButton.isEnabled = false
|
||||
IAPManager.checkPurchaseInfo(.PencilProPack) { info in
|
||||
self.saveButton.isEnabled = true
|
||||
if info.valid {
|
||||
self.persistCurve()
|
||||
}
|
||||
@@ -731,4 +929,15 @@ class PressureCurveViewController: UIViewController {
|
||||
@objc private func exitTapped() {
|
||||
self.dismiss(animated: true)
|
||||
}
|
||||
|
||||
public func gestureRecognizer(
|
||||
_ gestureRecognizer: UIGestureRecognizer,
|
||||
shouldReceive touch: UITouch
|
||||
) -> Bool {
|
||||
return touch.view == view
|
||||
}
|
||||
|
||||
@objc private func dismissSelf(){
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8088,6 +8088,75 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Stroke start equalization depth: " : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "笔迹起始端均衡深度: "
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "筆跡起始端均衡深度: "
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "筆跡起始端均衡深度: "
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Stroke start force scaling: " : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "笔迹起始端力度系数: "
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "筆跡起始端力度係數: "
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "筆跡起始端力度係數: "
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Stroke start hook filtering depth: " : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "笔迹起始端勾子过滤深度: "
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "筆跡起始端勾子過濾深度: "
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "筆跡起始端勾子過濾深度: "
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Successfully sent wake-up request. It may take a few moments for the PC to wake. If it never wakes up, ensure it's properly configured for Wake-on-LAN." : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
|
||||
Reference in New Issue
Block a user