Çizilen çizginin düzlüğünü nasıl ölçebilirim?


37

Bir Android cihazının ekranında oyuncuların A noktasından (x1, y1) diğer noktaya B (x2, y2) kadar bir çizgi çekmesini gerektiren bir oyun üzerinde çalışıyorum.

Bu çizimin düz bir çizgiye ne kadar iyi uyduğunu bulmak istiyorum. Örneğin,% 90'lık bir sonuç, çizimin çizgiye neredeyse mükemmel şekilde uyacağı anlamına gelir. Oyuncular A'dan B'ye eğri bir çizgi çekerse, düşük bir puan almalıdır.

Bitiş noktaları önceden bilinmemektedir. Bunu nasıl yapabilirim?


1
İki bitiş noktanızın ne olduğunu önceden biliyor musunuz? Veya kullanıcı ekrana dokunmayı bıraktığı anda belirlenir mi?
Vaillancourt

Açıklamam senin için açık değilse üzgünüm. Eh, başlangıç ​​noktası A (x, y) ilk dokunuş ve bitiş noktası B (x, y) dediğiniz gibi dokunmatik ekrandan çıktığımız zamandır.
user3637362


3
Lütfen gelecekte kaynak kod için resim göndermeyin.
Josh

1
@ user3637362 Sana başlıyor anlıyoruz j=1karşılaştırabilirsiniz böylece touchList[j]ile touchList[j-1], ama ne zaman touch.phase == TouchPhase.Beganya da touch.phase == TouchPhase.Endedpozisyonları eklenmez touchListve sonradan dahil değildir sumLength. Bu hata her durumda mevcut olacak, ancak satır az sayıda segment içerdiğinde daha belirgin olacaktır.
Kelly Thomas

Yanıtlar:


52

Tam olarak düz bir çizgi, toplam uzunluğu ile mümkün olan en kısa çizgi olacaktır sqrt((x1-x2)² + (y1-y2)²). Daha çizilebilir bir çizgi daha az ideal bir bağlantı olacaktır ve dolayısıyla kaçınılmaz olarak daha uzun olacaktır.

Kullanıcının çizdiği yolun tüm bireysel puanlarını aldığınızda ve aralarındaki mesafeleri topladığınızda, toplam uzunluğu ideal uzunlukla karşılaştırabilirsiniz. İdeal uzunluk bölünen toplam uzunluk ne kadar küçük olursa, çizgi o kadar iyidir.

İşte bir görselleştirme. Siyah noktalar, hareketin bitiş noktaları ve mavi noktalar hareket sırasında ölçtüğünüz noktalar olduğunda, yeşil çizgilerin uzunluklarını hesaplayıp ekler ve kırmızı çizginin uzunluğuna bölersiniz:

görüntü tanımını buraya girin

1 puan veya sinuosity endeksi mükemmel olur, daha yüksek bir şey daha az mükemmel olur, 1'in altındaki bir hata olur. Skoru yüzde olarak almayı tercih ettiğinizde,% 100'ü bu sayıya bölün.


34
Bir vardır küçük eşit uzunlukta olduğu devamlı çizgiler, bu yaklaşımla ilgili problem 'düz' aynı değildir. Düz çizgi etrafında düşük sapma (ancak birçok kez) sallanan bir çizgi, tek bir noktaya ve sonra geriye sapan eşit uzunlukta bir çizgiden 'daha düzdür'.
Dancrumb

@Dancrumbs yeterince yorum yapamıyorum - bu, kullanıcı düz bir çizgi çiziyormuş gibi biraz sıkılaştırır, bu yüzden bu yaygın bir kullanım durumu gibi hissettirecek gibi bu yöntemle oldukça önemli bir sınırlama.
T. Kiley

@Dancrumb, çizgiden ortalama uzaklıktaki faktörü ya da "maksimum mesafedeki" faktörü, çizgiden herhangi bir noktadan alır. Daha sonra algoritmayı, daha küçük sapma genlikleri ile daha titrek çizgilerde ve beklenen yoldan sapan çizgilerden uzatabilirsiniz.
Superdoggy

2
@Dancrumb bana öyle geliyor ki, OP'nin kullanım durumu için bir fayda sağlayabilir. Çizilmiş çizgiler elbette küçük sapmalara sahip olacaktır. Bu yaklaşım aslında beklenen farklılıkların etkisini azaltmak için işe yarayabilir.

2
@ user3637362 kodunuzda bir hata var. Muhtemel bir açıklama, başlangıç ​​noktası ile ilk nokta veya bitiş noktası ile son nokta arasındaki mesafeyi hesaba katmayı unutmuş olmanızdır, ancak kodunuza bakmadan, hatanızın ne olabileceğini söylemek imkansızdır.
Philipp,

31

Bu bölümde ya uygulamak için en iyi yol olmayabilir, ama önermek RMSD (kök ortalama kare sapması), daha iyi sadece uzaktan yönteminden daha, Dancrumb bahsettiği durumlarda (Aşağıdaki ilk iki satır bakınız) olabilir.

RMSD = sqrt(mean(deviation^2))

Not:

  • Mutlak sapmaların (integral benzeri) toplamı, negatif olanlarla pozitif hataların ortalaması alınmadığından daha iyi olabilir. ( =sum(abs(deviation)))
  • Dikey çizgiden daha kısa mesafeler yaratan bir yol varsa, muhtemelen doğrusal çizgiye en kısa mesafeyi aramanız gerekecektir.

çizim

(Lütfen çizimimin kalitesinden dolayı özür dilerim)

Gördüğün gibi, zorundasın.

  1. çizginize dik bir vektör bulun ( nokta-çarpım 0'a eşittir ). Çizgileriniz istediğinize
    işaret ediyorsa (her birinin kökeni)(1, 3)(3, -1)
  2. hİdeal çizgiden kullanıcıya olan mesafeleri o vektöre paralel olarak ölçün .
  3. RMSD'yi veya mutlak farkların toplamını hesaplayın .

Joel Bosveld'in cevabı ilginç bir durumu gösteriyor: başlangıçta ve sonunda köşeleri olan neredeyse kusursuz bir çizgi. Satır kullanıcı tarafından serbestçe çizilirse, bu gerçekten bir sorundur. Bununla birlikte, bu yöntemin bu senaryoyu kapsayabileceğini düşünüyorum. Biri aslında en aza indirgenmiş bir değer olarak RMSD veya mutlak Integral ile uyum sağlayabilir . Başlangıç ​​değerleri başlangıç ​​ve bitiş noktaları olabilir. Uzunluk önemli olmadığı için, optimizasyonun noktaları hareket ettirip ideal çizginin daha uzağa ulaşması için veya daha kısa olması önemli değildir (yükseklik o niveauya göre hesaplanmalıdır).
gr4nt3d

1
Bunun bir başka durumdan kaynaklanmadığı görülüyor: Her ölçülen noktanın x ekseni üzerinde olduğunu söyleyin, ancak çizgi birkaç kez yönünü tersine çevirir. Bu 0 hatası verir.
dave mankoff

23

Mevcut cevaplar, son noktaların keyfi olduğunu (verilenlerden ziyade) dikkate almamaktadır. Bu nedenle, eğrinin düzlüğünü ölçerken, bitiş noktalarını kullanmak anlamsızdır (örneğin, beklenen uzunluğu, açıyı, konumu hesaplamak için). Basit bir örnek, her iki ucu da çentikli olan düz bir çizgi olacaktır. Eğriden uzaklığı ve bitiş noktaları arasındaki düz çizgiyi kullanarak ölçersek, bu çizilen düz çizgi bitiş noktaları arasındaki düz çizgiden ayrıldığı için oldukça büyük olacaktır.

Eğrinin ne kadar düz olduğunu nasıl söyleriz? Eğrinin yeterince pürüzsüz olduğunu varsayarak, eğrinin teğetinin ne kadar değiştiğini bilmek istiyoruz. Bir çizgi için, bu sıfır olacaktır (teğet sabittir).

T zamanındaki pozisyonun (x (t), y (t)) olmasına izin verirsek, o zaman teğet (Dx (t), Dy (t)) 'dir, burada Dx (t), t zamanındaki x'in türevidir. (bu site TeX desteği eksik görünüyor). Eğri yay uzunluğu ile parametrelenmemişse, || (Dx (t), Dy (t)) || ile bölünerek normalleşiriz. Dolayısıyla, t zamanında eğriye teğetin bir birim vektörüne (veya açısına) sahibiz. Yani, açı bir (t) = (Dx (t), Dy (t)) / || (Dx (t), Dy (t)) ||

Daha sonra eğri boyunca entegre edilen || Da (t) || ^ 2 ile ilgileniyoruz.

Eğri yerine kesin veri noktalarına sahip olduğumuz göz önüne alındığında, türevleri yaklaşık olarak belirlemek için sonlu farklar kullanmalıyız. Böylece, Da (t) olur (a(t+h)-a(t))/h. Ve, bir (t) olur ((x(t+h)-x(t))/h,(y(t+h)-y(t))/h)/||((x(t+h)-x(t))/h,(y(t+h)-y(t))/h)||. Daha sonra h||Da(t)||^2tüm veri noktaları için toplayarak S'yi alırız ve muhtemelen eğrinin uzunluğu ile normalleşiriz. Büyük olasılıkla kullanıyoruz h=1, ancak bu gerçekten sadece rastgele bir ölçek faktörü.

Tekrarlamak için, S bir satır için sıfır olacak ve bir satırdan saptıkça daha büyük olacaktır. İstenilen formata dönüştürmek için kullanın 1/(1+S). Ölçeğin keyfi olduğu göz önüne alındığında, belirli eğrilerin ne kadar düz olduğunu ayarlamak için S'yi bazı pozitif sayılarla çarpmak (veya başka bir şekilde dönüştürmek, örneğin S yerine bS ^ c kullanın) mümkündür.


2
Bu, düzlüğün en mantıklı tanımıdır.
Marcks Thomas

1
Bu şu ana kadar en mantıklı cevap ve diğerlerinin de çok sinir bozucu olacağına eminim. Ne yazık ki, çözümün sunulduğu form diğerlerinden biraz daha belirsiz, ancak OP'nin devam etmesini öneriyorum.
Dan Sheppard

Genellikle bu cevabın gerçekten de en iyisi olduğunu düşünüyorum. Bir sorun beni rahatsız etse de: Eğer çizgi "yeterince düzgün değilse" ne olur? Örneğin, açısı 90 derece olan iki tane düz çizgi kesimi varsa. Yanlış mıyım yoksa bu gerçekten düzgün bir doğrusal çizgiyle karşılaştırıldığında oldukça düşük bir sonuçla sonuçlanır mı? (Bence Dancrumb'un titrek hatlı kullanıcı davası da benzer bir problemdi) ... Yerel olarak bu en iyi yol olsa da eminim.
gr4nt3d

3

Bu bir ızgara tabanlı sistem, değil mi? Satır için kendi puanlarınızı bulun ve hattın eğimini hesaplayın. Şimdi, bu hesaplamayı kullanarak, kesin değerden bir miktar hata payı düşünüldüğünde, hattın geçeceği geçerli noktaları belirleyin.

Kısa bir deneme yanılma testi sayesinde, ne kadar iyi ve kötü eşleşme puanının bulunabileceğini belirleyin ve oyununuzu testinizden aynı sonuç için bir ölçek kullanarak ayarlayın.

Yani neredeyse yatay eğime sahip kısa bir çizginin içinden geçebileceğiniz 7 nokta olabilir. Düz çizginin bir parçası olduğu belirlenen 7'den 6 veya daha fazlasını tutarlı bir şekilde eşleştirebilirseniz, bu en yüksek puan olacaktır. Uzunluk ve doğruluk için puanlama, puanlamanın bir parçası olmalıdır.


3

Çok kolay ve sezgisel bir ölçüm en uygun düz çizgi ile gerçek eğri arasındaki alandır. Bunu belirlemek oldukça basittir:

  1. Tüm noktalara en küçük kareleri kullanın (bu, Joel Bosveld tarafından belirtilen son kırılma problemini önler).
  2. Eğrideki tüm noktalar için çizgiye olan mesafeyi belirleyin. Bu aynı zamanda standart bir sorundur. (doğrusal cebir, baz dönüşümü)
  3. Tüm mesafeleri toplayın.

Sizden bazı metin kodlamaları (JS, C #) veya sözde kodu sormamın sakıncası var, çünkü yukarıdaki cevapların çoğu teoride açıklandığı için nasıl başlayacağımı bilmiyorum.
user3637362

1
@ user3637362: StackOverflow'un pratik cevapları var: stackoverflow.com/questions/6195335/… stackoverflow.com/questions/849211/…
MSalters

2

Buradaki amaç, kullanıcının dokunduğu tüm noktaları tutmak, daha sonra kullanıcı ekranı bıraktığında bu noktaların her biri arasındaki mesafeyi değerlendirir ve toplar.

İşte sözde kodla başlamanızı sağlayacak bir şey:

bool mIsRecording = false;
point[] mTouchedPoints = new point[];

function onTouch
  mIsRecording = true

functon update
  if mIsRecording
    mTouchedPoints.append(currentlyTouchedLocation)

function onRelease
  mIsRecording = false

  cumulativeDistance = 0

  line = makeLine( mTouchedPoints.first, mTouchedPoints.last )

  for each point in mTouchedPoints:
    cumulativeDistance = distanceOfPointToLine(point, line)

  mTouchedPoints = new point[]

Nedir cumulativeDistancesize takılması ile ilgili bir fikir verebilir. 0 mesafesi, kullanıcının her zaman doğrudan çizgi üzerinde olduğu anlamına gelir. Şimdi, bağlamınızda nasıl davrandığını görmek için bazı testler yapmanız gerekecek. Ve distanceOfPointToLineçizgiden uzaktaki daha büyük mesafeleri cezalandırmak için karenin getirdiği değeri artırmak isteyebilirsiniz .

Birliğe aşina değilim, ancak buradaki kod updatebir onDragişlevde olabilir.

Kayıtlı sonuncuyla aynıysa, bir noktanın kaydedilmesini önlemek için bir yere bazı kodlar eklemek isteyebilirsiniz. Kullanıcı hareket etmediğinde bir şey kaydetmek istemezsiniz.


5
İdeal hat ile ölçülen her nokta arasındaki nokta arasındaki mesafeyi artırdığınızda, aldığınız önlem sayısını hesaba katmanız gerekir, aksi takdirde kullanıcı daha yavaş çekerse veya daha hızlı tarama hızına sahip bir cihaz kullandığında puanlar daha kötü bir puan alacağı anlamına gelir.
Philipp

@Philipp Evet yaparsınız! Bunu yapmanın benimkinden daha iyi göründüğünü itiraf etmeliyim: P
Vaillancourt

Bence bu yaklaşım kümülatif Direnç yerine ortalama mesafeyi alarak geliştirildi .
Dancrumb

@Dancrumb Gerçekten, ihtiyaçlara bağlı, ama evet, bunu yapmanın bir yolu olurdu.
Vaillancourt

2

Kullanabileceğiniz bir yöntem, çizgiyi bölümlere ayırmak ve segmenti temsil eden her vektör ile birinci ve son nokta arasında düz bir çizgiyi temsil eden bir vektör arasında bir vektör nokta ürünü yapmaktır. Bu, kolayca son derece "dikenli" segmentleri bulmanıza izin verme avantajına sahiptir.

Düzenle:

Ayrıca, nokta ürüne ek olarak segmentin uzunluğunu kullanmayı düşünürdüm. Çok kısa fakat dik bir vektör, sapma daha az olan uzun olandan daha az sayılmalıdır.


1

En kolay ve en hızlı olan, çizginin, kullanıcının çizdiği çizginin tüm noktalarını kapsayacak kadar kalın olması gerektiğini bulmak olabilir.

Satır ne kadar kalın olursa, kullanıcının çizgisini çizmede o kadar kötü olur.


0

Her nasılsa, MSalters Cevabına atıfta bulunmak, işte bazı daha özel bilgiler.

Puanlarınız için bir çizgiye uyması için en küçük kareler yöntemini kullanın. Temel olarak, en uygun olan y = f (x) fonksiyonunu arıyorsunuz. Sahip olduktan sonra, farklılıklar karesini toplamak için gerçek y değerlerini kullanabilirsiniz:

s = üstü ((yf (x)) ^ 2)

Toplam ne kadar küçükse, çizgi o kadar düz olur.

En iyi yaklaşım nasıl elde edilir, burada açıklanmaktadır: http://math.mit.edu/~gs/linearalgebra/ila0403.pdf

Sadece "Düz bir çizgi takma" dan okuyun. Y yerine x ve b yerine t kullanıldığını unutmayın. C ve D yaklaşık olarak belirlenecek, sonra f (x) = C + Dx

Ek Not: Açıkçası, hattın uzunluğunu da hesaba katmanız gerekir. 2 Noktadan oluşan her çizgi mükemmel olacaktır. İçeriği tam olarak bilmiyorum ama sanırım derecelendirme olarak puan sayısına bölünen karelerin toplamını kullanırdım. Ayrıca minimum uzunluk, minimum nokta sayısı gereksinimini de eklerdim. (Belki maksimum uzunluğun yaklaşık% 75'i)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.