xkcd 스타일 설명 차트 만들기


45

더 상징적 인 xkcd 스트립 중 하나에서 Randall Munroe는 여러 영화의 타임 라인을 설명 차트로 시각화했습니다.

여기에 이미지 설명을 입력하십시오 (더 큰 버전을 보려면 클릭하십시오.)

출처 : xkcd No. 657 .

영화 (또는 다른 이야기)의 타임 라인을 지정하면 그러한 차트를 생성해야합니다. 이것은 인기 콘테스트이므로, 가장 많은 (순) 투표로 답변이 이길 것입니다.

최소 요구 사항들

사양을 약간 강화하기 위해 모든 답변이 구현해야하는 최소 기능 세트는 다음과 같습니다.

  • 문자 이름 목록과 이벤트 목록을 입력으로 사용하십시오. 각 이벤트는 죽어가는 문자 ​​목록 또는 문자 그룹 목록 (현재 함께있는 문자를 나타냄)입니다. Jurassic Park 내러티브를 인코딩하는 방법에 대한 예는 다음과 같습니다.

    ["T-Rex", "Raptor", "Raptor", "Raptor", "Malcolm", "Grant", "Sattler", "Gennaro",
     "Hammond", "Kids", "Muldoon", "Arnold", "Nedry", "Dilophosaurus"]
    [
      [[0],[1,2,3],[4],[5,6],[7,8,10,11,12],[9],[13]],
      [[0],[1,2,3],[4,7,5,6,8,9,10,11,12],[13]],
      [[0],[1,2,3],[4,7,5,6,8,9,10],[11,12],[13]],
      [[0],[1,2,3],[4,7,5,6,9],[8,10,11,12],[13]],
      [[0,4,7],[1,2,3],[5,9],[6,8,10,11],[12],[13]],
      [7],
      [[5,9],[0],[4,6,10],[1,2,3],[8,11],[12,13]],
      [12],
      [[0, 5, 9], [1, 2, 3], [4, 6, 10, 8, 11], [13]], 
      [[0], [5, 9], [1, 2], [3, 11], [4, 6, 10, 8], [13]], 
      [11], 
      [[0], [5, 9], [1, 2, 10], [3, 6], [4, 8], [13]], 
      [10], 
      [[0], [1, 2, 9], [5, 6], [3], [4, 8], [13]], 
      [[0], [1], [9, 5, 6], [3], [4, 8], [2], [13]], 
      [[0, 1, 9, 5, 6, 3], [4, 8], [2], [13]], 
      [1, 3], 
      [[0], [9, 5, 6, 3, 4, 8], [2], [13]]
    ]
    

    예를 들어 첫 번째 줄은 차트의 시작 부분에서 T-Rex가 고독하고, 3 개의 랩터가 함께 있고, 말콤이 혼자이며, Grant와 Sattler가 함께있는 것을 의미합니다. 마지막 두 번째 이벤트는 랩터 중 2 명이 죽는 것을 의미합니다 .

    이러한 종류의 정보를 지정할 수있는 한 입력이 사용자에게 얼마나 정확한지 결정하십시오. 예를 들어 편리한 목록 형식을 사용할 수 있습니다. 또한 이벤트의 문자가 다시 전체 문자 이름이 될 것으로 기대할 수 있습니다.

    각 그룹 목록에 정확히 하나의 그룹에 각 살아있는 캐릭터가 포함되어 있다고 가정 할 수도 있습니다. 그러나 한 이벤트 내의 그룹 또는 문자 가 특히 편리한 순서 라고 가정 해서는 안됩니다 .

  • 각 문자에 대해 한 줄이있는 차트 또는 화면 (벡터 또는 래스터 그래픽으로)으로 렌더링합니다. 각 줄은 줄의 시작 부분에 문자 이름으로 표시되어야합니다.

  • 각각의 정상적인 이벤트에 대해, 문자 그룹이 각 라인의 근접성에 의해 명확하게 닮은 차트의 일부 단면이 순서대로 있어야합니다.
  • 각 사망 이벤트에 대해 관련 캐릭터의 행은 눈에 보이는 얼룩으로 끝나야합니다.
  • 당신은 할 수 없습니다 랜달의 플롯의 다른 기능을 재현 할 필요가 없으며 자신의 그리기 스타일을 재현해야합니까. 추가 레이블이없는 날카로운 회전이있는 직선, 모두 검은 색으로 표시되며 제목은 경쟁에 완전히 적합합니다. 또한 공간을 효율적으로 사용할 필요가 없습니다. 예를 들어 눈에 띄는 시간 방향이있는 한 다른 문자와 만나기 위해 줄을 아래로만 움직여 알고리즘을 단순화 할 수 있습니다.

이러한 최소 요구 사항을 정확히 충족시키는 참조 솔루션 을 추가했습니다 .

예쁘게 만들기

이것은 인기 콘테스트이므로, 그 위에, 당신이 원하는 어떤 환상이든 구현할 수 있습니다. 가장 중요한 추가 사항은 차트를 더 읽기 쉽게 만드는 적절한 레이아웃 알고리즘입니다. 이것이이 도전의 핵심 알고리즘 문제입니다! 투표는 차트를 깔끔하게 유지하는 알고리즘의 성능을 결정합니다.

그러나 Randall의 차트를 기반으로 한 더 많은 아이디어가 있습니다.

장식물:

  • 컬러 라인.
  • 줄거리의 제목입니다.
  • 라벨링 라인이 종료됩니다.
  • 사용량이 많은 섹션을 통과 한 회선에 자동으로 레이블을 다시 지정합니다.
  • 선과 글꼴에 대해 손으로 그린 ​​스타일 (또는 내가 말했듯이 Randall의 스타일을 더 잘 생각하면 재현 할 필요가 없습니다).
  • 시간 축의 사용자 정의 가능한 방향.

추가 표현력 :

  • 명명 된 이벤트 / 그룹 / 죽음.
  • 라인이 사라지고 다시 나타납니다.
  • 늦게 등장하는 캐릭터.
  • 캐릭터의 속성을 나타내는 하이라이트 (예 : LotR 차트의 ringbearer 참조).
  • 그룹화 축에 추가 정보 인코딩 (예 : LotR 차트와 같은 지리 정보).
  • 시간 여행?
  • 대체 현실?
  • 다른 캐릭터로 변신하는 캐릭터?
  • 두 글자가 합쳐 지나요? (캐릭터 분할?)
  • 3D? ( 정말로 나아간 경우 실제로 추가 차원을 사용하여 Someting을 시각화하고 있는지 확인하십시오!)
  • 영화 (또는 서적 등)의 이야기를 시각화하는 데 유용한 기타 관련 기능.

물론이 중 많은 부분은 추가 입력이 필요하며 필요에 따라 입력 형식을 자유롭게 늘릴 수 있지만 데이터 입력 방법을 문서화하십시오.

구현 한 기능을 보여주기 위해 하나 또는 두 개의 예를 포함하십시오.

귀하의 솔루션은 유효한 입력을 처리 할 수 ​​있어야하지만, 다른 유형보다 특정 종류의 이야기에 더 적합한 경우에는 절대적으로 좋습니다.

투표 기준

사람들에게 표를 써야하는 방법을 알려줄 수 있다는 환상은 없지만 다음은 중요한 순서대로 제안 된 지침입니다.

  • 허점 답은 허점, 표준 또는 기타를 이용하거나 하나 이상의 결과를 하드 코딩합니다.
  • 최소한의 요구 사항을 충족시키지 못하는 답변을 공표하지 마십시오 (나머지가 아무리 멋진 지에 상관없이).
  • 우선, 멋진 레이아웃 알고리즘을 찬성하십시오. 여기에는 그래프의 가독성을 유지하기 위해 선의 교차를 최소화하면서 수직 공간을 많이 사용하지 않거나 추가 정보를 수직 축으로 인코딩하는 답이 포함됩니다. 큰 혼란을 일으키지 않고 그룹화를 시각화하는 것이이 과제의 주요 초점이되어야합니다. 그래야 흥미로운 알고리즘 문제를 염두에두고 프로그래밍 콘테스트를 유지할 수 있습니다.
  • 표현력을 추가하는 선택적 기능을 찬성하십시오 (예 : 순수한 장식이 아님).
  • 마지막으로 멋진 프리젠 테이션을 찬성하십시오.

7
코드 골프 충분 XKCD이 없기 때문에
자랑 haskeller

8
@proudhaskeller PPCG는 충분한 xkcd를 가질 수 없습니다. ;) 그러나 나는 우리가 그의 초대형 정보 그래픽 / 시각화에 도전을 시도하지 않았다고 생각합니다. 그래서 나는 이것으로 테이블에 새로운 것을 가져 오기를 바랍니다. 그리고 다른 사람들도 매우 다르고 흥미로운 도전을 할 것이라고 확신합니다.
Martin Ender

내 솔루션이 12 명의 화난 남자, Duel (Spielberg, 1971, 일반 운전자 대 미친 트럭 운전사) 및 비행기, 기차 및 자동차 만 처리해도 괜찮습니까? ;-)
Level River St

4
입문서에 대한 입력이 어떻게 생겼는지 궁금합니다.
Joshua Joshua

1
@ping 네, 그 아이디어였습니다. 이벤트에 추가 목록이 포함 된 경우 목록 그룹입니다. 그래서 [[x,y,z]]모든 문자가 함께 현재 것을 의미 할 것입니다. 그러나 이벤트에 목록이없고 문자 만 직접 포함되어있는 경우에도 사망에 해당하므로 동일한 상황 [x,y,z]에서이 세 문자가 죽는다는 의미입니다. 어떤 것이 사망인지 또는 그룹화 이벤트인지에 대한 명확한 표시와 함께 다른 형식을 자유롭게 사용하십시오. 위 형식은 제안 일뿐입니다. 입력 형식이 최소한 표현적인 한 다른 것을 사용할 수 있습니다.
Martin Ender

답변:


18

numpy, scipy 및 matplotlib가있는 Python3

쥬라기 공원

편집 :

  • 이벤트간에 그룹을 동일한 상대 위치에 유지하려고 시도했기 때문에 sorted_event기능이었습니다.
  • 문자의 y 위치를 계산하는 새로운 기능 ( coords).
  • 모든 살아있는 이벤트는 이제 두 번 그려 지므로 캐릭터가 더 잘 어울립니다.
  • 범례 레이블이 제거되고 축 레이블이 제거되었습니다.
import math
import numpy as np
from scipy.interpolate import interp1d
from matplotlib import cm, pyplot as plt


def sorted_event(prev, event):
    """ Returns a new sorted event, where the order of the groups is
    similar to the order in the previous event. """
    similarity = lambda a, b: len(set(a) & set(b)) - len(set(a) ^ set(b))
    most_similar = lambda g: max(prev, key=lambda pg: similarity(g, pg))
    return sorted(event, key=lambda g: prev.index(most_similar(g)))


def parse_data(chars, events):
    """ Turns the input data into 3 "tables":
    - characters: {character_id: character_name}
    - timelines: {character_id: [y0, y1, y2, ...],
    - deaths: {character_id: (x, y)}
    where x and y are the coordinates of a point in the xkcd like plot.
    """
    characters = dict(enumerate(chars))
    deaths = {}
    timelines = {char: [] for char in characters}

    def coords(character, event):
        for gi, group in enumerate(event):
            if character in group:
                ci = group.index(character)
                return (gi + 0.5 * ci / len(group)) / len(event)
        return None

    t = 0
    previous = events[0]
    for event in events:
        if isinstance(event[0], list):
            previous = event = sorted_event(previous, event)
            for character in [c for c in characters if c not in deaths]:
                timelines[character] += [coords(character, event)] * 2
            t += 2
        else:
            for char in set(event) - set(deaths):
                deaths[char] = (t-1, timelines[char][-1])

    return characters, timelines, deaths


def plot_data(chars, timelines, deaths):
    """ Draws a nice xkcd like movie timeline """

    plt.xkcd()  # because python :)

    fig = plt.figure(figsize=(16,8))
    ax = fig.add_subplot(111)
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    ax.set_xlim([0, max(map(len, timelines.values()))])

    color_floats = np.linspace(0, 1, len(chars))
    color_of = lambda char_id: cm.Accent(color_floats[char_id])

    for char_id in sorted(chars):
        y = timelines[char_id]
        f = interp1d(np.linspace(0, len(y)-1, len(y)), y, kind=5)
        x = np.linspace(0, len(y)-1, len(y)*10)
        ax.plot(x, f(x), c=color_of(char_id))

    x, y = zip(*(deaths[char_id] for char_id in sorted(deaths)))
    ax.scatter(x, y, c=np.array(list(map(color_of, sorted(deaths)))), 
               zorder=99, s=40)

    ax.legend(list(map(chars.get, sorted(chars))), loc='best', ncol=4)
    fig.savefig('testplot.png')


if __name__ == '__main__':
    chars = [
        "T-Rex","Raptor","Raptor","Raptor","Malcolm","Grant","Sattler",
        "Gennaro","Hammond","Kids","Muldoon","Arnold","Nedry","Dilophosaurus"
    ]
    events = [
        [[0],[1,2,3],[4],[5,6],[7,8,10,11,12],[9],[13]],
        [[0],[1,2,3],[4,7,5,6,8,9,10,11,12],[13]],
        [[0],[1,2,3],[4,7,5,6,8,9,10],[11,12],[13]],
        [[0],[1,2,3],[4,7,5,6,9],[8,10,11,12],[13]],
        [[0,4,7],[1,2,3],[5,9],[6,8,10,11],[12],[13]],
        [7],
        [[5,9],[0],[4,6,10],[1,2,3],[8,11],[12,13]],
        [12],
        [[0,5,9],[1,2,3],[4,6,10,8,11],[13]],
        [[0],[5,9],[1,2],[3,11],[4,6,10,8],[13]],
        [11],
        [[0],[5,9],[1,2,10],[3,6],[4,8],[13]],
        [10],
        [[0],[1,2,9],[5,6],[3],[4,8],[13]],
        [[0],[1],[9,5,6],[3],[4,8],[2],[13]],
        [[0,1,9,5,6,3],[4,8],[2],[13]],
        [1,3],
        [[0],[9,5,6,3,4,8],[2],[13]]
    ]
    plot_data(*parse_data(chars, events))

Hah, 아주 좋은 xkcd 모양 :) ... 선에 레이블을 지정할 수 있습니까?
Martin Ender

선에 레이블을 지정하고 선의 너비를 달리하고 (일부 점 사이에서 감소 / 증가) 마지막으로 ... 보간하는 동안 정점에 가까울 때 선을 더 수평으로 만듭니다. )
Optimizer

1
고맙지 만 xkcd 스타일은 matplotlib에 포함되어 있으므로 함수 호출 일뿐입니다.) 범례를 만들었지 만 이미지의 거의 3 분의 1을 차지했기 때문에 주석을 달았습니다.
pgy December

내 대답을 수정했는데 이제 더 좋아 보인다고 생각합니다.
pgy

6

T-SQL

나는 이것으로 출품작에 만족하지 않지만이 질문에 적어도 시도해 볼 가치가 있다고 생각합니다. 나중에 허용 하여이 개선을 시도하지만 레이블은 SQL에서 항상 문제가 될 것입니다. 이 솔루션에는 SQL 2012+가 필요하며 SSMS (SQL Server Management Studio)에서 실행됩니다. 결과는 공간 결과 탭에 있습니다.

-- Variables for the input
DECLARE @actors NVARCHAR(MAX) = '["T-Rex", "Raptor", "Raptor", "Raptor", "Malcolm", "Grant", "Sattler", "Gennaro", "Hammond", "Kids", "Muldoon", "Arnold", "Nedry", "Dilophosaurus"]';
DECLARE @timeline NVARCHAR(MAX) = '
[
   [[1], [2, 3, 4], [5], [6, 7], [8, 9, 11, 12, 13], [10], [14]],
   [[1], [2, 3, 4], [5, 8, 6, 7, 9, 10, 11, 12, 13], [14]],
   [[1], [2, 3, 4], [5, 8, 6, 7, 9, 10, 11], [12, 13], [14]],
   [[1], [2, 3, 4], [5, 8, 6, 7, 10], [9, 11, 12, 13], [14]],
   [[1, 5, 8], [2, 3, 4], [6, 10], [7, 9, 11, 12], [13], [14]],
   [8],
   [[6, 10], [1], [5, 7, 11], [2, 3, 4], [9, 12], [13, 14]],
   [13],
   [[1, 6, 10], [2, 3, 4], [5, 7, 11, 9, 12], [14]],
   [[1], [6, 10], [2, 3], [4, 12], [5, 7, 11, 9], [14]],
   [12],
   [[1], [6, 10], [2, 3, 11], [4, 7], [5, 9], [14]],
   [11],
   [[1], [2, 3, 10], [6, 7], [4], [5, 9], [14]],
   [[1], [2], [10, 6, 7], [4], [5, 9], [3], [14]],
   [[1, 2, 10, 6, 7, 4], [5, 9], [3], [14]],
   [2, 4],
   [[1], [10, 6, 7, 5, 9], [3], [14]]
]
';

-- Populate Actor table
WITH actor(A) AS ( SELECT CAST(REPLACE(STUFF(REPLACE(REPLACE(@actors,', ',','),'","','</a><a>'),1,2,'<a>'),'"]','</a>') AS XML))
SELECT ROW_NUMBER() OVER (ORDER BY(SELECT \)) ActorID, a.n.value('.','varchar(50)') Name
INTO Actor
FROM actor CROSS APPLY A.nodes('/a') as a(n);

-- Populate Timeline Table
WITH Seq(L) AS (
    SELECT CAST(REPLACE(REPLACE(REPLACE(REPLACE(@timeline,'[','<e>'),']','</e>'),'</e>,<e>','</e><e>'),'</e>,','</e>') AS XML)
    ),
    TimeLine(N,Exerpt,Elem) AS (
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) N
        ,z.query('.')
        ,CAST(REPLACE(CAST(z.query('.') AS VARCHAR(MAX)),',','</e><e>') AS XML)
    FROM Seq 
        CROSS APPLY Seq.L.nodes('/e/e') AS Z(Z)
    ),
    Groups(N,G,Exerpt) AS (
    SELECT N, 
        ROW_NUMBER() OVER (PARTITION BY N ORDER BY CAST(SUBSTRING(node.value('.','varchar(50)'),1,ISNULL(NULLIF(CHARINDEX(',',node.value('.','varchar(50)')),0),99)-1) AS INT)), 
        CAST(REPLACE(CAST(node.query('.') AS VARCHAR(MAX)),',','</e><e>') AS XML) C
    FROM TimeLine 
        CROSS APPLY Exerpt.nodes('/e/e') as Z(node)
    WHERE Exerpt.exist('/e/e') = 1
    )
SELECT * 
INTO TimeLine
FROM (
    SELECT N, null G, null P, node.value('.','int') ActorID, 1 D 
    FROM TimeLine CROSS APPLY TimeLine.Elem.nodes('/e') AS E(node)
    WHERE Exerpt.exist('/e/e') = 0
    UNION ALL
    SELECT N, G, DENSE_RANK() OVER (PARTITION BY N, G ORDER BY node.value('.','int')), node.value('.','int') ActorID, 0
    FROM Groups CROSS APPLY Groups.Exerpt.nodes('/e') AS D(node)
    ) z;

-- Sort the entries again
WITH ReOrder AS (
            SELECT *, 
                ROW_NUMBER() OVER (PARTITION BY N,G ORDER BY PG, ActorID) PP, 
                COUNT(P) OVER (PARTITION BY N,G) CP, 
                MAX(G) OVER (PARTITION BY N) MG, 
                MAX(ActorID) OVER (ORDER BY (SELECT\)) MA
            FROM (
                SELECT *,
                    LAG(G,1) OVER (PARTITION BY ActorID ORDER BY N) PG,
                    LEAD(G,1) OVER (PARTITION BY ActorID ORDER BY N) NG
                FROM timeline
                ) rg
    )
SELECT * INTO Reordered
FROM ReOrder;
ALTER TABLE Reordered ADD PPP INT
GO
ALTER TABLE Reordered ADD LPP INT
GO
WITH U AS (SELECT N, P, LPP, LAG(PP,1) OVER (PARTITION BY ActorID ORDER BY N) X FROM Reordered)
UPDATE U SET LPP = X FROM U;
WITH U AS (SELECT N, ActorID, P, PG, LPP, PPP, DENSE_RANK() OVER (PARTITION BY N,G ORDER BY PG, LPP) X FROM Reordered)
UPDATE U SET PPP = X FROM U;
GO

SELECT Name, 
    Geometry::STGeomFromText(
        STUFF(LS,1,2,'LINESTRING (') + ')'
        ,0)
        .STBuffer(.1)
        .STUnion(
        Geometry::STGeomFromText('POINT (' + REVERSE(SUBSTRING(REVERSE(LS),1,CHARINDEX(',',REVERSE(LS))-1)) + ')',0).STBuffer(D*.4)
        )
FROM Actor a
    CROSS APPLY (
        SELECT CONCAT(', '
            ,((N*5)-1.2)
                ,' ',(G)+P
            ,', '
            ,((N*5)+1.2)
                ,' ',(G)+P 
            ) AS [text()]
        FROM (
            SELECT ActorID, N,
                CASE WHEN d = 1 THEN
                    ((MA+.0) / (LAG(MG,1) OVER (PARTITION BY ActorID ORDER BY N)+.0)) * 
                    PG * 1.2
                ELSE 
                    ((MA+.0) / (MG+.0)) * 
                    G * 1.2
                END G,
                CASE WHEN d = 1 THEN
                (LAG(PPP,1) OVER (PARTITION BY ActorID ORDER BY N) -((LAG(CP,1) OVER (PARTITION BY ActorID ORDER BY N)-1)/2)) * .2 
                ELSE
                (PPP-((CP-1)/2)) * .2 
                END P
                ,PG
                ,NG
            FROM Reordered
            ) t
        WHERE a.actorid = t.actorid
        ORDER BY N, G
        FOR XML PATH('')
        ) x(LS)
    CROSS APPLY (SELECT MAX(D) d FROM TimeLine dt WHERE dt.ActorID = a.ActorID) d
GO

DROP TABLE Actor;
DROP TABLE Timeline;
DROP TABLE Reordered;

결과 타임 라인은 다음과 같습니다 여기에 이미지 설명을 입력하십시오


4

Mathematica, 참조 솔루션

참고로, 나는 최소 요구 사항을 충족시키는 Mathematica 스크립트를 제공합니다.

문자는의 질문 형식 chars및의 이벤트 목록이 될 것으로 예상합니다 events.

n = Length@chars;
m = Max@Map[Length, events, {2}];
deaths = {};
Graphics[
 {
  PointSize@Large,
  (
     linePoints = If[Length@# == 3,
         lastPoint = {#[[1]], #[[2]] + #[[3]]/(m + 2)},
         AppendTo[deaths, Point@lastPoint]; lastPoint
         ] & /@ Position[events, #];
     {
      Line@linePoints,
      Text[chars[[#]], linePoints[[1]] - {.5, 0}]
      }
     ) & /@ Range@n,
  deaths
  }
 ]

예를 들어 Mathematica의 목록 유형을 사용하는 Jurassic Park 예제는 다음과 같습니다.

chars = {"T-Rex", "Raptor", "Raptor", "Raptor", "Malcolm", "Grant", 
   "Sattler", "Gennaro", "Hammond", "Kids", "Muldoon", "Arnold", 
   "Nedry", "Dilophosaurus"};
events = {
   {{1}, {2, 3, 4}, {5}, {6, 7}, {8, 9, 11, 12, 13}, {10}, {14}},
   {{1}, {2, 3, 4}, {5, 8, 6, 7, 9, 10, 11, 12, 13}, {14}},
   {{1}, {2, 3, 4}, {5, 8, 6, 7, 9, 10, 11}, {12, 13}, {14}},
   {{1}, {2, 3, 4}, {5, 8, 6, 7, 10}, {9, 11, 12, 13}, {14}},
   {{1, 5, 8}, {2, 3, 4}, {6, 10}, {7, 9, 11, 12}, {13}, {14}},
   {8},
   {{6, 10}, {1}, {5, 7, 11}, {2, 3, 4}, {9, 12}, {13, 14}},
   {13},
   {{1, 6, 10}, {2, 3, 4}, {5, 7, 11, 9, 12}, {14}},
   {{1}, {6, 10}, {2, 3}, {4, 12}, {5, 7, 11, 9}, {14}},
   {12},
   {{1}, {6, 10}, {2, 3, 11}, {4, 7}, {5, 9}, {14}},
   {11},
   {{1}, {2, 3, 10}, {6, 7}, {4}, {5, 9}, {14}},
   {{1}, {2}, {10, 6, 7}, {4}, {5, 9}, {3}, {14}},
   {{1, 2, 10, 6, 7, 4}, {5, 9}, {3}, {14}},
   {2, 4},
   {{1}, {10, 6, 7, 4, 5, 9}, {3}, {14}}
};

우리는 얻을 것이다 :

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

(더 큰 버전을 보려면 클릭하십시오.)

즉 보이지 않는 너무 나쁜하지만, 입력 데이터가 더 많거나 적은 순서이기 때문에 그 대부분입니다. 동일한 이벤트를 유지하면서 각 이벤트에서 그룹과 문자를 섞으면 다음과 같은 일이 발생할 수 있습니다.

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

어느 정도 엉망입니다.

내가 말했듯이 이것은 최소 요구 사항 만 충족시킵니다. 멋진 레이아웃을 찾으려고 시도하지 않고 예쁘지는 않지만 그 곳이 있습니다!


방금 날카로운 모서리를 제거하기 위해 2 차 또는 3 차 스플라인을 사용하여 '예리 화'할 수 있다고 생각 했습니까? (나는 주어진 점에서의 탄젠트가 항상 0이되도록 그렇게한다)
flawr

@flawr 물론, 또는 이 트릭 중 일부를 적용 할 수 는 있지만이 답변의 목적은 아닙니다. ;) 정말 절대 최소값에 대한 참조를 제공하고 싶었습니다.
Martin Ender

3
아 죄송합니다, 이것이 여러분의 질문이라는 것을조차 몰랐습니다 = P
flawr
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.