IT Share you

iPhone 부드러운 스케치 그리기 알고리즘

shareyou 2020. 12. 4. 21:25
반응형

iPhone 부드러운 스케치 그리기 알고리즘


iPhone에서 스케치 앱을 작업 중입니다. 나는 그것을 작동했지만 여기에서 본 것처럼 예쁘지 않습니다.여기에 이미지 설명 입력

그리고 그림을 부드럽게하기위한 제안을 찾고 있습니다. 기본적으로 사용자가 전화 한 화면에 손가락을 놓을 때

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 

그런 다음 배열에서 단일 터치를 수집합니다.

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

사용자가 화면에서 손가락을 뗐을 때

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

그런 다음 사용하여 배열의 모든 점을 그립니다.

NSMutableArray *points = [collectedArray points];   

CGPoint firstPoint;
[[points objectAtIndex:0] getValue:&firstPoint];

CGContextMoveToPoint(context, firstPoint.x, firstPoint.y);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineJoin(context, kCGLineJoinRound);

for (int i=1; i < [points count]; i++) {
    NSValue *value = [points objectAtIndex:i];
    CGPoint point;
    [value getValue:&point];    
    CGContextAddLineToPoint(context, point.x, point.y);

} 

CGContextStrokePath(context);
UIGraphicsPushContext(context);

그리고 이제는 "스케치북"앱처럼 그림을 개선하고 싶습니다. 여기에 이미지 설명 입력

배열의 모든 포인트를 재정렬하는 신호 처리 알고리즘과 관련이 있다고 생각하지만 확실하지 않습니다. 어떤 도움이라도 대단히 감사하겠습니다.

사전에 Thankz :)


이와 같이 곡선을 매끄럽게하는 가장 쉬운 방법은 직선 세그먼트 대신 베 지어 곡선을 사용하는 것입니다. 이에 대한 수학은 여러 점을 통과하는 곡선을 매끄럽게 만드는 데 필요한 곡선을 계산하는 방법을 설명하는 이 문서 ( 이 답변 에서 가리키는)를 참조하십시오 .

Core Plot 프레임 워크는 이제 플롯 의 곡선을 매끄럽게 할 수있는 기능이 있다고 믿습니다 . 따라서 이러한 유형의 매끄럽게 구현하는 데 사용되는 코드를 볼 수 있습니다.

이러한 스무딩 루틴은 빠르고 비교적 쉽게 구현할 수 있기 때문에 마법 같은 것은 없습니다.


CGPoint midPoint(CGPoint p1, CGPoint p2)
{

    return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);

}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *touch = [touches anyObject];

    previousPoint1 = [touch previousLocationInView:self];
    previousPoint2 = [touch previousLocationInView:self];
    currentPoint = [touch locationInView:self];

}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *touch = [touches anyObject];

    previousPoint2 = previousPoint1;
    previousPoint1 = [touch previousLocationInView:self];
    currentPoint = [touch locationInView:self];


    // calculate mid point
    CGPoint mid1 = midPoint(previousPoint1, previousPoint2); 
    CGPoint mid2 = midPoint(currentPoint, previousPoint1);

    UIGraphicsBeginImageContext(self.imageView.frame.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self.imageView.image drawInRect:CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height)];

    CGContextMoveToPoint(context, mid1.x, mid1.y);
    // Use QuadCurve is the key
    CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y); 

    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetLineWidth(context, 2.0);
    CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
    CGContextStrokePath(context);

    self.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

}

나는 주제를 정말 좋아합니다. 모든 구현, 특히 Krzysztof Zabłocki와 Yu-Sen Han에 감사드립니다. 패닝 속도 (사실 마지막 터치 사이의 거리)에 따라 선 두께를 변경하기 위해 한유 선 버전을 수정했습니다. 또한 점 그리기를 구현했습니다 (touchBegan 및 touchEnded 위치가 서로 가까이있는 경우) 결과는 다음과 같습니다.여기에 이미지 설명 입력

선 두께를 정의하기 위해 거리 함수를 선택했습니다.

(이유는 묻지 마세요 ... 잘 어울리지 만 더 나은 것을 찾을 수있을 것입니다)

여기에 이미지 설명 입력

CGFloat dist = distance(previousPoint1, currentPoint);
CGFloat newWidth = 4*(atan(-dist/15+1) + M_PI/2)+2;

힌트 하나 더. 두께가 매끄럽게 변경되는지 확인하기 위해 이전 세그먼트의 두께와 사용자 지정 계수에 따라 경계를 지정했습니다.

self.lineWidth = MAX(MIN(newWidth,lastWidth*WIDTH_RANGE_COEF),lastWidth/WIDTH_RANGE_COEF);

kyoji의 대답 을 재사용 가능한 하위 클래스 인 Swift로 번역 했습니다UIImageView . 하위 클래스를 TouchDrawImageView사용하면 사용자가 손가락으로 이미지보기에 그릴 수 있습니다.

TouchDrawImageView클래스를 프로젝트에 추가했으면 스토리 보드를 열고

  1. TouchDrawImageView이미지보기의 "사용자 지정 클래스"로 선택
  2. 이미지보기의 "사용자 상호 작용 활성화"속성을 확인하십시오.

다음은 코드입니다 TouchDrawImageView.swift.

import UIKit

class TouchDrawImageView: UIImageView {

    var previousPoint1 = CGPoint()

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        previousPoint1 = touch.previousLocation(in: self)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }

        let previousPoint2 = previousPoint1
        previousPoint1 = touch.previousLocation(in: self)
        let currentPoint = touch.location(in: self)


        // calculate mid point
        let mid1 = midPoint(p1: previousPoint1, p2: previousPoint2)
        let mid2 = midPoint(p1: currentPoint, p2: previousPoint1)

        UIGraphicsBeginImageContext(self.frame.size)
        guard let context = UIGraphicsGetCurrentContext() else { return }
        if let image = self.image {
            image.draw(in: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height))
        }

        context.move(to: mid1)
        context.addQuadCurve(to: mid2, control: previousPoint1)

        context.setLineCap(.round)
        context.setLineWidth(2.0)
        context.setStrokeColor(red: 1.0, green: 0, blue: 0, alpha: 1.0)
        context.strokePath()

        self.image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }

    func midPoint(p1: CGPoint, p2: CGPoint) -> CGPoint {
        return CGPoint(x: (p1.x + p2.x) / 2.0, y: (p1.y + p2.y) / 2.0)
    }
}

입력 해 주셔서 감사합니다. 공간이 필요하기 때문에 여기에서 퀘스트를 업데이트합니다.

거의 성공하지 못한 제안한 corePlot 및 Bezier 곡선 솔루션을 모두 찾습니다.

corePlot의 경우 int 배열에서 그래프 플롯을 가져올 수 있지만 커브 스무딩과 관련된 것을 찾을 수 없습니다 .BTW 여기에서는 임의의 숫자로 CPScatterPlot을 사용하고 있습니다.

여기에 이미지 설명 입력

Bezier 곡선에 관해서는 나의 탐구가 여기 로 이끈다 iOS에서 Spline 구현과 관련이있다

  CatmullRomSpline *myC = [[CatmullRomSpline alloc] initAtPoint:CGPointMake(1.0, 1.0)];
  [myC addPoint:CGPointMake(1.0, 1.5)];
  [myC addPoint:CGPointMake(1.0, 1.15)];
  [myC addPoint:CGPointMake(1.0, 1.25)];
  [myC addPoint:CGPointMake(1.0, 1.23)];
  [myC addPoint:CGPointMake(1.0, 1.24)];
  [myC addPoint:CGPointMake(1.0, 1.26)];
  NSLog(@"xxppxx %@",[myC asPointArray]);
  NSLog(@"xxppxx2 %@",myC.curves);

그리고 내가 얻는 결과는 다음과 같습니다.

  2011-02-24 14:45:53.915 DVA[10041:40b] xxppxx (
  "NSPoint: {1, 1}",
  "NSPoint: {1, 1.26}"
   )

  2011-02-24 14:45:53.942 DVA[10041:40b] xxppxx2 (
  "QuadraticBezierCurve: 0x59eea70"
  )

나는 거기에서 어떻게 가는지 잘 모르겠습니다. 그래서 나는 그 정면에도 붙어 있습니다 :(

마지막 리소스로 GLPaint를 찾아 보았습니다. OpenGLES를 사용하고 "소프트 도트"스프라이트를 사용하여 배열의 점을 플로팅합니다. 나는 그것이 문제를 고치기보다는 피하는 것과 같다는 것을 안다. 하지만 어쨌든 여기서 발견 한 내용을 공유 할 것 같습니다.

Black은 GLPaint이고 흰색은 오래된 방법입니다. 그리고 마지막은 "Sketch Book"앱의 그림입니다.

여기에 이미지 설명 입력 여기에 이미지 설명 입력 여기에 이미지 설명 입력

나는 여전히 이것을 제대로하기 위해 노력하고 있으며, 어떤 추가 제안이라도 가장 환영받습니다.


GLPaint 코드에서 어리석은 점을 제거합니다.

변화

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

이 기능

//Ändrat av OLLE
/*
// Convert touch point from UIView referential to OpenGL one (upside-down flip)
if (firstTouch) {
    firstTouch = NO;
    previousLocation = [touch previousLocationInView:self];
    previousLocation.y = bounds.size.height - previousLocation.y;
} else {
    location = [touch locationInView:self];
    location.y = bounds.size.height - location.y;
    previousLocation = [touch previousLocationInView:self];
    previousLocation.y = bounds.size.height - previousLocation.y;
}
 */
location = [touch locationInView:self];
location.y = bounds.size.height - location.y;
previousLocation = [touch previousLocationInView:self];
previousLocation.y = bounds.size.height - previousLocation.y;

//Ändrat av OLLE//

나는 이것이 우리 문제의 해결책이 아니라는 것을 알고 있지만 그것은 무언가입니다.

참고 URL : https://stackoverflow.com/questions/5076622/iphone-smooth-sketch-drawing-algorithm

반응형