Temukan Centroid dari Poligon

16

Dari Wikipedia :

Centroid dari poligon tertutup yang tidak memotong sendiri didefinisikan oleh n simpul ( x 0 , y 0 ), ( x 1 , y 1 ), ..., ( x n - 1 , y n − 1 ) adalah titik ( C x , C y ), di mana

Formula untuk Centroid

dan di mana A adalah area yang ditandatangani poligon,

Formula untuk Area Poligon

Dalam rumus-rumus ini, simpul diasumsikan diberi nomor sesuai urutan terjadinya sepanjang garis poligon. Lebih jauh, simpul ( x n , y n ) diasumsikan sama dengan ( x 0 , y 0 ), artinya i + 1 pada kasus terakhir harus di-loop ke i = 0 . Perhatikan bahwa jika titik-titiknya diberi nomor searah jarum jam, area A , dihitung seperti di atas, akan memiliki tanda negatif; tetapi koordinat centroid akan benar bahkan dalam kasus ini.


  • Diberikan daftar simpul secara berurutan (baik searah jarum jam, atau berlawanan arah jarum jam), cari pusat massa dari poligon tertutup yang tidak memotong sendiri yang diwakili oleh simpul tersebut.
    • Jika ini membantu, Anda dapat menganggap input hanya CW, atau hanya CCW. Katakan demikian dalam jawaban Anda jika Anda memerlukan ini.
  • Koordinat tidak harus berupa bilangan bulat, dan dapat berisi angka negatif.
  • Input akan selalu valid dan mengandung setidaknya tiga simpul.
  • Input hanya perlu ditangani yang sesuai dengan tipe data floating point asli bahasa Anda.
  • Anda dapat berasumsi bahwa angka input akan selalu mengandung titik desimal.
  • Anda dapat mengasumsikan bahwa bilangan bulat input diakhiri dengan .atau .0.
  • Anda dapat menggunakan angka kompleks untuk input.
  • Output harus akurat hingga seperseribu terdekat.

Contohnya

[(0.,0.), (1.,0.), (1.,1.), (0.,1.)]        -> (0.5, 0.5)
[(-15.21,0.8), (10.1,-0.3), (-0.07,23.55)]  -> -1.727 8.017
[(-39.00,-55.94), (-56.08,-4.73), (-72.64,12.12), (-31.04,53.58), (-30.36,28.29), (17.96,59.17), (0.00,0.00), (10.00,0.00), (20.00,0.00), (148.63,114.32), (8.06,-41.04), (-41.25,34.43)]   -> 5.80104769975, 15.0673812762

Lihat juga setiap poligon pada bidang koordinat, tempelkan koordinat tanpa tanda kurung siku di menu "Edit" halaman ini .

Saya mengkonfirmasi hasil saya menggunakan Polygon Centroid Point Calculator ini , yang mengerikan. Saya tidak dapat menemukan satu yang dapat Anda masukkan semua simpul sekaligus, atau yang tidak mencoba menghapus -tanda Anda saat Anda mengetiknya terlebih dahulu. Saya akan memposting solusi Python saya untuk Anda gunakan setelah orang memiliki kesempatan untuk menjawab.

mbomb007
sumber
Teknik yang jauh lebih sederhana dari rata-rata semua karya x dan y untuk dua set pertama, tetapi tidak untuk yang ketiga. Saya ingin tahu apa yang membuat perbedaan ...
ETHproduk
1
@ ETHproductions Poligon ketiga bukan cembung.
JungHwan Min
1
@ ETHproductions Jika Anda memperkirakan lingkaran dengan poligon, Anda dapat memindahkan titik rata-rata mendekati titik pada lingkaran dengan menggunakan lebih banyak titik dekat dengan titik itu, sementara hampir tidak mempengaruhi centroid dan menjaga poligon cembung.
Christian Sievers
2
@ ETHproductions Sebenarnya cembung bukan alasannya. Rata-rata semua xs dan ys menempatkan semua bobot dalam simpul alih-alih didistribusikan ke seluruh tubuh. Yang pertama berhasil karena itu teratur, sehingga kedua metode berakhir di pusat simetri. Yang kedua berfungsi karena untuk segitiga kedua metode mengarah ke titik yang sama.
Ton Hospel
1
Bisakah kita menggunakan angka kompleks untuk I / O?
xnor

Jawaban:

16

Jelly , 25 24 22 21 18 byte

S×3÷@×"
ṙ-żµÆḊçS€S

Terapkan rumus yang ditunjukkan dalam masalah.

Disimpan 3 byte dengan bantuan dari @ Jonathan Allan.

Cobalah online! atau Verifikasi semua kasus uji.

Penjelasan

S×3÷@×"  Helper link. Input: determinants on LHS, sum of pairs on RHS
S        Sum the determinants
 ×3      Multiply by 3
     ×"  Vectorized multiply between determinants and sums
   ÷@    Divide that by the determinant sum multipled by 3 and return

ṙ-żµÆḊçS€S  Main link. Input: 2d list of points
ṙ-          Rotate the list of points by 1 to the right
  ż         Interleave those with the original points
            This creates all overlapping slices of length 2
   µ        Start new monadic chain
    ÆḊ      Get the determinant of each slice
       S€   Get the sum of each slice (sum of pairs of points)
      ç     Call the helper link
         S  Sum and return
mil
sumber
Anda dapat mengganti ṁL‘$ṡ2dengan ṙ1ż@ataużṙ1$
Jonathan Allan
@ JonathanAllan Terima kasih, saya juga dapat memutar ṙ-żuntuk menghindari swap dan menyimpan byte lain
mil
Oh ya tentu saja!
Jonathan Allan
17

Mathematica, 23 byte

RegionCentroid@*Polygon

Ambil ITU , Jelly!

Sunting: Seseorang tidak hanya mengalahkan Jelly ...

Penjelasan

Polygon

Hasilkan poligon dengan simpul pada titik yang ditentukan.

RegionCentroid

Temukan centroid poligon.

JungHwan Min
sumber
2
Baik Anda mengalahkan saya, tetapi mungkin ada cara yang lebih pendek dari apa yang saya miliki, saya belum memiliki pemahaman yang lengkap tentang Jelly
mil
3
@miles aw ... :(
JungHwan Min
4

J, 29 byte

2+/@(+/\(*%3*1#.])-/ .*\)],{.

Terapkan rumus yang ditunjukkan dalam masalah.

Pemakaian

   f =: 2+/@(+/\(*%3*1#.])-/ .*\)],{.
   f 0 0 , 1 0 , 1 1 ,: 0 1
0.5 0.5
   f _15.21 0.8 , 10.1 _0.3 ,: _0.07 23.55
_1.72667 8.01667
   f _39 _55.94 , _56.08 _4.73 , _72.64 12.12 , _31.04 53.58 , _30.36 28.29 , 17.96 59.17 , 0 0 , 10 0 , 20 0 , 148.63 114.32 , 8.06 _41.04 ,: _41.25 34.43
5.80105 15.0674

Penjelasan

2+/@(+/\(*%3*1#.])-/ .*\)],{.  Input: 2d array of points P [[x1 y1] [x2 y2] ...]
                           {.  Head of P
                         ]     Get P
                          ,    Join, makes the end cycle back to the front
2                              The constant 2
2                      \       For each pair of points
                  -/ .*        Take the determinant
2    +/\                       Sum each pair of points
         *                     Multiply the sum of each pair by its determinant
          %                    Divide each by
             1#.]              The sum of the determinants
           3*                  Multiplied by 3
 +/@                           Sum and return
mil
sumber
4

Maxima, 124 118 116 112 112 106 byte

f(l):=(l:endcons(l[1],l),l:sum([3,l[i-1]+l[i]]*determinant(matrix(l[i-1],l[i])),i,2,length(l)),l[2]/l[1]);

Saya tidak berpengalaman dengan Maxima, jadi ada petunjuk yang diterima.

Pemakaian:

(%i6) f([[-15.21,0.8], [10.1,-0.3], [-0.07,23.55]]);
(%o6)              [- 1.726666666666668, 8.016666666666668]
Sievers Kristen
sumber
3

Racket 420 byte

(let*((lr list-ref)(getx(lambda(i)(lr(lr l i)0)))(gety(lambda(i)(lr(lr l i)1)))(n(length l))(j(λ(i)(if(= i(sub1 n))0(add1 i))))
(A(/(for/sum((i n))(-(*(getx i)(gety(j i)))(*(getx(j i))(gety i))))2))
(cx(/(for/sum((i n))(*(+(getx i)(getx(j i)))(-(*(getx i)(gety(j i)))(*(getx(j i))(gety i)))))(* 6 A)))
(cy(/(for/sum((i n))(*(+(gety i)(gety(j i)))(-(*(getx i)(gety(j i)))(*(getx(j i))(gety i)))))(* 6 A))))
(list cx cy))

Tidak Terkumpul:

(define(f l)
  (let* ((lr list-ref)
         (getx (lambda(i)(lr (lr l i)0)))
         (gety (lambda(i)(lr (lr l i)1)))
         (n (length l))
         (j (lambda(i) (if (= i (sub1 n)) 0 (add1 i))))
         (A (/(for/sum ((i n))
                (-(* (getx i) (gety (j i)))
                  (* (getx (j i)) (gety i))))
              2))
         (cx (/(for/sum ((i n))
                 (*(+(getx i)(getx (j i)))
                   (-(*(getx i)(gety (j i)))
                     (*(getx (j i))(gety i)))))
               (* 6 A)))
         (cy (/(for/sum ((i n))
                 (*(+(gety i)(gety (j i)))
                   (-(*(getx i)(gety (j i)))
                     (*(getx (j i))(gety i)))))
               (* 6 A))))
    (list cx cy)))

Pengujian:

(f '[(-15.21 0.8)  (10.1 -0.3)  (-0.07 23.55)] ) 
(f '[(-39.00 -55.94)  (-56.08 -4.73)  (-72.64 12.12)  (-31.04 53.58) 
     (-30.36 28.29)  (17.96 59.17)  (0.00 0.00)  (10.00 0.00)  
     (20.00 0.00) (148.63 114.32)  (8.06 -41.04)  (-41.25 34.43)])

Keluaran:

'(-1.7266666666666677 8.01666666666667)
'(5.8010476997538465 15.067381276150996)
juga
sumber
3

R, 129 127 byte

function(l){s=sapply;x=s(l,`[`,1);y=s(l,`[`,2);X=c(x[-1],x[1]);Y=c(y[-1],y[1]);p=x*Y-X*y;c(sum((x+X)*p),sum((y+Y)*p))/sum(p)/3}

Fungsi tanpa nama yang mengambil R-daftar tupel sebagai input. Setara yang dinamai dapat dipanggil menggunakan mis:

f(list(c(-15.21,0.8),c(10.1,-0.3),c(-0.07,23.55)))

Tidak diikat dan dijelaskan

f=function(l){s=sapply;                           # Alias for sapply
              x=s(l,`[`,1);                       # Split list of tuples into vector of first elements
              y=s(l,`[`,2);                       # =||= but for second element 
              X=c(x[-1],x[1]);                    # Generate a vector for x(i+1)
              Y=c(y[-1],y[1]);                    # Generate a vector for y(i+1)
              p=x*Y-X*y;                          # Calculate the outer product used in both A, Cx and Cy
              c(sum((x+X)*p),sum((y+Y)*p))/sum(p)/3    # See post for explanation
}

Langkah terakhir ( c(sum((x+X)*p),sum((y+Y)*p))/sum(p)*2/6) adalah cara vektor untuk menghitung keduanya Cxdan Cy. Jumlah dalam rumus untuk Cxdan Cydisimpan dalam vektor dan akibatnya dibagi dengan "jumlah A" *2/6. Misalnya:

(SUMinCx, SUMinCy) / SUMinA / 3

, dan kemudian dicetak secara implisit.

Cobalah R-biola

Billywob
sumber
*2/6mungkin bisa /3?
mbomb007
@ mbomb007 Jelas sekali, saya rasa saya terjebak bermain golf di bagian lain. /
shrug
Elegan, saya suka Anda menggunakan sapplyuntuk berurusan dengan daftar itu! Mungkin ada ruang untuk bermain golf di sini, saya tidak yakin seberapa fleksibel input yang diperbolehkan. Jika Anda diizinkan memasukkan hanya urutan koordinat, misalnya c(-15.21,0.8,10.1,-0.3,-0.07,23.55), maka Anda dapat menyimpan 17 byte dengan mengganti baris pertama dari fungsi Anda y=l[s<-seq(2,sum(1|l),2)];x=l[-s];. Yaitu, pengaturan yuntuk setiap elemen berindeks genap l, dan xmenjadi setiap elemen berindeks ganjil.
rturnbull
Akan lebih baik lagi, jika kita dapat memasukkan matriks (atau larik), seperti matrix(c(-15.21,0.8,10.1,-0.3,-0.07,23.55),2), saat awal fungsi Anda x=l[1,];y=l[2,];, yang menghemat 35 byte. (Matriks input dapat ditransposisikan, dalam hal ini x=l[,1];y=l[,2];.) Tentu saja, solusi termudah dari semua adalah jika xdan ytitik-titik hanya input sebagai vektor yang terpisah function(x,y),, tapi saya tidak berpikir itu diperbolehkan ...
rturnbull
@rturnbull Saya bertanya OP dalam komentar dan dia secara khusus menginginkan daftar tupel (sangat tidak nyaman dalam R tentu saja) jadi saya tidak berpikir pendekatan matriks diperbolehkan. Dan bahkan jika itu, input harus menjadi bagian vektor (yaitu c(...)) dan konversi matriks harus dilakukan di dalam fungsi.
Billywob
2

Python, 156 127 byte

def f(p):n=len(p);p=p+p[:1];i=s=0;exec'd=(p[i].conjugate()*p[i+1]).imag;s+=d;p[i]=(p[i]+p[i+1])*d;i+=1;'*n;print sum(p[:n])/s/3

Tidak Terkumpul:

def f(points):
  n = len(points)
  points = points + [points[0]]
  determinantSum = 0
  for i in range(n):
    determinant = (points[i].conjugate() * points[i+1]).imag
    determinantSum += determinant
    points[i] = (points[i] + points[i+1]) * determinant
  print sum(points[:n]) / determinantSum / 3

Ide itu.

Ini mengambil setiap pasangan poin [x, y]sebagai bilangan kompleks x + y*j, dan mengeluarkan centroid yang dihasilkan sebagai bilangan kompleks dalam format yang sama.

Untuk pasangan poin [a, b]dan [c, d], nilai a*d - b*cyang dibutuhkan untuk setiap pasangan poin dapat dihitung dari determinan matriks

| a b |
| c d |

Menggunakan aritmatika kompleks, nilai a + b*j- nilai kompleks dan c + d*jdapat digunakan sebagai

conjugate(a + b*j) * (c + d*j)
(a - b*j) * (c + d*j)
(a*c + b*d) + (a*d - b*c)*j

Perhatikan bahwa bagian imajiner setara dengan determinan. Selain itu, menggunakan nilai-nilai kompleks memungkinkan poin untuk dengan mudah dijumlahkan berdasarkan komponen dalam operasi lain.

mil
sumber
2

R + sp (46 byte)

spPaket diasumsikan diinstal ( https://cran.r-project.org/web/packages/sp/ )

Mengambil daftar simpul, (misalnya list(c(0.,0.), c(1.,0.), c(1.,1.), c(0.,1.)))

Mengambil keuntungan dari kenyataan bahwa "labpt" dari suatu Poligon adalah centroid.

function(l)sp::Polygon(do.call(rbind,l))@labpt
mnel
sumber
2

JavaScript (ES6), 102

Implementasi formula yang lurus

l=>[...l,l[0]].map(([x,y],i)=>(i?(a+=w=t*y-x*u,X+=(t+x)*w,Y+=(u+y)*w):X=Y=a=0,t=x,u=y))&&[X/3/a,Y/3/a]

Uji

f=
l=>[...l,l[0]].map(([x,y],i)=>(i?(a+=w=t*y-x*u,X+=(t+x)*w,Y+=(u+y)*w):X=Y=a=0,t=x,u=y))&&[X/3/a,Y/3/a]

function go()
{
  var c=[],cx,cy;
  // build coordinates array
  I.value.match(/-?[\d.]+/g).map((v,i)=>i&1?t[1]=+v:c.push(t=[+v]));
  console.log(c+''),
  [cx,cy]=f(c);
  O.textContent='CX:'+cx+' CY:'+cy;
  // try to display the polygon
  var mx=Math.max(...c.map(v=>v[0])),
    nx=Math.min(...c.map(v=>v[0])),
    my=Math.max(...c.map(v=>v[1])),
    ny=Math.min(...c.map(v=>v[1])),  
    dx=mx-nx, dy=my-ny,
    ctx=C.getContext("2d"),
    cw=C.width, ch=C.height,
    fx=(mx-nx)/cw, fy=(my-ny)/ch, fs=Math.max(fx,fy)
  C.width=cw
  ctx.setTransform(1,0,0,1,0,0);
  ctx.beginPath();
  c.forEach(([x,y],i)=>ctx.lineTo((x-nx)/fs,(y-ny)/fs));
  ctx.closePath();
  ctx.stroke();
  ctx.fillStyle='#ff0000';
  ctx.fillRect((cx-nx)/fs-2,(cy-ny)/fs-2,5,5);
}
go()
#I { width:90% }
#C { width:90%; height:200px;}
<input id=I value='[[-15.21,0.8], [10.1,-0.3], [-0.07,23.55]]'>
<button onclick='go()'>GO</button>
<pre id=O></pre>
<canvas id=C></canvas>

edc65
sumber
1

Python 2, 153 byte

Tidak menggunakan bilangan kompleks.

P=input()
A=x=y=0;n=len(P)
for i in range(n):m=-~i%n;a=P[i][0];b=P[i][1];c=P[m][0];d=P[m][1];t=a*d-b*c;A+=t;x+=t*(a+c);y+=t*(b+d)
k=1/(3*A);print x*k,y*k

Cobalah online

Tidak Terkumpul:

def centroid(P):
    A=x=y=0
    n=len(P)
    for i in range(n):
        m=-~i%n
        x0=P[i][0];y0=P[i][1]
        x1=P[m][0];y1=P[m][1]
        t = x0*y1 - y0*x1
        A += t/2.
        x += t * (x0 + x1)
        y += t * (y0 + y1)
    k = 1/(6*A)
    x *= k
    y *= k
    return x,y
mbomb007
sumber
1

Sebenarnya, 45 40 39 byte

Ini menggunakan algoritma yang mirip dengan jawaban mil Jelly . Ada cara yang lebih pendek untuk menghitung determinan menggunakan produk titik, tetapi saat ini ada bug dengan produk titik Sebenarnya di mana itu tidak akan bekerja dengan daftar pelampung. Saran golf diterima. Cobalah online!

;\Z♂#;`i¥`M@`i│N@F*)F@N*-`M;Σ3*)♀*┬♂Σ♀/

Tidak melakukanolf

         Implicit input pts.
;\       Duplicate pts, rotate right.
Z        Zip rot_pts and pts together.
♂#       Convert the iterables inside the zip to lists
         (currently necessary due to a bug with duplicate)
;        Duplicate the zip.
`...`M   Get the sum each pair of points in the zip.
  i        Flatten the pair to the stack.
  ¥        Pairwise add the two coordinate vectors.
@        Swap with the other zip.
`...`M   Get the determinants of the zip.
  i│       Flatten to stack and duplicate entire stack.
           Stack: [a,b], [c,d], [a,b], [c,d]
  N@F*)    Push b*c and move it to BOS.
  F@N*     Push a*d.
  -        Get a*d-b*c.
;Σ3*)    Push 3 * sum(determinants) and move it to BOS.
♀*       Vector multiply the determinants and the sums.
┬        Transpose the coordinate pairs in the vector.
♂Σ       Sum the x's, then the y's.
♀/       Divide the x and y of this last coordinate pair by 3*sum(determinants).
         Implicit return.

Versi yang lebih pendek dan tidak kompetitif

Ini adalah versi 24-byte lain yang menggunakan bilangan kompleks. Ini tidak kompetitif karena bergantung pada perbaikan bug yang menunda tantangan ini. Cobalah online!

;\│¥)Z`iá*╫@X`M;Σ3*)♀*Σ/

Tidak melakukanolf

         Implicit input a list of complex numbers, pts.
;\       Duplicate pts, rotate right.
│        Duplicate stack. Stack: rot_pts, pts, rot_pts, pts.
¥)       Pairwise sum the two lists of points together and rotate to BOS.
Z        Zip rot_pts and pts together.
`...`M   Map the following function over the zipped points to get our determinants.
  i        Flatten the list of [a+b*i, c+d*i].
  á        Push the complex conjugate of a+bi, i.e. a-b*i.
  *        Multiply a-b*i by c+d*i, getting (a*c+b*d)+(a*d-b*c)*i.
           Our determinant is the imaginary part of this result.
  ╫@X      Push Re(z), Im(z) to the stack, and immediately discard Re(z).
           This map returns a list of these determinants.
;        Duplicate list_determinants.
Σ3*)     Push 3 * sum(list_determinants) and rotate that to BOS.
♀*Σ      Pairwise multiply the sums of pairs of points and the determinants and sum.
/        Divide that sum by 3*sum(list_determinants).
         Implicit return.
Sherlock9
sumber
1

C ++ 14, 241 byte

struct P{float x;float y;};
#define S(N,T)auto N(P){return 0;}auto N(P a,P b,auto...V){return(T)*(a.x*b.y-b.x*a.y)+N(b,V...);}
S(A,1)S(X,a.x+b.x)S(Y,a.y+b.y)auto f(auto q,auto...p){auto a=A(q,p...,q)*3;return P{X(q,p...,q)/a,Y(q,p...,q)/a};}

Output adalah struct pembantu P,

Tidak Terkumpul:

 //helper struct
struct P{float x;float y;};

//Area, Cx and Cy are quite similar
#define S(N,T)\  //N is the function name, T is the term in the sum
auto N(P){return 0;} \   //end of recursion for only 1 element
auto N(P a,P b,auto...V){ \ //extract the first two elements
  return (T)*(a.x*b.y-b.x*a.y) //compute with a and b
         + N(b,V...); \        //recursion without first element
}

//instantiate the 3 formulas
S(A,1)
S(X,a.x+b.x)
S(Y,a.y+b.y)


auto f(auto q,auto...p){
  auto a=A(q,p...,q)*3; //q,p...,q appends the first element to the end
  return P{X(q,p...,q)/a,Y(q,p...,q)/a};
}

Pemakaian:

f(P{0.,0.}, P{1.,0.}, P{1.,1.}, P{0.,1.})
f(P{-15.21,0.8}, P{10.1,-0.3}, P{-0.07,23.55})
Karl Napf
sumber
1

Clojure, 177 156 143 bytes

Pembaruan: Alih-alih panggilan balik yang saya gunakan [a b c d 1]sebagai fungsi dan argumennya hanyalah daftar indeks untuk vektor ini. 1digunakan sebagai nilai sentinel saat menghitungA .

Pembaruan 2: Tidak dihitung Apada let, menggunakan (rest(cycle %))untuk mendapatkan vektor input diimbangi dengan satu.

#(let[F(fn[I](apply +(map(fn[[a b][c d]](*(apply +(map[a b c d 1]I))(-(* a d)(* c b))))%(rest(cycle %)))))](for[i[[0 2][1 3]]](/(F i)(F[4])3)))

Versi asli:

#(let[F(fn[L](apply +(map(fn[[a b][c d]](*(L[a b c d])(-(* a d)(* c b))))%(conj(subvec % 1)(% 0)))))A(*(F(fn[& l]1))3)](map F[(fn[v](/(+(v 0)(v 2))A))(fn[v](/(+(v 1)(v 3))A))]))

Pada tahap yang kurang golf:

(def f (fn[v](let[F (fn[l](apply +(map
                                    (fn[[a b][c d]](*(l a b c d)(-(* a d)(* c b))))
                                    v
                                    (conj(subvec v 1)(v 0)))))
                  A (* (F(fn[& l] 1)) 3)]
                [(F (fn[a b c d](/(+ a c)A)))
                 (F (fn[a b c d](/(+ b d)A)))])))

Menciptakan fungsi pembantu Fyang mengimplementasikan penjumlahan dengan panggilan balik apa pun l. Untuk Apanggilan balik kembali secara konstan 1sedangkan koordinat X dan Y memiliki fungsinya sendiri. (conj(subvec v 1)(v 0))menjatuhkan elemen pertama dan menambahkan sampai akhir, dengan cara ini mudah untuk melacak x_idan x_(i+1). Mungkin masih ada beberapa pengulangan yang harus dihilangkan, terutama pada akhirnya (map F[....

NikoNyrh
sumber