Chuyển đổi đường cong 2D thành điểm để lưu trữ dữ liệu


12

Tôi đã tạo một thuật toán chuyển đổi bất kỳ đường cong nào, tức là đường dẫn thành số điểm tối thiểu để tôi có thể lưu nó vào một tệp hoặc cơ sở dữ liệu.

Phương pháp rất đơn giản: nó di chuyển ba điểm theo các bước bằng nhau và đo góc giữa các đường mà các điểm này hình thành. Nếu góc lớn hơn dung sai thì nó tạo ra một đường cong hình khối mới đến điểm đó. Sau đó, nó di chuyển các dòng về phía trước và đo góc một lần nữa

Đối với những người biết Lớp đường dẫn Android - Lưu ý rằng dstPath là lớp tùy chỉnh, ghi lại các điểm vào một mảng để tôi có thể lưu điểm sau đó, trong khi srcPath là kết quả của liên minh Vùng và do đó không có điểm chính nào đối với tôi để tiết kiệm.

Vấn đề là vòng tròn trông không được mượt mà như bạn có thể thấy trong hình ảnh này, được tạo bởi mã bên dưới, trong đó đường dẫn nguồn bao gồm một vòng tròn và hình chữ nhật hoàn hảo. Tôi đã cố gắng thay đổi góc độ chịu đựng và độ dài các bước, nhưng không có gì giúp được. Tôi tự hỏi nếu bạn có thể đề xuất bất kỳ cải tiến cho thuật toán này, hoặc một cách tiếp cận khác.

EDIT: Bây giờ tôi đã đăng toàn bộ mã cho những người sử dụng Android java, để họ có thể dễ dàng thử và thử nghiệm.

nhập mô tả hình ảnh ở đây

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();
         }
    }

}

Đây là so sánh giữa bản gốc và những gì thuật toán của tôi tạo ra:

so sánh giữa các con đường;  đáng chú ý là các góc mượt mà trên đạo hàm


Tại sao không sử dụng b-splines?
GriffinHeart

4
Nếu bạn biết thứ đó là hình tròn và hình chữ nhật, tại sao không lưu trữ hình tròn và hình chữ nhật? Và ở dạng tổng quát - bất kỳ đầu vào nào được tạo ra, thứ của bạn có thể là định dạng hợp lý để lưu trữ. Nếu bạn đang tìm kiếm một lược đồ nén có vẻ giống như một câu hỏi khác (hoặc ít nhất chúng tôi sẽ cần nhiều thông tin hơn về dữ liệu nguồn Để được hữu ích).
Jeff Gates

Nó có thể là bất kỳ hình dạng không thể đo lường như tôi đã nói trong câu đầu tiên - vòng tròn và trực tràng ở đây chỉ là một ví dụ thử nghiệm.
Lumis

@Lumis bạn thực sự nên xem xét b-splines, đó là những gì họ làm. Bất kỳ lý do để thử thực hiện giải pháp của riêng bạn?
GriffinHeart

1
Lớp đường dẫn tốt sẽ xây dựng các đường cong đó bằng các spline để bạn đã sử dụng nó. Tôi có một đề xuất khác, ít định hướng toán học hơn: thay vì lưu điểm, lưu đầu vào của người dùng (mẫu lệnh) và phát lại nó để xây dựng cùng một "hình ảnh".
GriffinHeart

Câu trả lời:


6

Tôi nghĩ bạn có hai vấn đề:

Điểm kiểm soát không đối xứng

Ban đầu bạn bắt đầu với khoảng cách bằng nhau giữa p0 đến p1 và p1 đến p2. Nếu góc dung sai giữa các đoạn đường không được đáp ứng, bạn di chuyển p1 và p2 về phía trước, nhưng giữ p0 ở vị trí cũ. Điều này làm tăng khoảng cách giữa p0 đến p1 trong khi vẫn giữ khoảng cách giữa p1 đến p2 như nhau. Khi bạn tạo một đường cong sử dụng p1 làm điểm kiểm soát, nó có thể bị sai lệch nhiều so với p2 tùy thuộc vào số lần lặp đã qua kể từ đường cong cuối cùng. Nếu bạn di chuyển p2 gấp đôi số tiền so với p1, bạn sẽ nhận được khoảng cách chẵn giữa các điểm.

Đường cong bậc hai

Như đã đề cập trong các câu trả lời khác, đường cong bậc hai không tốt cho trường hợp này. Các đường cong liền kề bạn tạo nên chia sẻ điểm kiểm soát và tiếp tuyến . Khi dữ liệu đầu vào của bạn chỉ là điểm, Catmull-Rom Spline là một lựa chọn tốt cho mục đích đó. Đó là một đường cong Hermite hình khối, trong đó các tiếp tuyến cho các điểm kiểm soát được tính từ các điểm trước và điểm tiếp theo.

API Path trong Android hỗ trợ các đường cong Bézier, khác một chút so với các đường cong Hermite liên quan đến các tham số. May mắn thay, đường cong Hermite có thể được chuyển đổi thành đường cong Bézier. Đây là mã ví dụ đầu tiên tôi tìm thấy khi Google. Câu trả lời Stackoverflow này dường như cũng đưa ra công thức.

Bạn cũng đề cập đến vấn đề của các cạnh sắc nét. Với dữ liệu đầu vào bạn có, không thể phát hiện nếu có một góc nhọn thực sự hay chỉ là một đường cong rất dốc. Nếu điều này trở thành một vấn đề, bạn có thể làm cho phép lặp thích ứng hơn bằng cách tăng / giảm bước khi cần thiết.

Chỉnh sửa: Sau khi suy nghĩ thêm các đường cong bậc hai có thể được sử dụng sau khi tất cả. Thay vì vẽ đường cong bậc hai từ p0 đến p2 bằng cách sử dụng p1 làm điểm kiểm soát, hãy vẽ đường cong từ p0 đến p1 bằng cách sử dụng điểm mới p0_1 làm điểm kiểm soát. Xem hình dưới đây. Điểm kiểm soát mới

Nếu p0_1 nằm trong giao điểm của các tiếp tuyến trong p0 và p1, kết quả sẽ trơn tru. Thậm chí tốt hơn, vì PathMeasure.getPosTan()trả về cũng tiếp tuyến là tham số thứ ba, bạn có thể sử dụng tiếp tuyến chính xác thực tế thay vì xấp xỉ từ các điểm liền kề. Với phương pháp này, bạn cần ít thay đổi hơn cho giải pháp hiện tại của mình.

Dựa trên câu trả lời này , điểm giao nhau có thể được tính theo công thức sau:

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;

Tuy nhiên, giải pháp này chỉ hoạt động nếu cả u và v đều không âm. Xem hình thứ hai: Tia không giao nhau

Ở đây các tia không giao nhau mặc dù các đường sẽ, vì u là âm. Trong trường hợp này, không thể vẽ một đường cong bậc hai có thể kết nối trơn tru với đường cong trước. Ở đây bạn cần các đường cong bézier. Bạn có thể tính toán các điểm kiểm soát cho nó bằng phương pháp được đưa ra trước đó trong câu trả lời này hoặc lấy chúng trực tiếp từ các tiếp tuyến. Chiếu p0 tới tia tiếp tuyến p0 + u * t0 và ngược lại cho các tia khác cho cả hai điểm điều khiển c0 và c1. Bạn cũng có thể điều chỉnh đường cong bằng cách sử dụng bất kỳ điểm nào giữa p0 và c0 thay vì c0 miễn là nó nằm trên tia tiếp tuyến.

Edit2: Nếu vị trí vẽ của bạn ở p1, bạn có thể tính các điểm điều khiển bezier thành p2 với mã giả sau:

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

Với những điều này, bạn có thể nối một đường dẫn từ p1 đến p2:

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

Thay thế các hoạt động vectơ bằng các hoạt động trên mỗi thành phần trên mảng float [ 2 ] để khớp với mã của bạn. Bạn bắt đầu bằng cách khởi tạo p1 = start;và p2 và p3 là những điểm tiếp theo. p0 ban đầu không được xác định. Đối với phân đoạn đầu tiên mà bạn chưa có p0, bạn có thể sử dụng đường cong bậc hai từ p1 đến p2 với cp2 làm điểm kiểm soát. Tương tự cho phần cuối của đường dẫn mà bạn không có p3, bạn có thể vẽ đường cong bậc hai từ p1 đến p2 với cp1 làm điểm kiểm soát. Ngoài ra, bạn có thể khởi tạo p0 = p1 cho phân đoạn đầu tiên và p3 = p2 cho phân khúc cuối cùng. Sau mỗi phân đoạn, bạn thay đổi các giá trị p0 = p1; p1 = p2; and p2 = p3;khi di chuyển về phía trước.

Khi bạn đang lưu đường dẫn, bạn chỉ cần lưu tất cả các điểm p0 ... pN. Không cần lưu các điểm kiểm soát cp1 và cp2, vì chúng có thể được tính khi cần thiết.

Edit3: Vì dường như khó có được giá trị đầu vào tốt cho việc tạo đường cong, tôi đề xuất một cách tiếp cận khác: Sử dụng tuần tự hóa. Android Path dường như không hỗ trợ nó, nhưng may mắn thay, lớp Vùng có. Xem câu trả lời này cho mã. Điều này sẽ cung cấp cho bạn kết quả chính xác. Nó có thể mất một số không gian ở dạng tuần tự nếu nó không được tối ưu hóa, nhưng trong trường hợp đó nó sẽ nén rất tốt. Nén dễ dàng trong Android Java bằng GZIPOutputStream .


Điều đó nghe có vẻ hứa hẹn. Tuy nhiên, nó không phải là p0 mà là p1, p2, p3 được sử dụng, p0 chỉ để lưu trữ các điểm xác định mới khi chúng được tính toán và vì lợi ích của các đường thẳng, do đó chúng không được lấy mẫu mỗi bước. Bạn có thể giúp tôi làm thế nào để tính x, y cho các điểm kiểm soát mới không?
Lumis

Tôi có thể làm điều đó sau, nhưng trong khi đó hãy kiểm tra stackoverflow.com/questions/2931573/ . Với u và v bạn có thể có được điểm giao nhau.
msell

Cảm ơn bạn đã giúp đỡ, tôi muốn thử điều này, nhưng nó cần phải được viết bằng Java cho Android. Không có vector2 và t1 và p1, v.v. là các mảng float nên tôi không thể thực hiện bất kỳ thao tác trực tiếp nào trên chúng như t1 = -t1 hoặc u * t0. Tôi giả sử rằng t1 = -t1 có nghĩa là t1.x = -t1x; t1.y = -t1.y v.v., phải không?
Lumis

Vâng, đó chỉ là mã giả để làm cho nó nhỏ gọn hơn và dễ đọc hơn.
msell

Vâng, cốt truyện đang dày lên. Bởi vì giao điểm khu vực của hai đường dẫn trong Android trả về một đường dẫn KHÔNG bị khử răng cưa, các tiếp tuyến nằm ở vị trí đó. Vì vậy, giải pháp thích hợp sẽ là lái một số đường cong trơn tru qua các điểm đã cho trước rồi lấy mẫu. Mã của bạn hoạt động hoàn toàn tốt trên một đường dẫn khử răng cưa, nó tạo ra các điểm kiểm soát thích hợp.
Lumis

13

W3C sẽ làm gì?

Internet đã có vấn đề này. Các World Wide Web Consortium chú ý. Nó có một giải pháp tiêu chuẩn được đề xuất từ năm 1999: Đồ họa vectơ có thể mở rộng (SVG) . Đây là định dạng tệp dựa trên XML được thiết kế đặc biệt để lưu trữ các hình dạng 2D.

" Có thể mở rộng-cái gì? "

Đồ họa Vector có thể mở rộng !

  • Có thể mở rộng : Có nghĩa là mở rộng quy mô trơn tru đến bất kỳ kích thước nào.
  • Vector : Nó dựa trên khái niệm toán học của vectơ .
  • Đồ họa . Nó có nghĩa là để làm cho hình ảnh.

Đây là thông số kỹ thuật cho phiên bản SVG 1.1.
(Đừng sợ cái tên đó; Thật sự rất dễ đọc.)

Họ đã viết chính xác cách lưu trữ các hình dạng cơ bản như hình tròn hoặc hình chữ nhật . Ví dụ, hình chữ nhật có các đặc tính này: x, y, width, height, rx, ry. (The rxrycó thể được sử dụng cho các góc tròn.)

Đây là hình chữ nhật mẫu của họ trong SVG: (Chà, hai thực sự - một cho phác thảo canvas.)

<?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>

Đây là những gì nó đại diện:

một hình chữ nhật màu vàng với đường viền màu xanh

Như thông số kỹ thuật nói, bạn có thể bỏ qua một số thuộc tính nếu bạn không cần chúng. (Ví dụ: rxrycác thuộc tính không được sử dụng ở đây.) Vâng, có một tấn hành trình ở đầu DOCTYPEmà bạn không cần chỉ cho trò chơi của mình. Chúng cũng là tùy chọn.

Đường dẫn

Đường dẫn SVG là "đường dẫn" theo nghĩa là nếu bạn đặt bút chì vào giấy, di chuyển nó xung quanh và cuối cùng nâng nó lên một lần nữa , bạn có một đường dẫn. Họ không cần phải đóng cửa , nhưng họ có thể.

Mỗi đường dẫn có một dthuộc tính (tôi muốn nghĩ rằng nó là viết tắt của "vẽ"), chứa dữ liệu đường dẫn , một chuỗi các lệnh về cơ bản chỉ cần đặt bút vào một tờ giấy và di chuyển nó xung quanh .

Họ đưa ra ví dụ về hình tam giác:

<?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>

một hình tam giác màu đỏ

Xem dthuộc tính trong path?

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

Các Mlà một lệnh cho Move to (tiếp theo tọa độ), các Ls là dành cho Line (với tọa độ) và zlà một lệnh để đóng đường dẫn (tức là vẽ một đường trở lại vị trí đầu tiên, đó không cần tọa độ).

Đường thẳng có nhàm chán? Sử dụng các lệnh Bézier hình khối hoặc bậc hai !

một số Béziers khối

Lý thuyết đằng sau các đường cong Bézier được đề cập tốt ở những nơi khác (chẳng hạn như trên Wikipedia ), nhưng đây là tóm tắt điều hành: Béziers có điểm bắt đầu và điểm kết thúc, có thể có nhiều điểm kiểm soát ảnh hưởng đến đường cong ở giữa.

truy tìm một Bézier bậc hai

Đặc tả cũng đưa ra các hướng dẫn để chuyển đổi hầu hết các hình dạng cơ bản thành các đường dẫn trong trường hợp bạn muốn.

Tại sao và khi nào nên sử dụng SVG

Quyết định cẩn thận nếu bạn muốn đi theo con đường này (ý định chơi chữ), bởi vì nó thực sự khá phức tạp để thể hiện bất kỳ hình dạng 2D tùy ý nào trong văn bản! Bạn có thể làm cho cuộc sống của mình dễ dàng hơn rất nhiều nếu bạn tự giới hạn mình chỉ bằng những con đường được tạo thành từ (có khả năng thực sự nhiều) đường thẳng.

Nhưng nếu bạn quyết định bạn muốn các hình dạng tùy ý, SVG là cách tốt nhất: Nó có hỗ trợ công cụ tuyệt vời: Bạn có thể tìm thấy nhiều thư viện để phân tích cú pháp XML ở cấp độ thấp và các công cụ soạn thảo SVG ở cấp độ cao.

Bất kể, tiêu chuẩn SVG là một ví dụ tốt.


Câu hỏi là về việc chuyển đổi một đường cong thành điểm, không lưu nó. Nhưng cảm ơn bạn đã tham khảo, thật tốt khi biết về tiêu chuẩn SVG.
Lumis

@Lumis Tiêu đề và nội dung sẽ đề nghị khác. Xem xét việc đọc lại câu hỏi. (Hoặc, bây giờ cái này đã được thiết lập, hỏi một cái khác.)
Anko

4

Mã của bạn chứa một nhận xét sai lệch:

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

Một đường cong bậc hai không đi qua điểm thứ hai. Nếu bạn muốn đi qua điểm thứ hai, bạn cần một loại đường cong khác, chẳng hạn như đường cong ẩn . Bạn có thể chuyển đổi các đường cong ẩn thành beziers để bạn có thể sử dụng lớp Path.

Một đề xuất khác là thay vì lấy mẫu các điểm, hãy sử dụng giá trị trung bình của các điểm bạn bỏ qua.

Một đề xuất khác là thay vì sử dụng một góc làm ngưỡng, hãy sử dụng sự khác biệt giữa đường cong thực tế và đường cong gần đúng. Angles không phải là vấn đề thực sự; vấn đề thực sự là khi tập hợp các điểm không khớp với đường cong bezier.

Một đề nghị khác là sử dụng các khối vuông, với tiếp tuyến của một khớp với tiếp tuyến của tiếp theo. Mặt khác (với tứ giác) Tôi nghĩ đường cong của bạn sẽ không khớp với nhau.


Bạn nói đúng, điểm thứ hai chỉ "kéo" đường cong về phía nó. Hình khối yêu cầu hai điểm điều khiển thay vì một điểm như hình tứ giác. Vấn đề là tất nhiên làm thế nào để có được điểm kiểm soát đúng. Lưu ý rằng tôi không muốn mất các góc nhọn vì Đường dẫn nguồn có thể là sự kết hợp của bất kỳ hình dạng thẳng hoặc tròn nào - về cơ bản tôi đang tạo một công cụ chọn hình ảnh nơi tôi có thể lưu đường dẫn đã chọn.
Lumis

4

Để có một giao lộ mượt mà hơn giữa hai đường dẫn, bạn có thể mở rộng chúng trước khi giao nhau và thu nhỏ chúng xuống sau.

Tôi không biết nếu đó là một giải pháp tốt, nhưng nó hoạt động tốt với tôi. Nó cũng nhanh thôi. Trong ví dụ của tôi, tôi giao một đường tròn với mẫu tôi đã tạo (sọc). Nó trông tốt ngay cả khi thu nhỏ.

Đây là mã của tôi:

    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);
}

nhập mô tả hình ảnh ở đây

Trông vẫn mượt mà khi phóng to với canvas.scale (): nhập mô tả hình ảnh ở đây


Cảm ơn những người đã dành cho tôi 10 danh tiếng để thêm hình ảnh :-)
user1344545

1
Thật đáng ngạc nhiên, thủ thuật đơn giản này giải quyết được hai vấn đề: thứ nhất là nó làm cho đường dẫn giao nhau hoặc kết hợp trở nên trơn tru và thứ hai là mã của tôi trong câu hỏi khi lấy mẫu đường dẫn có cùng tỷ lệ này tạo ra kết quả hoàn toàn trơn tru. Thật là một giải pháp bất ngờ và đơn giản, cảm ơn bạn!
Lumis

@user Chỉnh sửa là miễn phí. Đối với người dùng <2k-rep, đây thực sự là +2.
Anko

@Lumis Tôi hơi bối rối - Tôi nghĩ bạn hỏi làm thế nào để lưu trữ đường dẫn?
Anko

1
Thật không may, sau khi thử nghiệm nhiều hơn, tôi thấy rằng vì Vùng sử dụng các pixel mà đường dẫn sẽ chiếm khi được vẽ, ứng dụng dễ dàng hết bộ nhớ nếu tỷ lệ của Đường dẫn lớn và được thực hiện nhiều lần. Vì vậy, giải pháp này là hạn chế và rủi ro, nhưng tốt để ghi nhớ.
Lumis

3

Nhìn vào phép nội suy đa giác ( http://en.wikipedia.org/wiki/Polynomial_interpolation )

Về cơ bản, bạn có n nút không thể thay thế (nội suy tối ưu không phải là không thể thay thế, nhưng đối với trường hợp của bạn, nó phải đủ tốt và dễ thực hiện)

Bạn kết thúc với một đa giác có thứ tự n làm giảm lỗi giữa đường cong của bạn nếu (<- lớn nếu) đường của bạn đủ trơn tru .

Trong trường hợp của bạn, bạn đang thực hiện phép nội suy tuyến tính (thứ tự 1).

Một trường hợp khác (như khuyến cáo của GriffinHeart ) là sử dụng Splines ( http://en.wikipedia.org/wiki/Spline_interpolation )

Một trong hai trường hợp sẽ cung cấp cho bạn một số hình thức phù hợp đa thức cho đường cong của bạn.


2

Nếu điểm chuyển đổi chỉ dành cho lưu trữ và khi bạn hiển thị lại trên màn hình, bạn cần phải mượt mà, thì dung lượng lưu trữ có độ trung thực cao nhất bạn có thể nhận được, trong khi vẫn giảm thiểu tổng dung lượng cần thiết để duy trì một đường cong nhất định có thể để thực sự lưu trữ các thuộc tính của vòng tròn (hoặc một vòng cung, thay vào đó) và vẽ lại theo yêu cầu.

Gốc. Bán kính. Góc bắt đầu / dừng để vẽ vòng cung.

Nếu bạn cần chuyển đổi vòng tròn / vòng cung thành các điểm để hiển thị, thì bạn có thể có thể đến đó khi tải nó từ bộ lưu trữ, trong khi luôn chỉ lưu trữ các thuộc tính.


Đường dẫn / đường cong nguồn có thể là bất kỳ hình dạng nào, bao gồm cả hình vẽ một đường thẳng tự do. Tôi đã xem xét giải pháp đó sẽ phải lưu riêng từng thành phần và sau đó kết hợp chúng khi được tải nhưng nó đòi hỏi rất nhiều công việc và nó sẽ làm chậm thao tác của một đối tượng phức tạp như vậy vì mọi biến đổi sẽ phải được áp dụng cho từng các thành phần của nó để có thể lưu nó một lần nữa.
Lumis

2

Có một lý do cho việc đi cho các đường cong trái ngược với các đường thẳng? Các đường thẳng đơn giản hơn để làm việc và có thể được hiển thị hiệu quả trong phần cứng.

Cách tiếp cận khác đáng xem xét là lưu trữ một vài bit trên mỗi pixel, cho biết nếu nó ở bên trong, bên ngoài hoặc trên đường viền của hình dạng. Điều này sẽ nén tốt và có thể hiệu quả hơn các dòng cho các lựa chọn phức tạp.

Bạn cũng có thể thấy những bài viết này thú vị / hữu ích:


1

Hãy xem phép nội suy đường cong - có một vài loại khác nhau mà bạn có thể thực hiện sẽ giúp làm mịn đường cong của bạn. Càng nhiều điểm bạn có thể nhận được trên vòng tròn đó, thì càng tốt. Dung lượng lưu trữ khá rẻ - vì vậy, nếu trích xuất 360 nút đóng là đủ rẻ (thậm chí ở mức 8 byte cho vị trí; 360 nút khó có thể lưu trữ).

Bạn có thể đặt với một số mẫu nội suy ở đây chỉ với bốn điểm; và kết quả khá tốt (yêu thích của tôi là Bezier cho trường hợp này, mặc dù những người khác có thể kêu gọi về các giải pháp hiệu quả khác).

Bạn có thể chơi xung quanh đây , quá.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.