Ketegangan pada Grafik, Bagian II: A Karet gelang

13

Ini adalah tantangan kedua dari dua "menarik fungsi tegang". Inilah Bagian I yang sedikit lebih sederhana .

Mari kita pindahkan kuku ke papan pada posisi (x 1 , y 1 ) ke (x m , y m ) . Ikatkan karet gelang ke yang pertama dan terakhir, dan regangkan di sekitar kuku lainnya, sehingga pita tersebut melintasi semua kuku secara berurutan. Perhatikan bahwa karet gelang sekarang menggambarkan fungsi parameterised linear piecewise (x (t), y (t)) dalam ruang 2D.

Sekarang drive n kuku lainnya ke papan, pada posisi (x 1 , y 1 ) ke (x n , y n ) . Jika sekarang kita menghapus semua asli m kuku kecuali yang pertama dan terakhir satu (yang ujung karet terikat), karet gelang akan mempersingkat sampai berbohong kencang di sekitar kuku baru, menghasilkan piecewise lain linear fungsi.

Sebagai contoh, ambil m = 12 paku awal pada posisi (0, 0), (2, -1), (3/2, 4/3), (7/2, 1/3), (11/2, 16/3), (1, 16/3), (0, 1), (7, -2), (3, 4), (8, 1), (3, -1), (11, 0) , dan n = 10 paku lebih lanjut pada posisi (1, 1), (3, 1), (4, 4), (1, 3), (2, 2), (5, -1), (5, 0) ), (6, 2), (7, 1), (6, 0) . Tiga plot berikut menunjukkan proses yang dijelaskan di atas:

masukkan deskripsi gambar di sini

Untuk versi yang lebih besar: Klik kanan -> Buka di tab baru

Dan inilah animasi pengetatan karet gelang jika Anda mengalami kesulitan memvisualisasikannya:

masukkan deskripsi gambar di sini

Tantangan

Diberi dua daftar "paku", plot karet gelang kencang di sekitar daftar kedua jika dimulai dari bentuk melintasi semua paku di daftar pertama.

Anda dapat menulis program atau fungsi dan mengambil input melalui STDIN, ARGV atau argumen fungsi. Anda dapat menampilkan hasilnya di layar atau menyimpan gambar ke file.

Jika hasilnya dirasterisasi, setidaknya perlu 300 piksel di setiap sisi. Karet gelang dan paku terakhir harus menutupi setidaknya 75% dari batas gambar horisontal dan vertikal. Skala panjang x dan y harus sama. Anda harus menunjukkan paku di set kedua (menggunakan setidaknya 3x3 piksel) dan string (setidaknya 1 piksel lebar). Anda mungkin atau mungkin tidak termasuk sumbu.

Warna adalah pilihan Anda, tetapi Anda membutuhkan setidaknya dua warna yang dapat dibedakan: satu untuk latar belakang dan satu untuk kuku dan tali (meskipun mungkin memiliki warna yang berbeda).

Anda dapat mengasumsikan bahwa semua paku pada daftar kedua setidaknya 10 -5 unit jauhnya dari bentuk awal karet gelang (sehingga Anda tidak perlu khawatir tentang ketidaktepatan titik mengambang).

Ini kode golf, jadi jawaban tersingkat (dalam byte) menang.

Lebih banyak contoh

Berikut adalah dua contoh lagi:

{{1, 1}, {3, 3}, {2, 4}, {1, 3}, {4, 0}, {3, -1}, {2, 0}, {4, 2}}
{{2, 1}, {3, 2}, {1, 2}, {4, 1}}

masukkan deskripsi gambar di sini

{{1, 1}, {3, 1}, {3, 3}, {1, 3}, {1, 5}, {3, 5}, {-1, 3}, {-1, 0}, {3, 4}, {5, 1}, {5, -1}, {7, -1}, {3, 7}, {7, 5}}
{{0, 0}, {0, 2}, {0, 4}, {0, 6}, {2, 0}, {2, 2}, {2, 4}, {2, 6}, {4, 0}, {4, 2}, {4, 4}, {4, 6}, {6, 0}, {6, 2}, {6, 4}, {6, 6}}

masukkan deskripsi gambar di sini

Dan di sini adalah salah satu contoh yang menunjukkan pentingnya dua kuku awal yang tersisa. Hasilnya harus b dan tidak a :

{{0, 0}, {0, 1}, {-1, 1}, {-1, -1}, {1, -1}, {1, 0}}
{{-0.5, 0.5}}

masukkan deskripsi gambar di sini

Terima kasih kepada Ell karena memberikan contoh ini.

Martin Ender
sumber
@laurencevs String satu bernilai tunggal, yang menyederhanakan banyak hal, karena ada arah yang jelas untuk memproses fungsi dan paku. Yang ini mungkin berisi loop dan zigzag, dan bentuk fungsi sangat berbeda (dan variabel), yang berarti bahwa solusinya harus sangat berbeda.
Martin Ender
Apa hasil dari ini ?
Ell
@ Ell Ah, test case yang sangat bagus. Saya kira untuk konsistensi, harus b , tapi saya benar-benar perlu mengklarifikasi pertanyaan tentang itu. Akan segera melakukannya. Terima kasih!
Martin Ender

Jawaban:

11

Python + matplotlib, 688

from pylab import*
C=cross
P,M=eval("map(array,input()),"*2)
P,N=[[P[0]]+L+[P[-1]]for L in P,M]
W=[.5]*len(P)
def T(a,c,b):
 I=[(H[0]**2,id(n),n)for n in N for H in[(C(n-a,b-a),C(n-b,c-b),C(n-c,a-c))]if(min(H)*max(H)>=0)*H[1]*H[2]]
 if I:d=max(I)[2];A=T(a,c,d);B=T(d,c,b);return[A[0]+[d]+B[0],A[1]+[sign(C(c-a,b-c))]+B[1]]
 return[[]]*2
try:
 while 1:
    i=[w%1*2or w==0for w in W[2:-2]].index(1);u,a,c,b,v=P[i:i+5];P[i+2:i+3],W[i+2:i+3]=t,_=T(a,c,b);t=[a]+t+[b]
    for p,q,j,k in(u,a,1,i+1),(v,b,-2,i+len(t)):x=C(q-p,c-q);y=C(q-p,t[j]-q);z=C(c-q,t[j]-q);d=sign(j*z);W[k]+=(x*y<=0)*(x*z<0 or y*z>0)*(x!=0 or d*W[k]<=0)*(y!=0 or d*W[k]>=0)*d
except:plot(*zip(*P))
if M:scatter(*zip(*M))
show()

Membaca dua daftar poin dari STDIN.

Contoh

[(0, 0), (2, -1), (3.0/2, 4.0/3), (7.0/2, 1.0/3), (11.0/2, 16.0/3), (1, 16.0/3), (0, 1), (7, -2), (3, 4), (8, 1), (3, -1), (11, 0)]
[(1, 1), (3, 1), (4, 4), (1, 3), (2, 2), (5, -1), (5, 0), (6, 2), (7, 1), (6, 0)]

Gambar 1

Bagaimana itu bekerja

Kunci dari solusinya adalah bekerja dalam langkah-langkah kecil dan bertahap. Alih-alih mencoba mencari tahu apa yang terjadi ketika kita menghapus semua kuku sekaligus, kita berkonsentrasi pada efek langsung dari menghilangkan hanya satu kuku. Kami kemudian dapat menghapus kuku satu per satu dalam urutan acak.

Bekerja secara bertahap berarti bahwa kita harus melacak keadaan antara karet gelang. Inilah bagian yang sulit: hanya melacak kuku mana yang dilalui band tidak cukup. Selama proses menghilangkan kuku, pita dapat mengalami luka dan kemudian terlepas di sekitar kuku. Oleh karena itu, ketika pita berinteraksi dengan paku, kita harus melacak berapa kali dan ke arah mana pita itu membungkusnya. Kami melakukannya dengan memberikan nilai pada setiap paku di sepanjang pita seperti berikut:

Gambar 2

Perhatikan bahwa:

  • Kami mulai menghitung segera setelah pita bersinggungan dengan kuku, meskipun kuku tidak benar-benar memengaruhi bentuknya. Ingatlah bahwa, tidak seperti ilustrasi itu, kuku kita tidak berdimensi. Karena itu, jika pita menjadi bersinggungan dengan paku, kami tidak dapat menentukan sisi pita mana yang digunakan oleh paku itu sendiri --- kami harus melacak di mana dulu relatif terhadap pita.

  • Kami menjaga kuku dengan nilai nol, yaitu kuku yang dulu, tetapi tidak lagi memegang pita, bukannya melepasnya segera. Jika kami melakukannya, itu dapat memicu reaksi berantai yang tidak disukai, sementara kami mencoba untuk menjaga efek dari setiap langkah terlokalisasi. Sebaliknya, paku dengan nilai nol dianggap memenuhi syarat untuk dihilangkan sebagai bagian dari proses yang lebih besar.

Sekarang kita dapat menggambarkan apa yang terjadi pada setiap langkah:

  • Kami memilih kuku untuk dihapus dari jalur band saat ini. Paku memenuhi syarat untuk dihapus jika paku itu merupakan bagian dari set kuku pertama (simpan untuk titik akhir) atau jika nilainya nol.

  • Berpura-pura bahwa dua kuku tetangga sudah diperbaiki, kami mencari tahu kuku mana dari set kedua atau sepasang titik akhir yang akan dilewati band setelah kuku yang dipilih dilepas (kami tidak repot-repot dengan sisa kuku dari kuku). set pertama, karena mereka akan akhirnya semua dihapus.) Kami melakukannya dengan cara yang sama dengan solusi untuk Bagian I . Semua paku ini berada di sisi yang sama dari pita, oleh karena itu kami menugaskan mereka semua nilai 1 atau -1 , tergantung pada sisi.

  • Kami memperbarui nilai kedua paku tetangga untuk mencerminkan perubahan pada jalur band (mudahnya bagian paling sulit!)

Proses ini diulangi sampai tidak ada lagi kuku yang perlu dihilangkan:

Gambar 3

Dan inilah contoh yang lebih rumit menggambarkan band yang membungkus paku beberapa kali:

Gambar 4

Elo
sumber
Luar biasa! Hanya satu hal: apakah grafik itu dirasterisasi atau apakah itu grafik vektor? Dalam kasus sebelumnya, saya harus mengarahkan Anda ke "Skala panjang x dan y harus sama." Juga, apa yang Anda gunakan untuk membuat semua grafik yang Anda gunakan dalam penjelasan Anda. matplotlib, juga?
Martin Ender
Terima kasih! Err ... Karena matplotlib mari saya skala plot dengan cepat Aku akan pergi dengan grafis vektor :) Untuk ilustrasi saya menggunakan sebagian besar GeoGebra . Agak aneh tapi menyelesaikan pekerjaan.
Ell
Ya oke, jika Anda dapat mengubah ukurannya secara sewenang-wenang, tidak apa-apa. Terima kasih untuk tautannya, saya akan memeriksanya!
Martin Ender
4

Java - 1262 byte

Saya tahu ini mungkin tidak golf sebanyak itu.

Namun, sepertinya tidak ada orang lain yang melangkah ke piring dan menjawab pertanyaan ini, jadi saya akan melakukannya.

Pertama, kelas "T" - yang merupakan kelas titik - 57 byte

class T{double x,y;public T(double a,double b){x=a;y=b;}}

Dan kelas utama - 1205 byte

import java.awt.Color;import java.awt.Graphics;import java.util.*;import javax.swing.*;class Q extends JPanel{void d(List<T>a,T[]z){JFrame t=new JFrame();int m=0;int g=0;for(T v:a){if(v.x>m)m=(int)v.x;if(v.y>g)g=(int)v.y;}for(T v:z){if(v.x>m)m=(int)v.x;if(v.y>g)g=(int)v.y;}t.setSize(m+20,g+20);t.setVisible(true);t.getContentPane().add(this);double r=9;while(r>1){r=0;for(int i=0;i<a.size()-1;i+=2){T p1=a.get(i),p2=new T((p1.x+a.get(i+1).x)/2,(p1.y+a.get(i+1).y)/2);a.add(i+1,p2);if(y(p1,p2)>r){r=y(p1,p2);}}}double w=15;List<T>q=new ArrayList<T>();while(w>3.7){w=0;q.clear();for(int e=0;e<a.size()-2;e++){T p1=a.get(e),u=a.get(e+1),p3=a.get(e+2),p2=new T((p1.x+p3.x)/2,(p1.y+p3.y)/2);w+=y(u,p2);int k=0;if(y(p1,a.get(e+1))<.5){a.remove(e);}for(T n:z){if(y(n,p2)<1){k=1;q.add(n);}}if(k==0){a.set(e+1,p2);}}}q.add(a.get(a.size()-1));q.add(1,a.get(0));p=z;o=q.toArray(new T[q.size()]);repaint();}T[]p;T[]o;double y(T a,T b){return Math.sqrt(Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));}public void paintComponent(Graphics g){if(o!=null){for(int i=0;i<o.length-1;i++){g.drawLine((int)o[i].x,(int)o[i].y,(int)o[i+1].x,(int)o[i+1].y);}g.setColor(Color.blue);for(T i:p){g.fillOval((int)i.x-3,(int)i.y-3,6,6);}}}}

Contoh:

masukkan deskripsi gambar di sini

Untuk menjalankannya, panggil fungsi "d" dengan Daftar poin dan deretan paku (ya, saya tahu, aneh). Apa fungsinya:

  • membuat daftar titik yang mewakili garis - yaitu, semua titik di antara garis.
  • mengulangi suatu algoritma berulang kali ke titik-titik ini sehingga setiap titik adalah rata-rata dari dua titik di sekitarnya.
  • Ketika poin-poin itu tampaknya tidak bergerak lagi, saya menggambar di antara kuku yang mereka sentuh.

Saya tidak yakin apakah sumbu dalam piksel ok. Itu akan selalu mengambil lebih dari 75% dari ruang, mungkin hanya sangat, sangat kecil.

Berikut ini adalah animasi yang bagus untuk menunjukkan apa yang saya lakukan di sini:

masukkan deskripsi gambar di sini

Akhirnya, ini menjadi ini, di mana poin hampir tidak bergerak. Inilah saat saya melihat kuku yang disentuhnya:

masukkan deskripsi gambar di sini

Berikut adalah kode animasi yang tidak disatukan:

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Q extends JPanel{
    List<Point>points=new ArrayList<Point>();
    List<Point>n=new ArrayList<Point>();
    public Q() throws InterruptedException{
        double[][]rawPoints={{0, 0}, {2, -1}, {3/2, 4/3}, {7/2, 1/3}, {11/2, 16/3}, {1, 16/3}, {0, 1}, {7, -2}, {3, 4}, {8, 1}, {3, -1}, {11, 0}};
        double[][]rawNails={{1, 1}, {3, 1}, {4, 4}, {1, 3}, {2, 2}, {5, -1}, {5, 0}, {6, 2}, {7, 1}, {6, 0}};
        List<Point>p=new ArrayList<Point>(),nails = new ArrayList<Point>();
        double factor = 50;
        for(double[]rawP:rawPoints){p.add(new Point(rawP[0]*factor+100,rawP[1]*factor+100));}
        for(double[]rawN:rawNails){nails.add(new Point(rawN[0]*factor+100,rawN[1]*factor+100));}
        n=nails;
        JFrame frame=new JFrame();
        frame.setSize(700,500);
        frame.setVisible(true);
        frame.getContentPane().add(this);
        d(p,nails);
    }
    public static void main(String[]a) throws InterruptedException{
        new Q();
    }
    void d(List<Point>a,List<Point>nails) throws InterruptedException{
        //add midpoint every iteration until length of 1 is achieved
        //begin algorithm
        //stop points that are within a small amount of a nail
        double distance=20;
        while(distance>1){
            distance=0;
            for (int i=0;i<a.size()-1;i+=2){
                double fir=a.get(i).x;
                double sec=a.get(i).y;
                double c=(fir+a.get(i+1).x)/2;
                double d=(sec+a.get(i+1).y)/2;
                a.add(i+1,new Point(c,d));
                double dist=distBP(new Point(fir,sec),new Point(c,d));
                if(dist>distance){distance=dist;}
            }
        }
        for(Point p:a){a.set(a.indexOf(p), new Point(p.x,p.y));}
        //algorithm starts here:
        setEqual(a);
        repaint();
        invalidate();
        System.out.println(a);
        int count=0;
        while(true){
            count++;
            for(int index=0;index<a.size()-2;index++){
                double x2=(a.get(index).x+a.get(index+2).x)/2;
                double y2=(a.get(index).y+a.get(index+2).y)/2;
                int pointStable=0;
                if(distBP(a.get(index),a.get(index+1))<.5){a.remove(index);}
                for(Point n:nails){
                    if(distBP(n,new Point(x2,y2))<1){pointStable=1;}
                }
                if(pointStable==0){a.set(index+1, new Point(x2,y2));}
            }
            if(count%10==0){
            setEqual(a);
            invalidate();
            repaint();
            Thread.sleep(5);
            }
        }
        //System.out.println(a);
    }
    void setEqual(List<Point>a){
        points = new ArrayList<Point>();
        for(Point p:a){points.add(p);}
    }
    double distBP(Point a,Point b){
        return Math.sqrt(Math.pow(b.x-a.x, 2)+Math.pow(b.y-a.y, 2));
    }
    @Override
    public void paintComponent(Graphics g){
        g.setColor(Color.white);
        g.fillRect(0,0,getWidth(),getHeight());
        g.setColor(Color.black);
        for(Point p:points){
            g.drawRect((int)p.x, (int)p.y, 1, 1);
        }
        for(Point nail:n){
            g.drawOval((int)nail.x-2, (int)nail.y-2, 4, 4);
        }
    }
}
Stretch Maniac
sumber
7
Nama pengguna Anda sangat cocok untuk masalah ini.
phosgene
Ell telah memberikan kasus tepi yang menarik yang tidak saya pikirkan. Saya telah mengklarifikasi spesifikasi dan menambahkan contoh itu. Bagaimana kinerja kode Anda pada contoh itu? Karena ini diklarifikasi setelah posting Anda, Anda tidak berkewajiban untuk memperbaiki kode Anda, jika itu tidak sesuai dengan spesifikasi yang diperbarui, tapi saya pikir saya akan memberi tahu Anda.
Martin Ender
Saya memperkenalkan beberapa perubahan untuk memperbaikinya, tetapi ternyata ada bug di program saya (jika Anda mencoba memasukkannya sebagai contoh terakhir, itu hanya menunjukkan satu baris). Saya akan berusaha memperbaikinya.
Stretch Maniac