stroke start equalization and filtering

This commit is contained in:
True Zhuanjia
2026-03-21 12:43:16 +08:00
parent 42f29074fa
commit e59f3ba1ab
5 changed files with 410 additions and 36 deletions
+4
View File
@@ -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;
+24
View File
@@ -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
+84 -16
View File
@@ -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
+229 -20
View File
@@ -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" : {