데이터 저장을 위해 2D 곡선을 점으로 변환


12

곡선 또는 경로를 최소 포인트 수로 변환하여 파일이나 데이터베이스에 저장할 수있는 알고리즘을 만들었습니다.

이 방법은 간단합니다. 세 점을 동일한 단계로 이동하고이 점들이 형성하는 선 사이의 각도를 측정합니다. 각도가 공차보다 크면 해당 점에 대해 새로운 입방체 곡선을 만듭니다. 그런 다음 선을 앞으로 이동하고 각도를 다시 측정합니다.

안드로이드 경로 클래스 를 알고있는 사람들을 위해 -dstPath 는 사용자 정의 클래스이며 점을 배열에 기록하므로 나중에 포인트를 저장할 수 있습니다 .srcPath 는 Regions Union의 결과이므로 키 포인트가 없습니다. 저장합니다.

문제는 소스 코드가 완벽한 원과 사각형으로 구성된 아래 코드에서 생성 된이 이미지에서 볼 수 있듯이 원이 부드럽게 보이지 않는다는 것입니다. 공차 각도와 계단 길이를 변경하려고했지만 아무것도 도움이되지 않습니다. 이 알고리즘이나 다른 접근법의 개선을 제안 할 수 있는지 궁금합니다.

편집 : 이제 Android java를 사용하는 사람들을 위해 전체 코드를 게시 했으므로 쉽게 시도하고 실험 할 수 있습니다.

여기에 이미지 설명을 입력하십시오

public class CurveSavePointsActivity extends Activity{

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(new CurveView(this));
    }

    class CurveView extends View{

        Path srcPath, dstPath;
        Paint srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        public CurveView(Context context) {
            super(context);

            srcPaint.setColor(Color.BLACK);
            srcPaint.setStyle(Style.STROKE);
            srcPaint.setStrokeWidth(2);
            srcPaint.setTextSize(20);

            dstPaint.setColor(Color.BLUE);
            dstPaint.setStyle(Style.STROKE);
            dstPaint.setStrokeWidth(2);
            dstPaint.setTextSize(20);

            srcPath = new Path();
            dstPath = new Path();

        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);

            //make a circle path
            srcPath.addCircle(w/4, h/2, w/6 - 30, Direction.CW);

            //make a rectangle path
            Path rectPath = new Path();
            rectPath.addRect(new RectF(w/4, h/2 - w/16, w*0.5f, h/2 + w/16), Direction.CW);


            //create a path union of circle and rectangle paths
            RectF bounds = new RectF();
            srcPath.computeBounds(bounds, true);
            Region destReg = new Region();
            Region clip = new Region();
            clip.set(new Rect(0,0, w, h));
            destReg.setPath(srcPath, clip);
            Region srcReg = new Region();
            srcReg.setPath(rectPath, clip); 
            Region resultReg = new Region();
            resultReg.op(destReg, srcReg, Region.Op.UNION);
            if(!resultReg.isEmpty()){
                srcPath.reset();
                srcPath.addPath(resultReg.getBoundaryPath());
            }

            //extract a new path from the region boundary path
            extractOutlinePath();

            //shift the resulting path bottom left, so they can be compared
            Matrix matrix = new Matrix();
            matrix.postTranslate(10, 30);
            dstPath.transform(matrix);

        }

         @Override 
            public void onDraw(Canvas canvas) { 
                super.onDraw(canvas);    
                canvas.drawColor(Color.WHITE);
                canvas.drawPath(srcPath, srcPaint);
                canvas.drawPath(dstPath, dstPaint);

                canvas.drawText("Source path", 40, 50, srcPaint);
                canvas.drawText("Destination path", 40, 100, dstPaint);
         }


         public void extractOutlinePath() {

             PathMeasure pm = new PathMeasure(srcPath, false); //get access to curve points

             float p0[] = {0f, 0f}; //current position of the new polygon
             float p1[] = {0f, 0f}; //beginning of the first line
             float p2[] = {0f, 0f}; //end of the first & the beginning of the second line
             float p3[] = {0f, 0f}; //end of the second line

             float pxStep = 5; //sampling step for extracting points
             float pxPlace  = 0; //current place on the curve for taking x,y coordinates
             float angleT = 5; //angle of tolerance

             double a1 = 0; //angle of the first line
             double a2 = 0; //angle of the second line

             pm.getPosTan(0, p0, null); //get the beginning x,y of the original curve into p0
             dstPath.moveTo(p0[0], p0[1]); //start new path from the beginning of the curve
             p1 = p0.clone(); //set start of the first line

             pm.getPosTan(pxStep, p2, null); //set end of the first line & the beginning of the second

             pxPlace = pxStep * 2;
             pm.getPosTan(pxPlace, p3, null); //set end of the second line


             while(pxPlace < pm.getLength()){
             a1 = 180 - Math.toDegrees(Math.atan2(p1[1] - p2[1], p1[0] - p2[0])); //angle of the first line
             a2 = 180 - Math.toDegrees(Math.atan2(p2[1] - p3[1], p2[0] - p3[0])); //angle of the second line

             //check the angle between the lines
             if (Math.abs(a1-a2) > angleT){

               //draw a straight line to the first point if the current p0 is not already there
               if(p0[0] != p1[0] && p0[1] != p1[1]) dstPath.quadTo((p0[0] + p1[0])/2, (p0[1] + p1[1])/2, p1[0], p1[1]);

               dstPath.quadTo(p2[0] , p2[1], p3[0], p3[1]); //create a curve to the third point through the second

               //shift the three points by two steps forward
               p0 = p3.clone();
               p1 = p3.clone();
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p2, null); 
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p3, null);
               if (pxPlace > pm.getLength()) break;
             }else{
               //shift three points by one step towards the end of the curve
               p1 = p2.clone(); 
               p2 = p3.clone();
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p3, null); 
             }
             }
             dstPath.close();
         }
    }

}

다음은 원본 알고리즘과 내 알고리즘이 생성 한 내용을 비교 한 것입니다.

경로 간의 비교;  파생 상품의 눈에 띄게 매끄러운 코너


왜 b- 스플라인을 사용하지 않습니까?
GriffinHeart 2013

4
물건이 원과 사각형이라는 것을 알고 있다면 원과 사각형을 저장하지 않겠습니까? 그리고 일반화 된 형태로-어떤 입력이라도 당신의 물건을 생성하는 것은 아마도 그것을 저장하기에 합리적인 형식 일 것입니다. 다른 질문처럼 보이는 압축 체계를 찾고 있다면 (또는 적어도 소스 데이터에 대한 더 많은 정보가 필요할 것입니다) 도움이 될 것입니다).
Jeff Gates

첫 번째 문장에서 말했듯이 예측할 수없는 모양 일 수 있습니다. 여기서 원과 사각형은 테스트 예제 일뿐입니다.
Lumis

@Lumis 당신은 정말로 b- 스플라인을 살펴 봐야합니다. 자체 솔루션을 구현해야 할 이유가 있습니까?
GriffinHeart

1
Well path 클래스는 스플라인으로 해당 커브를 구성하므로 이미 사용하고 있습니다. 나는 수학 중심의 또 다른 제안이 있습니다. 포인트를 저장하는 대신 사용자 입력 (명령 패턴)을 저장하고 재생하여 동일한 "이미지"를 만듭니다.
GriffinHeart

답변:


6

두 가지 문제가 있다고 생각합니다.

비대칭 제어점

처음에는 p0에서 p1과 p1에서 p2 사이의 동일한 거리로 시작합니다. 선분 사이의 공차 각도가 충족되지 않으면 p1과 p2를 앞으로 이동하지만 p0을 원래 위치에 유지합니다. p1 ~ p2 사이의 거리를 동일하게 유지하면서 p0 ~ p1 사이의 거리를 늘립니다. 제어점으로 p1을 사용하여 곡선을 작성하면 마지막 곡선 이후에 몇 번의 반복이 수행되었는지에 따라 p2를 향하여 크게 바이어스 될 수 있습니다. p2보다 p2의 양을 두 배로 이동하면 점 사이의 거리가 균일 해집니다.

이차 곡선

다른 답변에서도 언급했듯이 이차 곡선은이 경우에 좋지 않습니다. 생성 한 인접 커브는 제어점과 접선을 공유해야합니다 . 입력 데이터가 포인트 인 경우 Catmull-Rom 스플라인 이 그 목적에 적합합니다. 제어점의 탄젠트가 이전 및 다음 포인트에서 계산되는 입방 형 Hermite 곡선입니다.

Android의 Path API는 매개 변수와 관련하여 Hermite 곡선과 약간 다른 베 지어 곡선을 지원합니다. 다행히 Hermite 커브를 베 지어 커브로 변환 할 수 있습니다. 다음 은 인터넷 검색시 찾은 첫 번째 예제 코드입니다. 이 Stackoverflow 답변 은 수식을 제공하는 것으로 보입니다.

날카로운 모서리 문제도 언급했습니다. 입력 데이터가 있으면 실제 날카로운 모서리 또는 매우 가파른 곡선이 있는지 감지 할 수 없습니다. 이것이 문제가된다면, 필요에 따라 즉시 단계를 증가 / 감소함으로써 반복을보다 적응 적으로 만들 수 있습니다.

편집 : 추가 생각 후 이차 곡선을 사용할 수 있습니다. p1을 제어점으로 사용하여 p0에서 p2로 2 차 곡선을 그리는 대신 새 점 p0_1을 제어점으로 사용하여 p0에서 p1로 그립니다. 아래 그림을 참조하십시오. 새로운 제어점

p0_1이 p0 및 p1의 탄젠트 교차점에 있으면 결과가 매끄러 워야합니다. 더 좋은 방법 PathMeasure.getPosTan()은 리턴 값도 세 번째 매개 변수로 접하기 때문에 인접한 점의 근사값 대신 실제 정확한 접선을 사용할 수 있습니다. 이 방법을 사용하면 기존 솔루션에 대한 변경이 적습니다.

이 답변을 기반으로 다음 공식으로 교차점을 계산할 수 있습니다.

getPosTan(pxPlace0, p0, t0); // Also get the tangent
getPosTan(pxPlace1, p1, t1);
t1 = -t1; // Reverse direction of second tangent
vec2 d = p1 - p0;
float det = t1.x * t0.y - t1.y * t0.x;
float u = (d.y * t1.x - d.x * t1.y) / det;
float v = (d.y * t0.x - d.x * t0.y) / det; // Not needed ... yet
p0_1 = p0 + u * t0;

그러나이 솔루션은 u와 v가 모두 음수가 아닌 경우에만 작동합니다. 두 번째 그림을보십시오 : 광선이 교차하지 않습니다

u는 음수이므로 광선은 교차하지만 교차하지 않습니다. 이 경우 이전 곡선과 매끄럽게 연결되는 2 차 곡선을 그릴 수 없습니다. 베 지어 곡선이 필요합니다. 이 답변의 앞부분에 제공된 방법으로 제어점을 계산하거나 접선에서 직접 제어점을 파생시킬 수 있습니다. 탄젠트 광선 p0 + u * t0에 p0을 투영하고 다른 광선에 대해서도 그 반대로는 제어점 c0 및 c1을 모두 제공합니다. 접선에있는 한 c0 대신 p0과 c0 사이의 점을 사용하여 곡선을 조정할 수도 있습니다.

Edit2 : 드로잉 위치가 p1 인 경우 다음 의사 코드를 사용하여 베 지어 제어점을 p2로 계산할 수 있습니다.

vec2 p0, p1, p2, p3; // These are calculated with PathMeasure
vec2 cp1 = p1 + (p2 - p0) / 6;
vec2 cp2 = p2 - (p3 - p1) / 6;

이것으로 p1에서 p2까지의 경로를 추가 할 수 있습니다.

path.cubicTo(cp1.x, cp1.y, cp2.x, cp2.y, p2.x, p2.y);

float [ 2 ] 배열 에서 벡터 작업을 구성 요소 당 작업으로 바꾸면 코드와 일치합니다. 초기화로 시작 p1 = start;하면 p2와 p3이 다음 포인트입니다. p0은 처음에 정의되지 않았습니다. 아직 p0이없는 첫 번째 세그먼트의 경우 제어점으로 cp2를 사용하여 p1에서 p2까지의 2 차 곡선을 사용할 수 있습니다. p3이없는 경로의 끝에서도 cp1을 제어점으로하여 p1에서 p2로 2 차 곡선을 그릴 수 있습니다. 또는 첫 번째 세그먼트의 경우 p0 = p1을, 마지막 세그먼트의 경우 p3 = p2를 초기화 할 수 있습니다. 모든 세그먼트 후에는 p0 = p1; p1 = p2; and p2 = p3;앞으로 이동할 때 값 을 이동합니다.

경로를 저장할 때 모든 포인트 p0 ... pN을 저장하면됩니다. 제어점 cp1 및 cp2는 필요에 따라 계산할 수 있으므로 저장할 필요가 없습니다.

Edit3 : 커브 생성에 좋은 입력 값을 얻는 것이 어려워 보이는 또 다른 접근법을 제안합니다. 직렬화 사용. 안드로이드 경로는 그것을 지원하지 않는 것 같지만 다행히 지역 클래스는 지원합니다. 코드는 이 답변 을 참조하십시오 . 정확한 결과를 얻을 수 있습니다. 최적화되지 않은 경우 직렬화 된 형식으로 약간의 공간이 필요할 수 있지만이 경우 압축률이 매우 높아야합니다. GZIPOutputStream을 사용하면 Android Java에서 압축이 쉽습니다 .


유망한 소리. 그러나 p0이 아니라 p1, p2, p3이 사용되며, p0은 계산할 때 새로운 한정 점을 저장하기위한 것이며 직선을 위해서만 사용되므로 각 단계에서 샘플링되지 않습니다. 새로운 제어점에 대해 x, y를 계산하는 방법을 알려주시겠습니까?
Lumis

나중에 할 수는 있지만 stackoverflow.com/questions/2931573/…을 확인하십시오 . u와 v를 사용하면 교차점을 얻을 수 있습니다.
msell

도움을 주셔서 감사합니다. 이것을 시도하고 싶습니다만, Android 용 Java로 작성해야합니다. vector2가 없으며 t1과 p1 등은 float 배열이므로 t1 = -t1 또는 u * t0과 같은 직접 연산을 수행 할 수 없습니다. t1 = -t1은 t1.x = -t1x를 의미한다고 가정합니다. t1.y = -t1.y 등?
Lumis

예, 그것은 더 간결하고 읽기 쉬운 의사 코드였습니다.
msell

음모가 두꺼워지고 있습니다. Android에서 두 경로의 영역 교차가 앤티 앨리어싱되지 않은 경로를 반환하기 때문에 접선이 그 자리에 있습니다. 따라서 적절한 해결책은 주어진 점을 통해 매끄러운 곡선을 먼저 주행 한 다음 샘플링하는 것입니다. 앤티 앨리어싱 경로에서 코드가 완벽하게 작동하고 적절한 제어점을 생성합니다.
Lumis

13

W3C는 무엇을 할 것인가?

인터넷에 이런 문제가있었습니다. 월드 와이드 웹 컨소시엄 나타났습니다. 그것은이 권장 표준 용액 : 1999 년부터 확장 가능한 벡터 그래픽 (SVG를) . 그것은이다 XML 특히 2D 모양을 저장하기 위해 설계 기반의 파일 형식입니다.

" 확장 가능? "

확장 가능한 벡터 그래픽 !

  • 확장 성 : 모든 크기로 부드럽게 확장 할 수 있습니다.
  • 벡터 : 그것은의 수학적 개념을 기반으로 벡터 .
  • 그래픽 . 사진을 찍기위한 것입니다.

SVG 버전 1.1의 기술 사양은 다음과 같습니다 .
(이름으로 무서워하지 마십시오; 실제로 읽는 것이 즐겁습니다.)

그들은 이나 사각형 과 같은 기본 도형이 어떻게 저장 될지를 정확히 적었습니다 . 예를 들어, 사각형은 이러한 특성을 가지고 : , , , , , . ( 및 모서리가 둥근 모서리에 사용할 수 있습니다.)xywidthheightrxryrxry

SVG의 직사각형 예는 다음과 같습니다. (실제로 두 개는 캔버스 외곽선입니다.)

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="12cm" height="4cm" viewBox="0 0 1200 400"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <desc>Example rect01 - rectangle with sharp corners</desc>

  <!-- Show outline of canvas using 'rect' element -->
  <rect x="1" y="1" width="1198" height="398"
        fill="none" stroke="blue" stroke-width="2"/>

  <rect x="400" y="100" width="400" height="200"
        fill="yellow" stroke="navy" stroke-width="10"  />
</svg>

그것이 나타내는 것은 다음과 같습니다.

파란색 윤곽선이있는 노란색 사각형

사양에서 알 수 있듯이 필요하지 않은 경우 일부 속성을 생략 할 수 있습니다. (예를 들어, rxry속성은 여기에 사용되지 않았다.) 예에 대해 상단에 cruft에의 톤 거기에 DOCTYPE당신이 당신의 게임에 대한 필요가 없습니다 이는. 그들은 또한 선택 사항입니다.

경로

SVG 경로종이에 연필을 넣고 그 주위로 이동하여 결국 다시 올리면 경로 가 있다는 의미에서 "경로"입니다 . 그들은하지 않습니다 폐쇄 ,하지만 그들은 수 있습니다.

각 경로에는 기본적으로 펜을 종이에 넣고 움직일 수 있는 일련의 명령 인 경로 데이터가d 포함 된 속성 ( "그리기"를 의미한다고 생각합니다) 이 있습니다 .

그들은 삼각형의 예를 보여줍니다.

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4cm" height="4cm" viewBox="0 0 400 400"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>Example triangle01- simple example of a 'path'</title>
  <desc>A path that draws a triangle</desc>
  <rect x="1" y="1" width="398" height="398"
        fill="none" stroke="blue" />
  <path d="M 100 100 L 300 100 L 200 300 z"
        fill="red" stroke="blue" stroke-width="3" />
</svg>

빨간 삼각형

? 의 d속성을 참조하십시오 path.

d="M 100 100 L 300 100 L 200 300 z"

MA는 명령 에 대한 으로 이동 (좌표 다음)의 L의가 있습니다 에 선 (좌표) 및 z(; 좌표를 필요로하지 않는다 즉, 첫 번째 위치에 선 다시 그리기) 경로를 종료하는 명령입니다.

직선이 지루합니까? 3 차 또는 2 차 베 지어 명령을 사용하십시오 !

큐빅 베 지어

베 지어 곡선 뒤에있는 이론은 다른 곳에서도 잘 다루고 있지만 ( 위키 백과 같은 ) 여기 요약은 다음과 같습니다.

이차 베 지어 추적

또한이 사양에는 원하는 경우 대부분의 기본 모양을 패스로 변환하기위한 지침이 제공됩니다.

SVG를 사용해야하는 이유와시기

텍스트에서 임의의 2D 모양을 나타내는 것은 매우 복잡하기 때문에이 경로를 따라 가고 싶을 때 신중하게 결정하십시오. 예를 들어 (잠재적으로 많은) 직선으로 이루어진 경로로 자신을 제한하면 인생을 훨씬 쉽게 만들 수 있습니다.

그러나 임의의 모양을 원한다면 SVG를 사용하는 것이 좋습니다. 훌륭한 도구 지원 : 낮은 수준에서 XML 구문 분석을위한 많은 라이브러리와 높은 수준에서 SVG 편집기 도구 를 찾을 수 있습니다 .

그럼에도 불구하고 SVG 표준은 좋은 예입니다.


문제는 곡선을 저장하지 않고 점으로 변환하는 것입니다. 그러나이 참조에 감사드립니다 .SVG 표준에 대해 아는 것이 좋습니다.
Lumis

@Lumis 제목과 내용이 다르게 제안 할 것입니다. 질문을 다시 생각해보십시오. (또는 이제 이것 하나는 다른 것을 요구하면서 상당히 확립되었습니다.)
Anko

4

코드에 잘못된 주석이 포함되어 있습니다.

dstPath.quadTo(p2[0] , p2[1], p3[0], p3[1]); //create a curve to the third point through the second

, 2 차 베 지어 곡선은 않습니다 하지 이동 을 통해 두 번째 점. 두 번째 점을 통과하려면 은자 곡선 과 같은 다른 유형의 곡선이 필요합니다 . Path 클래스를 사용할 수 있도록 은자 곡선을 베지 어로 변환 할 수 있습니다.

또 다른 제안은 점을 샘플링하는 대신 건너 뛰는 점의 평균을 사용하는 것입니다.

다른 제안은 각도를 임계 값으로 사용하는 대신 실제 곡선과 근사 곡선의 차이를 사용하는 것입니다. 각도는 실제 문제가 아닙니다. 실제 문제는 점 세트가 베 지어 곡선에 맞지 않을 때입니다.

또 다른 제안은 큐빅 베 지어를 사용하는 것이고, 하나의 탄젠트는 다음 탄젠트와 일치합니다. 그렇지 않으면 (이차 법) 곡선이 매끄럽게 일치하지 않는다고 생각합니다.


당신은 맞습니다, 두 번째 점은 그쪽으로 향하는 곡선을 "풀"합니다. cubicTo에는 quadTo 대신 하나가 아닌 두 개의 제어점이 필요합니다. 문제는 물론 올바른 제어점을 얻는 방법입니다. 소스 경로는 직선 또는 원형의 조합이 될 수 있으므로 날카로운 모서리를 잃고 싶지는 않습니다. 기본적으로 선택한 경로를 저장할 수있는 이미지 선택 도구를 만들고 있습니다.
Lumis

4

두 경로의 매끄러운 교차점을 얻으려면 교차로보다 먼저 확대하고 이후에 축소 할 수 있습니다.

그것이 좋은 해결책인지는 모르겠지만 나에게 잘 작동했습니다. 또한 빠릅니다. 이 예에서는 내가 만든 패턴 (줄무늬)과 둥근 경로를 교차시킵니다. 크기를 조절해도 좋아 보입니다.

여기 내 코드 :

    Path mypath=new Path(<desiredpath to fill with a pattern>);
    String sPatternType=cpath.getsPattern();

    Path pathtempforbounds=new Path(cpath.getPath());
    RectF rectF = new RectF();
     if (sPatternType.equals("1")){
         turnPath(pathtempforbounds, -45);
     }
     pathtempforbounds.computeBounds(rectF, true);

     float ftop=rectF.top;
     float fbottom=rectF.bottom;
     float fleft=rectF.left;
     float fright=rectF.right;
     float xlength=fright-fleft;

     Path pathpattern=new Path();

     float ypos=ftop;
     float xpos=fleft;

     float fStreifenbreite=4f;

     while(ypos<fbottom){
         pathpattern.moveTo(xpos,ypos);
         xpos=xpos+xlength;
         pathpattern.lineTo(xpos, ypos);
         ypos=ypos+fStreifenbreite;
         pathpattern.lineTo(xpos, ypos);
         xpos=xpos-xlength;
         pathpattern.lineTo(xpos, ypos);
         ypos=ypos-fStreifenbreite;
         pathpattern.lineTo(xpos, ypos);
         pathpattern.close();
         ypos=ypos+2*fStreifenbreite;

     }

     // Original vergrössern

     scalepath(pathpattern,10);
     scalepath(mypath,10);

     if (sPatternType.equals("1")){
         Matrix mdrehen=new Matrix();
         RectF bounds=new RectF();
         pathpattern.computeBounds(bounds, true);
         mdrehen.postRotate(45, (bounds.right + bounds.left)/2,(bounds.bottom + bounds.top)/2);
         pathpattern.transform(mdrehen);
     }

     RectF rectF2 = new RectF();
     mypath.computeBounds(rectF2, true);

     Region clip = new Region();
     clip.set((int)(rectF2.left-100f),(int)(rectF2.top -100f), (int)(rectF2.right+100f),(int)( rectF2.bottom+100f));
     Region region1 = new Region();
     region1.setPath(pathpattern, clip);

     Region region2 = new Region();
     region2.setPath(mypath, clip);

     region1.op(region2, Region.Op.INTERSECT);


     Path pnew=region1.getBoundaryPath();


     scalepath(pnew, 0.1f);
     cpath.setPathpattern(pnew);




public void turnPath(Path p,int idegree){
     Matrix mdrehen=new Matrix();
     RectF bounds=new RectF();
     p.computeBounds(bounds, true);
     mdrehen.postRotate(idegree, (bounds.right + bounds.left)/2,(bounds.bottom + bounds.top)/2);
     p.transform(mdrehen);
}

public void scalepath(Path p,float fscale){
     Matrix mverkleinern=new Matrix();
     mverkleinern.preScale(fscale,fscale);
     p.transform(mverkleinern);
}

여기에 이미지 설명을 입력하십시오

canvas.scale ()을 사용하여 확대 / 축소 할 때 여전히 매끄럽게 보입니다. 여기에 이미지 설명을 입력하십시오



1
놀랍게도이 간단한 트릭은 두 가지 문제를 해결합니다. 먼저 교차점 또는 합집합의 결과 경로를 매끄럽게 만들고 둘째로 동일한 스케일 업 경로를 샘플링 할 때 문제의 코드를 완벽하게 매끄럽게 만듭니다. 예상치 못한 간단한 해결책, 감사합니다!
Lumis

@user Editing은 무료입니다. <2k-rep 사용자의 경우 실제로는 +2입니다.
Anko

@Lumis 혼란스러워 경로 를 저장 하는 방법을 물었다 고 생각 했습니까?
Anko

1
불행히도, 더 많은 테스트를 마친 후에는 Region이 패스를 그릴 때 차지하는 픽셀을 사용하기 때문에 Path의 스케일링이 크고 반복적으로 수행되면 앱에 메모리가 부족합니다. 따라서이 솔루션은 제한적이며 위험하지만 명심해야합니다.
Lumis

3

다각형 보간을보십시오 ( http://en.wikipedia.org/wiki/Polynomial_interpolation )

기본적으로 n 개의 등 간격 노드를 사용합니다 (최적 보간은 등 간격이 아니지만 귀하의 경우 충분하고 구현하기 쉬워야합니다)

선이 충분히 매끄럽다 (<-큰 경우) 곡선 사이의 오차를 줄이는 차수 n의 다각형으로 끝납니다 .

귀하의 경우 선형 (차수 1) ​​보간을하고 있습니다.

GriffinHeart가 권장 하는 다른 경우 는 Splines를 사용하는 것입니다 ( http://en.wikipedia.org/wiki/Spline_interpolation )

어느 경우 든 곡선 대해 어떤 형태의 다항식 적합도 를 제공합니다.


2

변환 지점이 스토리지 전용이고 화면에서 다시 렌더링 할 때 매끄러 워야하는 경우에는 최대한의 충실도 스토리지를 얻을 수 있지만 주어진 곡선을 유지하는 데 필요한 총 스토리지는 최소화 할 수 있습니다. 실제로 원의 속성 (또는 호)을 저장하고 필요할 때 다시 그립니다.

유래. 반지름. 호를 그리기위한 시작 / 정지 각도.

어쨌든 렌더링을 위해 원 / 호를 점으로 변환 해야하는 경우 항상 속성 만 저장하면서 저장소에서로드 할 때 원 / 호를 변환 할 수 있습니다.


소스 경로 / 곡선은 자유 선 그리기를 포함하여 모든 모양이 될 수 있습니다. 각 구성 요소를 개별적으로 저장 한 다음로드 할 때 결합 해야하는 솔루션을 고려했지만 많은 양의 작업이 필요하며 모든 변환을 각 대상에 적용 해야하는 복잡한 객체의 조작 속도가 느려집니다. 다시 저장할 수 있도록
Lumis

2

직선이 아닌 곡선을 사용해야하는 이유가 있습니까? 직선은 작업하기가 더 쉽고 하드웨어에서 효율적으로 렌더링 될 수 있습니다.

고려해야 할 다른 접근 방식은 픽셀 당 몇 비트를 저장하여 모양의 내부, 외부 또는 외곽선에 있는지 나타냅니다. 이것은 압축률이 높아야하며 복잡한 선택의 경우 선보다 더 효율적일 수 있습니다.

이 기사는 흥미롭고 유용 할 수도 있습니다.


1

커브 보간을 살펴보십시오. 커브를 부드럽게하는 데 도움이되는 몇 가지 유형이 있습니다. 그 원에서 얻을 수있는 점수가 많을수록 좋습니다. 스토리지는 매우 저렴합니다. 따라서 360 개의 닫기 노드를 추출하는 것만으로도 저렴합니다 (위치는 8 바이트 일지라도 360 개 노드는 저장하기가 거의 비쌉니다).

여기 에는 4 개의 포인트만으로 보간 샘플을 배치 할 수 있습니다 . 결과는 꽤 좋습니다 (다른 사람들이 다른 효과적인 솔루션에 대해 차임 할 수도 있지만이 경우 가장 좋아하는 것은 Bezier입니다).

여기서도 놀 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.