Created
May 3, 2019 22:48
-
-
Save G4rryS1ngh/536200a131d53eaf06c488862caf4da2 to your computer and use it in GitHub Desktop.
輪郭をなぞる文字たち_( _´ω`)_
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # include <Siv3D.hpp> // OpenSiv3D v0.3.2 | |
| using ContourPaths = Array<Array<Array<Vec2>>>; | |
| class AnimatedCharacter | |
| { | |
| private: | |
| int m_interpolation; | |
| Array<Array<Vec2>> m_contours; | |
| Duration m_trans_time; | |
| Duration m_wait_time; | |
| Stopwatch m_stopwatch; | |
| Transition m_transition; | |
| // 輪郭線のパスをLineStringに変換 | |
| LineString ConvertPathToLS(Array<Array<Vec2>> _contour_path, Vec2 _bottom_left) | |
| { | |
| LineString ls; | |
| for (const auto& path : _contour_path) | |
| { | |
| if (path.size() == 2) | |
| { | |
| ls << path[0]; | |
| } | |
| else if (path.size() == 3) | |
| { | |
| ls.append(Bezier2(path[0], path[1], path[2]).getLineString()); | |
| ls.pop_back(); | |
| } | |
| else if (path.size() == 4) | |
| { | |
| ls.append(Bezier3(path[0], path[1], path[2], path[3]).getLineString()); | |
| ls.pop_back(); | |
| } | |
| } | |
| ls.moveBy(_bottom_left); | |
| return ls; | |
| } | |
| // LineStringをinterpolation個の等間隔の点列に変換 | |
| Array<Vec2> ConvertLSToEqualSpaced(LineString ls, int _interpolation) | |
| { | |
| if (ls.front() != ls.back()) | |
| ls << ls.front(); | |
| Array<double> length_table = { 0.0 }; | |
| for (int i : step(ls.num_lines())) | |
| length_table << length_table.back() + ls.line(i).length(); | |
| const double perimeter = length_table.back(); | |
| Array<Vec2> sequence = { ls.front() }; | |
| int prev_i = 1; | |
| for (int s : Range(1, _interpolation - 1)) | |
| { | |
| const double distance = perimeter * s / _interpolation; | |
| for (int i : Range(prev_i, (int)length_table.count() - 1)) | |
| { | |
| if (distance <= length_table[i]) | |
| { | |
| const double len_buf = distance - length_table[i - 1]; | |
| sequence << ls[i - 1].lerp(ls[i], len_buf / ls.line(i - 1).length()); | |
| prev_i = i; | |
| break; | |
| } | |
| } | |
| } | |
| return sequence; | |
| } | |
| public: | |
| AnimatedCharacter(ContourPaths _paths, Vec2 _bl, int _inter, Duration _trans, Duration _wait) | |
| : m_interpolation(_inter) | |
| , m_trans_time(_trans) | |
| , m_wait_time(_wait) | |
| , m_stopwatch(true) | |
| , m_transition(_trans, _trans) | |
| { | |
| // 文字の各輪郭線を等間隔の点列に変換する | |
| for (const auto& contour_path : _paths) | |
| { | |
| LineString ls = ConvertPathToLS(contour_path, _bl); | |
| Array<Vec2> sequence = ConvertLSToEqualSpaced(ls, _inter); | |
| m_contours << sequence; | |
| } | |
| } | |
| bool isAlive() const | |
| { | |
| return m_stopwatch < m_trans_time * 2 + m_wait_time; | |
| } | |
| void update() | |
| { | |
| m_transition.update(m_stopwatch < m_trans_time + m_wait_time); | |
| } | |
| void draw(ColorF _color) const | |
| { | |
| const double thickness = 2.0; | |
| if (m_transition.value() < 1) | |
| { | |
| int n = (int)(m_interpolation * m_transition.easeInOut() + 0.5); | |
| m_contours.each([&](const auto & ary) { LineString(ary.take(n)).draw(thickness, _color); }); | |
| } | |
| else | |
| { | |
| m_contours.each([&](const auto & ary) { LineString(ary).drawClosed(thickness, _color); }); | |
| } | |
| } | |
| }; | |
| class AnimatedSentence | |
| { | |
| private: | |
| String m_text; | |
| Font m_font; | |
| ColorF m_color; | |
| RectF m_region; | |
| Duration m_delay; | |
| Duration m_trans_time; | |
| Duration m_wait_time; | |
| Vec2 m_pen_pos; | |
| int m_interpolation; | |
| size_t m_index; | |
| bool m_reached_end; | |
| Stopwatch m_stopwatch; | |
| Array<AnimatedCharacter> m_characters; | |
| public: | |
| AnimatedSentence(String _text, const Font& _font, RectF _region, Duration _delay, Duration _trans, Duration _wait, int _inter) | |
| : m_text(_text) | |
| , m_font(_font) | |
| , m_color(Random(0.5, 1.0), 1.0) | |
| , m_region(_region) | |
| , m_delay(_delay) | |
| , m_trans_time(_trans) | |
| , m_wait_time(_wait) | |
| , m_pen_pos(_region.tl().movedBy(0, _font.height())) | |
| , m_interpolation(_inter) | |
| , m_index(0) | |
| , m_reached_end(false) | |
| , m_stopwatch(true) | |
| {} | |
| bool isAlive() const | |
| { | |
| return !(m_reached_end && m_characters.isEmpty()); | |
| } | |
| void update() | |
| { | |
| if (!m_reached_end && m_stopwatch > m_delay * m_index) | |
| { | |
| // 文字を生成 | |
| const char32 c = m_text[m_index]; | |
| const ContourPaths paths = m_font.getOutlineGlyph(c).contourPaths; | |
| m_characters << AnimatedCharacter(paths, m_pen_pos, m_interpolation, m_trans_time, m_wait_time); | |
| m_pen_pos.x += m_font.getGlyph(c).xAdvance; | |
| // 描画範囲の右端を超えたら改行 | |
| if (!m_region.contains(m_pen_pos)) | |
| { | |
| m_pen_pos.x = m_region.x; | |
| m_pen_pos.y += m_font.height(); | |
| } | |
| // 描画範囲の下端を超えるか文章を書き終えたらこれ以上生成しない | |
| m_reached_end |= !m_region.contains(m_pen_pos); | |
| m_reached_end |= (++m_index >= m_text.length()); | |
| } | |
| // 各文字の更新と削除 | |
| m_characters.each([](auto & c) { return c.update(); }); | |
| m_characters.keep_if([](const auto & c) { return c.isAlive(); }); | |
| } | |
| void draw() const | |
| { | |
| m_characters.each([&](const auto & c) { c.draw(m_color); }); | |
| } | |
| }; | |
| void Main() | |
| { | |
| // const String sample = U"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; | |
| const Array<String> poems = | |
| { | |
| U"秋の田のかりほの庵のとまをあらみ わがころもでは露にぬれつゝ", | |
| U"春すぎて夏来にけらし白妙の 衣ほすてふ天の香具山", | |
| U"足引きの山鳥の尾のしだりおの ながながし夜をひとりかもねん", | |
| U"田子の浦にうち出てみれば白妙の ふじのたかねに雪はふりつゝ", | |
| U"おくやまに紅葉ふみわけ鳴く鹿の 声きく時ぞ秋は悲しき", | |
| U"かさゝぎのわたせる橋に置く霜の 白きを見れば夜ぞふけにける", | |
| U"天の原ふりさけ見れば春日なる 三笠の山にいでし月かも", | |
| U"わが庵は都のたつみしかぞすむ 世をうぢ山と人はいふなり", | |
| U"花の色はうつりにけりないたづらに わが身よにふるながめせしまに", | |
| U"これやこの行くも帰るも別れては しるもしらぬも相坂の関", | |
| U"わたのはら八十嶋かけてこぎ出ぬと 人には告げよあまのつりぶね", | |
| U"あまつ風雲のかよひ路吹きとぢよ 乙女のすがたしばしとゞめん", | |
| U"つくばねの峰より落つるみなの川 こひぞつもりて淵となりぬる", | |
| U"みちのくのしのぶもぢずり誰ゆへに みだれそめにし我ならなくに", | |
| U"君がため春の野に出て若菜つむ わが衣手に雪はふりつゝ", | |
| U"立ちわかれいなばの山の嶺におふる まつとし聞かば今かへりこむ", | |
| U"ちはやぶる神代もきかず龍田川 からくれなゐに水くゞるとは", | |
| U"住の江の岸による波よるさへや 夢の通ひ路人目よくらむ", | |
| U"難波潟みじかきあしのふしのまも あはでこの世を過ぐしてよとや", | |
| U"わびぬれば今はた同じ難波なる 身をつくしてもあはむとぞ思ふ", | |
| U"今来むと言ひしばかりに長月の 有明の月を待ちいでつるかな", | |
| U"吹くからに秋の草木のしほるれば むべ山風をあらしと云らむ", | |
| U"月みれば千々に物こそ悲しけれ わが身ひとつの秋にはあらねど", | |
| U"このたびはぬさもとりあへず手向山 紅葉のにしきかみのまにまに", | |
| U"名にしおはゞ相坂山のさねかづら 人にしられでくるよしもがな", | |
| U"小倉山峰のもみぢばこころあらば 今ひとたびのみゆきまたなん", | |
| U"みかのはらわきてながるゝ泉河 いつ見きとてかこひしかるらむ", | |
| U"山里は冬ぞさびしさまさりける 人めもくさもかれぬとおもへば", | |
| U"心あてにをらばやおらむ初霜の をきまどはせるしらぎくの花", | |
| U"有明のつれなくみえし別れより 暁ばかりうきものはなし", | |
| U"あさぼらけ有明の月とみるまでに よしのの里にふれるしら雪", | |
| U"山川に風のかけたるしがらみは ながれもあへぬ紅葉なりけり", | |
| U"ひさかたのひかりのどけき春の日に しづ心なく花のちるらむ", | |
| U"誰をかもしる人にせむ高砂の 松もむかしのともならなくに", | |
| U"人はいさこころもしらず故郷は はなぞむかしのかに匂ひける", | |
| U"夏の夜はまだ宵ながら明けぬるを 雲のいづくに月やどるらむ", | |
| U"白露に風のふきしく秋のゝは つらぬきとめぬ玉ぞちりける", | |
| U"忘らるゝ身をば思はずちかひてし 人のいのちのおしくもあるかな", | |
| U"浅茅生のをのゝしのはら忍ぶれど あまりてなどか人のこひしき", | |
| U"しのぶれど色に出にけりわが恋は 物や思ふと人の問ふまで", | |
| U"恋すてふ我名はまだき立ちにけり 人しれずこそ思ひ初めしか", | |
| U"ちぎりきなかたみに袖をしぼりつゝ 末の松山なみこさじとは", | |
| U"あひ見ての後の心にくらぶれば むかしは物を思はざりけり", | |
| U"あふことのたえてしなくは中々に 人をも身をもうらみざらまし", | |
| U"哀れともいふべき人はおもほえで みのいたづらになりぬべき哉", | |
| U"由良のとを渡る舟人かぢをたえ 行へもしらぬ恋のみちかな", | |
| U"やへむぐらしげれる宿のさびしきに 人こそ見えねあきは来にけり", | |
| U"風をいたみ岩うつ波のをのれのみ くだけてものをおもふころかな", | |
| U"みかきもり衛士のたく火の夜はもえ 昼は消えつゝ物をこそおもへ", | |
| U"君がためおしからざりし命さへ ながくもがなとおもひぬる哉", | |
| U"かくとだにえやはいぶきのさしも草 さしもしらじなもゆる思ひを", | |
| U"明けぬればくるゝものとはしりながら なをうらめしきあさぼらけかな", | |
| U"なげきつゝひとりぬるよの明くるまは いかに久しきものとかはしる", | |
| U"わすれじの行末迄はかたければ けふをかぎりの命ともがな", | |
| U"滝の音は絶えて久しくなりぬれど 名こそながれてなをきこえけれ", | |
| U"あらざらむこのよのほかの思ひ出に 今ひとたびのあふこともがな", | |
| U"めぐりあひて見しやそれとも分かぬまに 雲がくれにし夜半の月かな", | |
| U"ありま山いなの篠原風吹けば いでそよ人をわすれやはする", | |
| U"やすらはでねなましものをさよふけて かたぶくまでの月を見しかな", | |
| U"大江山いくのゝ道のとをければ まだふみもみず天のはしだて", | |
| U"いにしへのならの都の八重桜 けふ九重ににほひぬるかな", | |
| U"よをこめて鳥の空音ははかるとも よにあふさかの関はゆるさじ", | |
| U"今はたゞおもひ絶なんとばかりを 人づてならでいふよしもがな", | |
| U"朝ぼらけ宇治のかはぎりたえだえに あらはれわたる瀬々の網代木", | |
| U"恨みわびほさぬ袖だにあるものを 恋にくちなん名こそおしけれ", | |
| U"もろともに哀れと思へ山桜 花よりほかに知る人もなし", | |
| U"春の夜の夢ばかりなる手枕に かひなくたゝむ名こそ惜しけれ", | |
| U"心にもあらでこのよにながらへば こひしかるべきよはの月かな", | |
| U"あらし吹く三室の山のもみぢばゝ 龍田の川のにしきなりけり", | |
| U"さびしさに宿を立出て詠むれば いづくもおなじあきのゆふぐれ", | |
| U"夕されば門田の稲葉をとづれて あしのまろやに秋風ぞふく", | |
| U"音にきくたかしの浜のあだ波は かけじや袖のぬれもこそすれ", | |
| U"高砂の尾上の桜さきにけり とやまの霞たゝずもあらなん", | |
| U"うかりける人をはつせの山をろし風 はげしかれとは祈らぬものを", | |
| U"ちぎりをきしさせもが露を命にて あはれことしの秋もいぬめり", | |
| U"和田の原こぎ出てみればひさかたの くもゐにまがふ奥津白波", | |
| U"瀬をはやみ岩にせかるゝ滝川の われてもすゑにあはむとぞおもふ", | |
| U"淡路嶋かよふ千鳥のなく声に 幾夜ね覚ぬすまの関守", | |
| U"秋風にたなびく雲のたえまより もれいづる月のかげのさやけさ", | |
| U"長からむ心もしらずくろかみの みだれてけさは物をこそ思へ", | |
| U"ほととぎすなきつるかたをながむれば たゞありあけの月ぞのこれる", | |
| U"思ひわびさてもいのちはある物を うきにたへぬはなみだなりけり", | |
| U"世の中よ道こそなけれおもひ入る やまのおくにも鹿ぞなくなる", | |
| U"ながらへばまたこのごろやしのばれん うしと見しよぞいまは恋しき", | |
| U"よもすがら物思ふころは明けやらぬ 閨のひまさへつれなかりけり", | |
| U"なげけとて月やは物を思はする かこちがほなるわがなみだかな", | |
| U"村雨の露もまだひぬまきのはに 霧たちのぼるあきのゆふぐれ", | |
| U"難波江のあしのかりねのひとよゆへ 身をつくしてや恋わたるべき", | |
| U"玉の緒よ絶なば絶ねながらへば 忍ぶることのよはりもぞする", | |
| U"見せばやなをじまのあまの袖だにも ぬれにぞぬれし色はかはらず", | |
| U"きりぎりすなくや霜夜のさむしろに 衣かたしきひとりかもねん", | |
| U"我袖はしほひに見えぬおきの石の 人こそしらねかはくまもなし", | |
| U"世の中はつねにもがもななぎさこぐ あまのをぶねの綱手かなしも", | |
| U"みよしのゝ山の秋風さよふけて 故郷さむくころもうつなり", | |
| U"おほけなく浮世の民におほふかな わがたつそまにすみぞめの袖", | |
| U"花さそふあらしの庭の雪ならで ふり行くものは我身なりけり", | |
| U"こぬ人をまつほの浦の夕なぎに やくやもしほの身もこがれつゝ", | |
| U"風そよぐならの小川の夕暮は みそぎぞ夏のしるしなりける", | |
| U"人もおし人も恨めしあぢきなく よをおもふゆへに物思ふ身は", | |
| U"百敷やふるき軒端のしのぶにも なをあまりある昔なりけり", | |
| }; | |
| Array<AnimatedSentence> sentences; | |
| Stopwatch stopwatch(true); | |
| // インストールされている日本語フォントのパス一覧を取得 | |
| using namespace FileSystem; | |
| Array<FilePath> fonts = DirectoryContents(SpecialFolderPath(SpecialFolder::SystemFonts)); | |
| fonts.keep_if([](const auto& fp) { return fp.includes(U"ms") || fp.includes(U"HG") || fp.includes(U"UD"); }); | |
| while (System::Update()) | |
| { | |
| if (stopwatch > 1.0s) | |
| { | |
| // フォント、描画範囲、文章、アニメーション時間をランダムに設定してAnimatedSentenceを作る | |
| Font font(Random(24, 180), Sample(fonts), FontStyle::Bold); | |
| Vec2 size(Window::Width() * Random(0.7, 1.6), Window::Height() * Random(0.3, 1.0)); | |
| RectF region(Arg::center = RandomVec2(Window::ClientRect()), size); | |
| Duration delay = Random(0.1s, 0.5s); | |
| Duration trans = Random(0.2s, 1.0s); | |
| Duration wait = Random(0.0s, 1.5s); | |
| const int smoothness = 80; // 20:bad , 50:good , 100:fine | |
| sentences << AnimatedSentence(Sample(poems), font, region, delay, trans, wait, smoothness); | |
| stopwatch.restart(); | |
| } | |
| // 各文章の更新と削除、描画 | |
| sentences.each([](auto & s) { return s.update(); }); | |
| sentences.keep_if([](const auto & s) { return s.isAlive(); }); | |
| sentences.each([](const auto & s) { s.draw(); }); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment