diff --git a/onefile/anim-cube.c b/onefile/anim-cube.c new file mode 100644 index 0000000..ce5949b --- /dev/null +++ b/onefile/anim-cube.c @@ -0,0 +1,111 @@ + +#include +#include + + +#define LINE 0.05 +#define DOT 0 + + +double dx = 0.4; +double dy = 0.4; + + +void drawLine(double pos) { + line(0, 0, pos, 0); + circle(0, 0, DOT); + circle(pos, 0, DOT); +} + +void drawQuad(double pos) { + saveState(); + noFill(); + moveTo(0, 0); + lineTo(1, 0); + lineTo(1, pos); + lineTo(0, pos); + closePath(); + restoreState(); + + circle(0, 0, DOT); + circle(1, 0, DOT); + circle(0, pos, DOT); + circle(1, pos, DOT); +} + +void drawCube(double pos) { + double ddx = pos*dx; + double ddy = pos*dy; + + saveState(); + noFill(); + + moveTo(0, 0); + lineTo(1, 0); + lineTo(1, 1); + lineTo(0, 1); + closePath(); + + moveTo(ddx + 0, ddy + 0); + lineTo(ddx + 1, ddy + 0); + lineTo(ddx + 1, ddy + 1); + lineTo(ddx + 0, ddy + 1); + closePath(); + + line(0, 0, ddx + 0, ddy + 0); + line(1, 0, ddx + 1, ddy + 0); + line(0, 1, ddx + 0, ddy + 1); + line(1, 1, ddx + 1, ddy + 1); + + restoreState(); + + circle(0, 0, DOT); + circle(1, 0, DOT); + circle(0, 1, DOT); + circle(1, 1, DOT); + + circle(ddx + 0, ddx + 0, DOT); + circle(ddx + 1, ddx + 0, DOT); + circle(ddx + 0, ddx + 1, DOT); + circle(ddx + 1, ddy + 1, DOT); +} + + + +void init() { +} + + +void draw() { + double w = windowGetWidth(); + double h = windowGetHeight(); + double t = windowGetSeconds(); + + saveState(); + translate(w/2, h/2); + zoom(100); + translate(-0.5, -0.5); + + fill(COLOR_BLACK); + stroke(COLOR_BLACK); + strokeWidth(LINE); + + double a = t/3*2*PI; + double pos = 0.5*(1 - cos(a))*3; + if (pos < 1) drawLine(pos); else + if (pos < 2) drawQuad(pos-1); else + drawCube(pos-2); + + restoreState(); +} + + + +int main() { + windowSetVariableFrameRate(); + windowSetResizable(TRUE); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); + return 0; +} diff --git a/onefile/data-src/dungeon.svg b/onefile/data-src/dungeon.svg new file mode 100644 index 0000000..595ca83 --- /dev/null +++ b/onefile/data-src/dungeon.svg @@ -0,0 +1,609 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/onefile/data-src/dungeon2.svg b/onefile/data-src/dungeon2.svg new file mode 100644 index 0000000..43f2ddd --- /dev/null +++ b/onefile/data-src/dungeon2.svg @@ -0,0 +1,820 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/onefile/data-src/dungeon3.svg b/onefile/data-src/dungeon3.svg new file mode 100644 index 0000000..879f6f3 --- /dev/null +++ b/onefile/data-src/dungeon3.svg @@ -0,0 +1,830 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/onefile/data-src/scheme.svg b/onefile/data-src/scheme.svg new file mode 100644 index 0000000..4845eeb --- /dev/null +++ b/onefile/data-src/scheme.svg @@ -0,0 +1,514 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/onefile/data-src/writing-dev.svg b/onefile/data-src/writing-dev.svg new file mode 100644 index 0000000..f18ed77 --- /dev/null +++ b/onefile/data-src/writing-dev.svg @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + к + а + л + ц + + + ч + + + + А + А Б В Г Д Е Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Э Ю Я + + + diff --git a/onefile/data/.placeholder b/onefile/data/.placeholder deleted file mode 100644 index e69de29..0000000 --- a/onefile/data/.placeholder +++ /dev/null diff --git a/onefile/data/dungeon.png b/onefile/data/dungeon.png new file mode 100644 index 0000000..6a95a41 Binary files /dev/null and b/onefile/data/dungeon.png differ diff --git a/onefile/data/dungeon2.png b/onefile/data/dungeon2.png new file mode 100644 index 0000000..81536eb Binary files /dev/null and b/onefile/data/dungeon2.png differ diff --git a/onefile/data/dungeon3.png b/onefile/data/dungeon3.png new file mode 100644 index 0000000..b391150 Binary files /dev/null and b/onefile/data/dungeon3.png differ diff --git a/onefile/data/output-demo/generated-barcode.svg b/onefile/data/output-demo/generated-barcode.svg new file mode 100644 index 0000000..657bc5b --- /dev/null +++ b/onefile/data/output-demo/generated-barcode.svg @@ -0,0 +1,142 @@ + + +999597 +520598 + +657388 +812533 + +056280 +599741 + +076500 +006775 + +265828 +555597 + +722880 +920523 + +237630 +726661 + +387596 +137838 + +861714 +282700 + +408824 +109101 + +196985 +409884 + +341404 +634666 + +991756 +375374 + +227345 +336249 + +490763 +115708 + +878710 +490360 + +636307 +052450 + +302893 +692587 + +652192 +379394 + +215714 +918020 + +451343 +454946 + +354895 +688973 + +181607 +258843 + +343163 +559674 + +541544 +751987 + +668775 +738529 + +161871 +526532 + +373195 +669979 + +901906 +116893 + +006228 +909021 + +232601 +401290 + +915734 +868908 + +453740 +218627 + +077848 +622284 + +114373 +809914 + diff --git a/onefile/data/output-demo/generated-dungeon.png b/onefile/data/output-demo/generated-dungeon.png new file mode 100644 index 0000000..59827c0 Binary files /dev/null and b/onefile/data/output-demo/generated-dungeon.png differ diff --git a/onefile/data/output-demo/generated-dungeon2.png b/onefile/data/output-demo/generated-dungeon2.png new file mode 100644 index 0000000..0b10e07 Binary files /dev/null and b/onefile/data/output-demo/generated-dungeon2.png differ diff --git a/onefile/data/output-demo/generated-dungeon3.png b/onefile/data/output-demo/generated-dungeon3.png new file mode 100644 index 0000000..7a6c787 Binary files /dev/null and b/onefile/data/output-demo/generated-dungeon3.png differ diff --git a/onefile/data/output-demo/generated-round-grid.png b/onefile/data/output-demo/generated-round-grid.png new file mode 100644 index 0000000..c5a375e Binary files /dev/null and b/onefile/data/output-demo/generated-round-grid.png differ diff --git a/onefile/data/output-demo/generated-scheme.png b/onefile/data/output-demo/generated-scheme.png new file mode 100644 index 0000000..e07fab1 Binary files /dev/null and b/onefile/data/output-demo/generated-scheme.png differ diff --git a/onefile/data/output-demo/generated-writing.svg b/onefile/data/output-demo/generated-writing.svg new file mode 100644 index 0000000..c18b6fe --- /dev/null +++ b/onefile/data/output-demo/generated-writing.svg @@ -0,0 +1,4 @@ + + + diff --git a/onefile/data/output-demo/msg.gif b/onefile/data/output-demo/msg.gif new file mode 100644 index 0000000..0a0dd45 Binary files /dev/null and b/onefile/data/output-demo/msg.gif differ diff --git a/onefile/data/output-demo/splitted-text.txt b/onefile/data/output-demo/splitted-text.txt new file mode 100644 index 0000000..22658c5 --- /dev/null +++ b/onefile/data/output-demo/splitted-text.txt @@ -0,0 +1,100 @@ +Ро̩̍берт Ас̩̍прин +Е̩̍щё о̩̍дин ве̩̍ли̩̍ко̩̍леп̩̍ный миф + + +Э̩̍та кни̩̍га пос̩̍вя̩̍ща̩̍ет̩̍ся Бор̩̍ку Не̩̍у̩̍нич̩̍то̩̍жи̩̍мо̩̍му (из̩̍вес̩̍тно̩̍му мень̩̍шим смер̩̍тным как Джордж Хант), чья гру̩̍бо̩̍ва̩̍та̩̍я, но вер̩̍на̩̍я друж̩̍ба по̩̍мог̩̍ла мне пре̩̍о̩̍до̩̍леть мно̩̍го кри̩̍зи̩̍сов в прош̩̍лом… и, ве̩̍ро̩̍ят̩̍но, в бу̩̍ду̩̍щем! + +ГЛА̩̍ВА 1 + +«Есть мно̩̍го̩̍е на све̩̍те, друг Го̩̍ра̩̍ци̩̍о, что и не сни̩̍лось на̩̍шим муд̩̍ре̩̍цам». +Гам̩̍лет + +Од̩̍но из нем̩̍но̩̍гих все ис̩̍куп̩̍ля̩̍ю̩̍щих ка̩̍честв нас̩̍тав̩̍ни̩̍ков, ду̩̍ма̩̍ю я, зак̩̍лю̩̍ча̩̍ет̩̍ся в том, что их при слу̩̍ча̩̍е мож̩̍но о̩̍ду̩̍ра̩̍чить. Э̩̍то бы̩̍ло вер̩̍но, ког̩̍да мать у̩̍чи̩̍ла ме̩̍ня чи̩̍тать, э̩̍то бы̩̍ло вер̩̍но, ког̩̍да о̩̍тец пы̩̍тал̩̍ся на̩̍у̩̍чить ме̩̍ня быть фер̩̍ме̩̍ром, и вер̩̍но те̩̍перь, ког̩̍да я о̩̍бу̩̍ча̩̍юсь ма̩̍ги̩̍и. +— Ты не прак̩̍ти̩̍ко̩̍вал̩̍ся! — прер̩̍вал мо̩̍и раз̩̍мыш̩̍ле̩̍ни̩̍я рез̩̍кий уп̩̍рек Гар̩̍ки̩̍на. +— А вот и нет! — воз̩̍ра̩̍зил я. — Э̩̍то прос̩̍то труд̩̍но̩̍е уп̩̍раж̩̍не̩̍ни̩̍е. +Слов̩̍но в от̩̍вет ле̩̍ви̩̍ти̩̍ру̩̍е̩̍мо̩̍е мно̩̍ю пе̩̍ро на̩̍ча̩̍ло дро̩̍жать и ко̩̍ле̩̍бать̩̍ся в воз̩̍ду̩̍хе. +— Ты не сос̩̍ре̩̍до̩̍та̩̍чи̩̍ва̩̍ешь̩̍ся! — об̩̍ви̩̍нил он ме̩̍ня. +— Э̩̍то все ве̩̍тер, — не сог̩̍ла̩̍сил̩̍ся я. Мне хо̩̍те̩̍лось до̩̍ба̩̍вить: «От тво̩̍е̩̍го ум̩̍но̩̍го я̩̍зы̩̍ка», но я не пос̩̍мел. Гар̩̍кин в са̩̍мом на̩̍ча̩̍ле на̩̍ших у̩̍ро̩̍ков про̩̍де̩̍монс̩̍три̩̍ро̩̍вал сво̩̍е не̩̍у̩̍ме̩̍ни̩̍е це̩̍нить дер̩̍зких у̩̍че̩̍ни̩̍ков. +— Ве̩̍тер! — през̩̍ри̩̍тель̩̍но фыр̩̍кнул он, пе̩̍ред̩̍раз̩̍ни̩̍ва̩̍я ме̩̍ня. — Вот так на̩̍до, бол̩̍ван! +Мой мыс̩̍лен̩̍ный кон̩̍такт с пред̩̍ме̩̍том мо̩̍ей сос̩̍ре̩̍до̩̍то̩̍чен̩̍нос̩̍ти прер̩̍вал̩̍ся и ос̩̍та̩̍но̩̍вил̩̍ся, ког̩̍да пе̩̍ро дер̩̍ну̩̍лось и взмы̩̍ло к по̩̍тол̩̍ку. О̩̍но дер̩̍ну̩̍лось и ос̩̍та̩̍но̩̍ви̩̍лось, слов̩̍но вон̩̍зив̩̍шись во что‑то, хо̩̍тя все е̩̍ще на̩̍хо̩̍ди̩̍лось в фу̩̍те от де̩̍ре̩̍вян̩̍ных стро̩̍пил, а по̩̍том ста̩̍ло мед̩̍лен̩̍но вра̩̍щать̩̍ся в го̩̍ри̩̍зон̩̍таль̩̍ной плос̩̍кос̩̍ти. По̩̍том, о̩̍но так̩̍же мед̩̍лен̩̍но ста̩̍ло вра̩̍щать̩̍ся вок̩̍руг сво̩̍ей о̩̍си, а за̩̍тем по̩̍ме̩̍ня̩̍ло кон̩̍цы и на̩̍ча̩̍ло сколь̩̍зить по не̩̍ви̩̍ди̩̍мо̩̍му кру̩̍гу, слов̩̍но под̩̍хва̩̍чен̩̍ный смер̩̍чем лист. +Я рис̩̍кнул взгля̩̍нуть на Гар̩̍ки̩̍на. Тот раз̩̍ва̩̍лил̩̍ся в крес̩̍ле, бол̩̍тал но̩̍га̩̍ми, яв̩̍но пос̩̍вя̩̍тив сво̩̍е вни̩̍ма̩̍ни̩̍е по̩̍жи̩̍ра̩̍ни̩̍ю жа̩̍ре̩̍ной нож̩̍ки я̩̍ще̩̍роп̩̍ти̩̍цы, пой̩̍ман̩̍ной, мо̩̍гу до̩̍ба̩̍вить, мной. Ни̩̍че̩̍го се̩̍бе сос̩̍ре̩̍до̩̍то̩̍чен̩̍ность! +Он вдруг под̩̍нял гла̩̍за и на̩̍ши взгля̩̍ды встре̩̍ти̩̍лись. От̩̍во̩̍ра̩̍чи̩̍вать̩̍ся бы̩̍ло слиш̩̍ком поз̩̍дно и я по̩̍э̩̍то̩̍му прос̩̍то пог̩̍ля̩̍дел на не̩̍го в от̩̍вет. +— Про̩̍го̩̍ло̩̍дал̩̍ся? — е̩̍го из̩̍ма̩̍зан̩̍на̩̍я жи̩̍ром бо̩̍ро̩̍да с про̩̍се̩̍дью сде̩̍ла̩̍лась вдруг об̩̍рам̩̍лен̩̍ной вол̩̍чьей ус̩̍меш̩̍кой. — Тог̩̍да по̩̍ка̩̍жи мне, мно̩̍го ли ты прак̩̍ти̩̍ко̩̍вал̩̍ся? +Мне пот̩̍ре̩̍бо̩̍ва̩̍лось все̩̍го мгно̩̍ве̩̍ни̩̍е, что̩̍бы по̩̍нять, что он и̩̍мел вви̩̍ду, а за̩̍тем я с от̩̍ча̩̍я̩̍ньем под̩̍нял гла̩̍за. Пе̩̍ро ку̩̍выр̩̍ка̩̍лось к по̩̍лу ед̩̍ва на вы̩̍со̩̍те плеч от при̩̍зем̩̍ле̩̍ни̩̍я. Зас̩̍та̩̍вив вне̩̍зап̩̍но̩̍е нап̩̍ря̩̍же̩̍ни̩̍е по̩̍ки̩̍нуть мо̩̍е те̩̍ло, я мыс̩̍лен̩̍но про̩̍тя̩̍нул ру̩̍ку… мяг̩̍ко… об̩̍ра̩̍зу̩̍я по̩̍душ̩̍ку… не сши̩̍ба̩̍я е̩̍го… +Пе̩̍ро ос̩̍та̩̍но̩̍ви̩̍лось в двух ла̩̍до̩̍нях от по̩̍ла. Я ус̩̍лы̩̍шал ти̩̍хий сме̩̍шок Гар̩̍ки̩̍на, но не поз̩̍во̩̍лил э̩̍то̩̍му на̩̍ру̩̍шать мо̩̍ю сос̩̍ре̩̍до̩̍то̩̍чен̩̍ность. Я три го̩̍да не да̩̍вал пе̩̍ру кос̩̍нуть̩̍ся по̩̍ла, о̩̍но не кос̩̍нет̩̍ся е̩̍го и сей̩̍час. +Я мед̩̍лен̩̍но под̩̍нял е̩̍го, по̩̍ка о̩̍но не вос̩̍па̩̍ри̩̍ло на у̩̍ров̩̍не глаз. О̩̍бер̩̍нув е̩̍го сво̩̍ей мыс̩̍лью, я стал вра̩̍щать е̩̍го вок̩̍руг сво̩̍ей о̩̍си, а за̩̍тем зас̩̍та̩̍вил по̩̍ме̩̍нять кон̩̍цы. По̩̍ку̩̍да я про̩̍де̩̍лы̩̍вал с ним э̩̍ти уп̩̍раж̩̍не̩̍ни̩̍я, е̩̍го дви̩̍же̩̍ни̩̍я не бы̩̍ли та̩̍ки̩̍ми глад̩̍ки̩̍ми и у̩̍ве̩̍рен̩̍ны̩̍ми, как тог̩̍да, ког̩̍да э̩̍той за̩̍да̩̍че у̩̍де̩̍лял вни̩̍ма̩̍ни̩̍е Гар̩̍кин, но о̩̍но бе̩̍зо̩̍ши̩̍боч̩̍но дви̩̍га̩̍лось по за̩̍дан̩̍но̩̍му е̩̍му пу̩̍ти. +Хо̩̍тя я не прак̩̍ти̩̍ко̩̍вал̩̍ся с пе̩̍ром, я все‑та̩̍ки прак̩̍ти̩̍ко̩̍вал̩̍ся. Ког̩̍да Гар̩̍ки̩̍на не бы̩̍ва̩̍ло поб̩̍ли̩̍зос̩̍ти и̩̍ли он был за̩̍нят сво̩̍и̩̍ми собс̩̍твен̩̍ны̩̍ми ис̩̍сле̩̍до̩̍ва̩̍ни̩̍я̩̍ми, я пос̩̍вя̩̍щал боль̩̍шу̩̍ю часть сво̩̍е̩̍го вре̩̍ме̩̍ни ле̩̍ви̩̍ти̩̍ро̩̍ва̩̍ни̩̍ю ме̩̍тал̩̍ли̩̍чес̩̍ких пред̩̍ме̩̍тов, а ес̩̍ли точ̩̍не̩̍е клю̩̍чей. Каж̩̍до̩̍му ви̩̍ду ле̩̍ви̩̍та̩̍ци̩̍и при̩̍су̩̍щи сво̩̍и собс̩̍твен̩̍ным проб̩̍ле̩̍мы. С ме̩̍тал̩̍лом труд̩̍но ра̩̍бо̩̍тать, э̩̍то ма̩̍те̩̍ри̩̍ал и̩̍нер̩̍тный. Пе̩̍ро, быв̩̍ше̩̍е ког̩̍да‑то час̩̍тью жи̩̍во̩̍го су̩̍щес̩̍тва, от̩̍кли̩̍ка̩̍лось лег̩̍че… нам̩̍но̩̍го лег̩̍че. Для под̩̍ня̩̍ти̩̍я ме̩̍тал̩̍ла тре̩̍бо̩̍ва̩̍лось у̩̍си̩̍ли̩̍е, для ма̩̍нев̩̍ри̩̍ро̩̍ва̩̍ни̩̍я пе̩̍ром тре̩̍бо̩̍ва̩̍лась лег̩̍кость. Я пред̩̍по̩̍чи̩̍тал ра̩̍бо̩̍тать с ме̩̍тал̩̍лом. Мне ви̩̍де̩̍лось бо̩̍ле̩̍е пря̩̍мо̩̍е при̩̍ме̩̍не̩̍ни̩̍е э̩̍то̩̍го у̩̍ме̩̍ни̩̍я в из̩̍бран̩̍ной мной про̩̍фес̩̍си̩̍и. +— Дос̩̍та̩̍точ̩̍но хо̩̍ро̩̍шо, маль̩̍чу̩̍ган! А те̩̍перь по̩̍ло̩̍жи е̩̍го об̩̍рат̩̍но в кни̩̍гу. +Я у̩̍лыб̩̍нул̩̍ся про се̩̍бя. По э̩̍той час̩̍ти я нап̩̍рак̩̍ти̩̍ко̩̍вал̩̍ся не из‑за е̩̍е по̩̍тен̩̍ци̩̍аль̩̍но̩̍го при̩̍ме̩̍не̩̍ни̩̍я, а по̩̍то̩̍му, что э̩̍то ме̩̍ня за̩̍бав̩̍ля̩̍ло. +Кни̩̍га ле̩̍жа̩̍ла в рас̩̍кры̩̍том ви̩̍де в кон̩̍це ра̩̍бо̩̍че̩̍го сто̩̍ла. Я о̩̍пус̩̍тил пе̩̍ро по длин̩̍ной ле̩̍ни̩̍вой спи̩̍ра̩̍ли, да̩̍ва̩̍я е̩̍му слег̩̍ка прой̩̍тись по стра̩̍ни̩̍цам кни̩̍ги, под̩̍нял вверх по кру̩̍той ду̩̍ге, ос̩̍та̩̍но̩̍вил и по̩̍вел об̩̍рат̩̍но. А ког̩̍да о̩̍но во вто̩̍рой раз приб̩̍ли̩̍зи̩̍лось к кни̩̍ге, я ос̩̍во̩̍бо̩̍дил часть сво̩̍е̩̍го для брос̩̍ка к кни̩̍ге. Ког̩̍да пе̩̍ро чир̩̍кну̩̍ло по стра̩̍ни̩̍цам, кни̩̍га зах̩̍лоп̩̍ну̩̍лась, слов̩̍но че̩̍люс̩̍ти го̩̍лод̩̍но̩̍го хищ̩̍ни̩̍ка на ме̩̍та̩̍тель̩̍ном сна̩̍ря̩̍де в пре̩̍де̩̍лах их до̩̍ся̩̍га̩̍е̩̍мос̩̍ти. +— Хммм… — про̩̍тя̩̍нул Гар̩̍кин. — Чу̩̍точ̩̍ку на̩̍по̩̍каз, но эф̩̍фек̩̍тно. +— Все̩̍го лишь ма̩̍лость то̩̍го, что я раз̩̍ра̩̍бо̩̍тал, по̩̍ка прак̩̍ти̩̍ко̩̍вал̩̍ся, — неб̩̍реж̩̍но бро̩̍сил я, мыс̩̍лен̩̍но про̩̍тя̩̍нув ру̩̍ку к дру̩̍гой нож̩̍ке я̩̍ще̩̍роп̩̍ти̩̍цы. Од̩̍на̩̍ко вмес̩̍то то̩̍го, что̩̍бы гра̩̍ци̩̍оз̩̍но проп̩̍лыть к мо̩̍ей ру̩̍ке, о̩̍на ос̩̍та̩̍лась на де̩̍ре̩̍вян̩̍ной та̩̍рел̩̍ке, слов̩̍но пус̩̍ти̩̍ла ту̩̍да кор̩̍ни. +— Не так быс̩̍тро, мой ма̩̍лень̩̍кий во̩̍риш̩̍ка. Зна̩̍чит, ты прак̩̍ти̩̍ко̩̍вал̩̍ся, да? — он за̩̍дум̩̍чи̩̍во ог̩̍ла̩̍дил бо̩̍ро̩̍ду, не вы̩̍пус̩̍ка̩̍я из рук по̩̍лу̩̍об̩̍гры̩̍зен̩̍ну̩̍ю кость. +— Ра̩̍зу̩̍ме̩̍ет̩̍ся. Раз̩̍ве не за̩̍мет̩̍но? — мне приш̩̍ло в го̩̍ло̩̍ву, что Гар̩̍ки̩̍на не так лег̩̍ко о̩̍ду̩̍ра̩̍чить, как и̩̍ног̩̍да ка̩̍жет̩̍ся. +— В та̩̍ком слу̩̍ча̩̍е я хо̩̍тел бы пос̩̍мот̩̍реть, как ты заж̩̍жешь све̩̍чу. Ес̩̍ли ты прак̩̍ти̩̍ко̩̍вал̩̍ся так мно̩̍го, как ут̩̍вер̩̍жда̩̍ешь, э̩̍то дол̩̍жно быть лег̩̍ко. +— Я не воз̩̍ра̩̍жа̩̍ю про̩̍тив по̩̍пыт̩̍ки, но как вы са̩̍ми го̩̍во̩̍ри̩̍ли, од̩̍ни у̩̍ро̩̍ки да̩̍ют̩̍ся лег̩̍че, чем дру̩̍ги̩̍е. +Хо̩̍тя я на̩̍пус̩̍кал на се̩̍бя у̩̍ве̩̍рен̩̍ный вид я все‑та̩̍ки пал ду̩̍хом, ког̩̍да в от̩̍вет на вы̩̍зов Гар̩̍ки̩̍на боль̩̍ша̩̍я све̩̍ча поп̩̍лы̩̍ла к ра̩̍бо̩̍че̩̍му сто̩̍лу. За че̩̍ты̩̍ре го̩̍да о̩̍бу̩̍че̩̍ни̩̍я я е̩̍ще не и̩̍мел ус̩̍пе̩̍ха в э̩̍том о̩̍со̩̍бом уп̩̍раж̩̍не̩̍ни̩̍и. Ес̩̍ли Гар̩̍кин не со̩̍би̩̍рал̩̍ся под̩̍пус̩̍кать ме̩̍ня к е̩̍де, по̩̍ка я не пре̩̍ус̩̍пе̩̍ю, то ви̩̍ди̩̍мо мне, дол̩̍го̩̍е вре̩̍мя при̩̍дет̩̍ся хо̩̍дить го̩̍лод̩̍ным. +— Пос̩̍лу̩̍шай, э‑э, Гар̩̍кин, мне приш̩̍ло в го̩̍ло̩̍ву, что я, ве̩̍ро̩̍ят̩̍но, луч̩̍ше мо̩̍гу сос̩̍ре̩̍до̩̍то̩̍чит̩̍ся на пол̩̍ный же̩̍лу̩̍док. +— А мне приш̩̍ло в го̩̍ло̩̍ву, что ты об̩̍ма̩̍ны̩̍ва̩̍ешь. +— Раз̩̍ве мне нель̩̍зя… +— Прис̩̍ту̩̍пай, Скив. +Коль ско̩̍ро он у̩̍пот̩̍ре̩̍бил мо̩̍е над̩̍ле̩̍жа̩̍ще̩̍е и̩̍мя, е̩̍го бы̩̍ло у̩̍же не по̩̍ко̩̍ле̩̍бать. Уж э̩̍то‑то я за э̩̍ти го̩̍ды ус̩̍во̩̍ил хо̩̍ро̩̍шо. Маль̩̍чу̩̍ган, вор, и̩̍ди̩̍от, ре̩̍по̩̍го̩̍ло̩̍вый — все э̩̍ти и̩̍ме̩̍на хо̩̍тя и у̩̍ни̩̍зи̩̍тель̩̍ны, но по̩̍ка он при̩̍ме̩̍нял их, на не̩̍го е̩̍ще мож̩̍но бы̩̍ло пов̩̍ли̩̍ять, но как толь̩̍ко он у̩̍пот̩̍реб̩̍лял мо̩̍е над̩̍ле̩̍жа̩̍ще̩̍е и̩̍мя, э̩̍то ста̩̍но̩̍ви̩̍лось без̩̍на̩̍деж̩̍ным. И впрямь о̩̍гор̩̍чи̩̍тель̩̍но̩̍е по̩̍ло̩̍же̩̍ни̩̍е, ког̩̍да звук тво̩̍е̩̍го собс̩̍твен̩̍но̩̍го и̩̍ме̩̍ни ста̩̍но̩̍вит̩̍ся дур̩̍ным пред̩̍зна̩̍ме̩̍но̩̍ва̩̍ни̩̍ем. +Ну, ес̩̍ли нель̩̍зя ни̩̍как ук̩̍ло̩̍нить̩̍ся, то мне прос̩̍то при̩̍дет̩̍ся пос̩̍вя̩̍тить э̩̍то̩̍му все си̩̍лы. Для та̩̍ко̩̍го де̩̍ла не мо̩̍жет быть ни̩̍ка̩̍ких по̩̍лу̩̍у̩̍си̩̍лий и̩̍ли под̩̍дель̩̍ной сос̩̍ре̩̍до̩̍то̩̍чен̩̍нос̩̍ти. Мне при̩̍дет̩̍ся ис̩̍поль̩̍зо̩̍вать каж̩̍ду̩̍ю ун̩̍ци̩̍ю сво̩̍их сил и у̩̍ме̩̍ни̩̍я для вы̩̍зы̩̍ва̩̍ни̩̍я мо̩̍щи. +Я от̩̍вле̩̍чен̩̍но и̩̍зу̩̍чал све̩̍чу, от̩̍го̩̍ра̩̍жи̩̍ва̩̍я пред̩̍сто̩̍я̩̍щи̩̍е у̩̍си̩̍ли̩̍я от сво̩̍е̩̍го соз̩̍на̩̍ни̩̍я. По̩̍ме̩̍ще̩̍ни̩̍е, заг̩̍ро̩̍мож̩̍ден̩̍ный ра̩̍бо̩̍чий стол, Гар̩̍кин, да̩̍же мой собс̩̍твен̩̍ный го̩̍лос пос̩̍те̩̍пен̩̍но ис̩̍че̩̍за̩̍ли из ви̩̍ду, ког̩̍да я сфо̩̍ку̩̍си̩̍ро̩̍вал̩̍ся на све̩̍че, хо̩̍тя я дав̩̍ным‑дав̩̍но за̩̍пом̩̍нил все е̩̍е о̩̍со̩̍бен̩̍нос̩̍ти. +О̩̍на бы̩̍ла тол̩̍стой, поч̩̍ти шесть дюй̩̍мов в по̩̍пе̩̍реч̩̍ни̩̍ке, что̩̍бы ста̩̍би̩̍ли̩̍зи̩̍ро̩̍вать е̩̍е де̩̍ся̩̍ти̩̍дюй̩̍мо̩̍ву̩̍ю вы̩̍со̩̍ту. На е̩̍е по̩̍вер̩̍хнос̩̍ти я вы̩̍ре̩̍зал мно̩̍го̩̍чис̩̍лен̩̍ны̩̍е мис̩̍ти̩̍чес̩̍ки̩̍е сим̩̍во̩̍лы, ста̩̍ра̩̍тель̩̍но ско̩̍пи̩̍ро̩̍вав их по у̩̍ка̩̍за̩̍ни̩̍ю Гар̩̍ки̩̍на с е̩̍го книг, хо̩̍тя мно̩̍ги̩̍е из них бы̩̍ли час̩̍тич̩̍но у̩̍нич̩̍то̩̍же̩̍ны зат̩̍вер̩̍дев̩̍ши̩̍ми ру̩̍чей̩̍ка̩̍ми вос̩̍ка. Све̩̍ча го̩̍ре̩̍ла мно̩̍го дол̩̍гих ча̩̍сов, ос̩̍ве̩̍ща̩̍я мо̩̍и за̩̍ня̩̍ти̩̍я, но всег̩̍да за̩̍жи̩̍га̩̍лась от о̩̍ча̩̍га, а не от мо̩̍их у̩̍си̩̍лий. +Не̩̍га̩̍тив̩̍но̩̍е мыш̩̍ле̩̍ни̩̍е. Прек̩̍ра̩̍ти. +На э̩̍тот раз я заж̩̍гу све̩̍чу. Я заж̩̍гу е̩̍е по̩̍то̩̍му, что нет ни̩̍ка̩̍ких при̩̍чин, что̩̍бы не за̩̍жечь е̩̍е. +Соз̩̍на̩̍тель̩̍но уг̩̍луб̩̍ля̩̍я ды̩̍ха̩̍ни̩̍е, я на̩̍чал на̩̍кап̩̍ли̩̍вать мощь. Мой мозг су̩̍зил̩̍ся е̩̍ще боль̩̍ше, по̩̍ка в соз̩̍на̩̍ни̩̍и не ос̩̍тал̩̍ся толь̩̍ко свер̩̍нув̩̍ший̩̍ся, по̩̍чер̩̍нев̩̍ший фи̩̍тиль све̩̍чи. +Я — Скив. Мой о̩̍тец свя̩̍зан проч̩̍ны̩̍ми у̩̍за̩̍ми с зем̩̍лей. Мо̩̍я мать бы̩̍ла об̩̍ра̩̍зо̩̍ван̩̍на̩̍я жен̩̍щи̩̍на. Мой у̩̍чи̩̍тель — мас̩̍тер‑маг. Я — Скив. Я заж̩̍гу э̩̍ту све̩̍чу. +Я по̩̍чувс̩̍тво̩̍вал, что сам ста̩̍нов̩̍люсь теп̩̍лым, ког̩̍да во мне на̩̍ча̩̍ла воз̩̍рас̩̍тать э̩̍нер̩̍ги̩̍я. Я сфо̩̍ку̩̍си̩̍ро̩̍вал жар на фи̩̍ти̩̍ле. По̩̍доб̩̍но сво̩̍е̩̍му от̩̍цу, я чер̩̍па̩̍ю си̩̍лу от зем̩̍ли. А зна̩̍ни̩̍я, дан̩̍ны̩̍е мне ма̩̍те̩̍рью, по̩̍доб̩̍ны лин̩̍зе, о̩̍ни да̩̍ют мне воз̩̍мож̩̍ность сфо̩̍ку̩̍си̩̍ро̩̍вать то, что я при̩̍об̩̍рел. Муд̩̍рость мо̩̍е̩̍го у̩̍чи̩̍те̩̍ля нап̩̍рав̩̍ля̩̍ет мо̩̍и у̩̍си̩̍ли̩̍я на те точ̩̍ки все̩̍лен̩̍ной, ко̩̍то̩̍ры̩̍е ве̩̍ро̩̍ят̩̍не̩̍е все̩̍го под̩̍да̩̍дут̩̍ся мо̩̍ей си̩̍ле, мо̩̍ей во̩̍ле. Я — Скив. +Све̩̍ча ос̩̍та̩̍ва̩̍лась не̩̍заж̩̍жен̩̍ной. На лбу у ме̩̍ня выс̩̍ту̩̍пил пот, я на̩̍чал дро̩̍жать от нап̩̍ря̩̍же̩̍ни̩̍я. Нет, э̩̍то неп̩̍ра̩̍виль̩̍но. Мне не сле̩̍ду̩̍ет нап̩̍ря̩̍гать̩̍ся. Рас̩̍слабь̩̍ся. Не пы̩̍тай̩̍ся на̩̍жи̩̍мать. Нап̩̍ря̩̍жен̩̍ность ме̩̍ша̩̍ет те̩̍че̩̍ни̩̍ю. Дай э̩̍нер̩̍ги̩̍и про̩̍хо̩̍дить сво̩̍бод̩̍но, слу̩̍жи ей пас̩̍сив̩̍ным про̩̍вод̩̍ни̩̍ком. Я зас̩̍та̩̍вил мыш̩̍цы ли̩̍ца и плеч об̩̍мяк̩̍нуть и уд̩̍во̩̍ил сво̩̍и у̩̍си̩̍ли̩̍я. +Те̩̍че̩̍ни̩̍е ста̩̍ло за̩̍мет̩̍но ин̩̍тен̩̍сив̩̍не̩̍е. Я поч̩̍ти ви̩̍дел, как э̩̍нер̩̍ги̩̍я стру̩̍ит̩̍ся от ме̩̍ня к мо̩̍ей це̩̍ли. Я вы̩̍тя̩̍нул па̩̍лец, на ко̩̍то̩̍ром е̩̍ще боль̩̍ше сфо̩̍ку̩̍си̩̍ро̩̍ва̩̍лась э̩̍нер̩̍ги̩̍я. Све̩̍ча ос̩̍та̩̍ва̩̍лась не̩̍заж̩̍жен̩̍ной. +Я не мо̩̍гу э̩̍то̩̍го сде̩̍лать. Не̩̍га̩̍тив̩̍но̩̍е мыш̩̍ле̩̍ни̩̍е. Прек̩̍ра̩̍ти. Я — Скив. Я заж̩̍гу све̩̍чу. Мой о̩̍тец… Нет. Не̩̍га̩̍тив̩̍но̩̍е мыш̩̍ле̩̍ни̩̍е. Не по̩̍ла̩̍гай̩̍ся по час̩̍ти сво̩̍ей си̩̍лы на дру̩̍гих. Я заж̩̍гу све̩̍чу, по̩̍то̩̍му что я — Скив. +За э̩̍ту мысль я был воз̩̍наг̩̍раж̩̍ден вне̩̍зап̩̍ным при̩̍ли̩̍вом э̩̍нер̩̍ги̩̍и. Я ум̩̍но̩̍жил у̩̍си̩̍ли̩̍я, о̩̍пья̩̍ня̩̍ясь мо̩̍щью. Я — Скив. Я силь̩̍не̩̍е лю̩̍бо̩̍го из них. Я сбе̩̍жал от по̩̍пыт̩̍ки от̩̍ца при̩̍ко̩̍вать ме̩̍ня к плу̩̍гу, как он при̩̍ко̩̍вал мо̩̍е̩̍го бра̩̍та. Мо̩̍я мать у̩̍мер̩̍ла из‑за сво̩̍е̩̍го и̩̍де̩̍а̩̍лиз̩̍ма, но я ис̩̍поль̩̍зо̩̍вал е̩̍е нас̩̍тав̩̍ле̩̍ни̩̍я для вы̩̍жи̩̍ва̩̍ни̩̍я. Мой у̩̍чи̩̍тель — до̩̍вер̩̍чи̩̍вый ду̩̍рак, взяв̩̍ший в у̩̍че̩̍ни̩̍ки во̩̍ра. Я об̩̍став̩̍лю их всех. Я заж̩̍гу све̩̍чу. Я — Скив. +Те̩̍перь я пла̩̍вал. Я о̩̍соз̩̍на̩̍вал, ка̩̍ки̩̍ми кар̩̍ли̩̍ка̩̍ми де̩̍ла̩̍ли мо̩̍и спо̩̍соб̩̍нос̩̍ти тех, кто ме̩̍ня ок̩̍ру̩̍жал. Не̩̍важ̩̍но, заж̩̍гу я све̩̍чу и̩̍ли нет. Я — Скив. Я — мо̩̍гуч. +Я поч̩̍ти през̩̍ри̩̍тель̩̍но про̩̍тя̩̍нул мыс̩̍лен̩̍но ру̩̍ку к фи̩̍ти̩̍лю. Слов̩̍но в от̩̍вет на мо̩̍ю во̩̍лю по̩̍я̩̍ви̩̍лось ма̩̍лень̩̍ко̩̍е яр̩̍ко̩̍е тле̩̍ни̩̍е. +По̩̍ра̩̍жен̩̍ный, я вып̩̍ря̩̍мил̩̍ся, а по̩̍том мор̩̍гнул, гля̩̍дя на све̩̍чу. Ког̩̍да я э̩̍то сде̩̍лал тле̩̍ни̩̍е ис̩̍чез̩̍ло, ос̩̍та̩̍вив в оз̩̍на̩̍ме̩̍но̩̍ва̩̍ни̩̍е сво̩̍е̩̍го со̩̍бы̩̍ти̩̍я ма̩̍лень̩̍кий бе̩̍лый ды̩̍мок. И я слиш̩̍ком поз̩̍дно со̩̍об̩̍ра̩̍зил, что на̩̍ру̩̍шил сос̩̍ре̩̍до̩̍то̩̍чен̩̍ность. +— Ве̩̍ли̩̍ко̩̍леп̩̍но, маль̩̍чу̩̍ган! +Гар̩̍кин вдруг о̩̍ка̩̍зал̩̍ся ря̩̍дом со мной, с эн̩̍ту̩̍зи̩̍аз̩̍мом ко̩̍ло̩̍тя ме̩̍ня по пле̩̍чу. Дол̩̍го ли он был тут, я не знал и не ин̩̍те̩̍ре̩̍со̩̍вал̩̍ся. +— О̩̍на по̩̍гас̩̍ла, — у̩̍ны̩̍ло про̩̍го̩̍во̩̍рил я. +— Э̩̍то не̩̍важ̩̍но. Ты за̩̍жег е̩̍е. Те̩̍перь у те̩̍бя есть у̩̍ве̩̍рен̩̍ность. В сле̩̍ду̩̍ю̩̍щий раз э̩̍то бу̩̍дет лег̩̍че. Кля̩̍нусь звез̩̍да̩̍ми, мы е̩̍ще сде̩̍ла̩̍ем из те̩̍бя ма̩̍га. Вот, ты, на̩̍вер̩̍но̩̍е, про̩̍го̩̍ло̩̍дал̩̍ся. +Я ед̩̍ва ус̩̍пел вов̩̍ре̩̍мя по̩̍нять ру̩̍ку и пе̩̍рех̩̍ва̩̍тить нож̩̍ку я̩̍ще̩̍роп̩̍ти̩̍цы, преж̩̍де чем та шмяк̩̍ну̩̍ла ме̩̍ня по мор̩̍де. О̩̍на у̩̍же ос̩̍ты̩̍ла. +— Не ста̩̍ну скры̩̍вать, маль̩̍чу̩̍ган, я у̩̍же на̩̍чал от̩̍ча̩̍и̩̍вать̩̍ся. Что сде̩̍ла̩̍ло э̩̍тот у̩̍рок та̩̍ким труд̩̍ным? Раз̩̍ве те̩̍бе не при̩̍хо̩̍ди̩̍ло в го̩̍ло̩̍ву, что ты мо̩̍жешь вос̩̍поль̩̍зо̩̍вать̩̍ся э̩̍тим зак̩̍ли̩̍на̩̍ни̩̍ем для по̩̍лу̩̍че̩̍ни̩̍я до̩̍ба̩̍воч̩̍но̩̍го све̩̍та, ког̩̍да бу̩̍дешь взла̩̍мы̩̍вать за̩̍мок, и̩̍ли же для ус̩̍тройс̩̍тва по̩̍жа̩̍ра, что̩̍бы от̩̍влечь вни̩̍ма̩̍ни̩̍е? +— Я ду̩̍мал об э̩̍том, но до̩̍ба̩̍воч̩̍ный свет мо̩̍жет как раз прив̩̍лечь не̩̍же̩̍ла̩̍тель̩̍но̩̍е вни̩̍ма̩̍ни̩̍е. Что ка̩̍са̩̍ет̩̍ся от̩̍вле̩̍че̩̍ни̩̍я е̩̍го, то бо̩̍юсь, что при э̩̍том кто‑то пос̩̍тра̩̍да̩̍ет. Я не хо̩̍чу э̩̍то̩̍го. Я прос̩̍то… +Я ос̩̍та̩̍но̩̍вил̩̍ся, слиш̩̍ком поз̩̍дно со̩̍об̩̍ра̩̍зив, что го̩̍во̩̍рю. Тя̩̍же̩̍лый у̩̍дар гар̩̍кин̩̍ско̩̍го ку̩̍ла̩̍ка от̩̍пра̩̍вил ме̩̍ня с та̩̍бу̩̍ре̩̍та на пол. +— Так я и ду̩̍мал! Ты все е̩̍ще за̩̍мыш̩̍ля̩̍ешь стать во̩̍ром! Ты хо̩̍чешь ис̩̍поль̩̍зо̩̍вать мо̩̍ю ма̩̍ги̩̍ю для краж! +Он был у̩̍жа̩̍сен в сво̩̍ей я̩̍рос̩̍ти, но на сей раз я дал е̩̍му от̩̍пор. +Ну и что из э̩̍то̩̍го ? — за̩̍ры̩̍чал я. — Э̩̍то ку̩̍да луч̩̍ше, чем го̩̍ло̩̍дать. И во̩̍об̩̍ще, что та̩̍ко̩̍го хо̩̍ро̩̍ше̩̍го в том, что̩̍бы быть ма̩̍гом? Я хо̩̍чу ска̩̍зать, что твой здеш̩̍ний об̩̍раз жиз̩̍ни вы̩̍зы̩̍ва̩̍ет у ме̩̍ня прос̩̍то не̩̍у̩̍дер̩̍жи̩̍мо̩̍е же̩̍ла̩̍ни̩̍е дос̩̍тичь то̩̍го же. +Я по̩̍ка̩̍зал на заг̩̍ро̩̍мож̩̍ден̩̍ну̩̍ю ком̩̍на̩̍ту, сос̩̍тав̩̍ляв̩̍шу̩̍ю все внут̩̍рен̩̍не̩̍е прос̩̍транс̩̍тво хи̩̍жи̩̍ны. +— Пос̩̍лу̩̍шай̩̍те, как жа̩̍лу̩̍ет̩̍ся э̩̍тот вол̩̍чо̩̍нок! — фыр̩̍кнул Гар̩̍кин. — Он был дос̩̍та̩̍точ̩̍но хо̩̍рош для те̩̍бя, ког̩̍да зи̩̍ма выг̩̍на̩̍ла те̩̍бя во̩̍ро̩̍вать из ле̩̍са. «Э̩̍то ку̩̍да луч̩̍ше, чем спать под кус̩̍том», — ска̩̍зал ты. +— И все е̩̍ще луч̩̍ше. Вот по̩̍че̩̍му я все е̩̍ще здесь. Но я не со̩̍би̩̍ра̩̍юсь про̩̍во̩̍дить здесь ос̩̍та̩̍ток сво̩̍ей жиз̩̍ни. Пря̩̍тать̩̍ся в лес̩̍ной из̩̍буш̩̍ке — не мо̩̍е пред̩̍став̩̍ле̩̍ни̩̍е о бу̩̍ду̩̍щем. Ты жил, пи̩̍та̩̍ясь кор̩̍ня̩̍ми и я̩̍го̩̍да̩̍ми, по̩̍ка я не при̩̍шел и не на̩̍чал ло̩̍вить мя̩̍со для о̩̍ча̩̍га. Мо̩̍жет быть, ты, Гар̩̍кин, так и пред̩̍став̩̍ля̩̍ешь се̩̍бе счас̩̍тли̩̍ву̩̍ю жизнь, но я — нет! +Нес̩̍коль̩̍ко дол̩̍гих мгно̩̍ве̩̍ний мы жгли друг дру̩̍га взгля̩̍да̩̍ми. Те̩̍перь, вып̩̍лес̩̍нув свой гнев, я бо̩̍ле̩̍е чем ма̩̍лость по̩̍ба̩̍и̩̍вал̩̍ся. Хо̩̍тя я и не и̩̍мел ши̩̍ро̩̍ко̩̍го о̩̍пы̩̍та в э̩̍той об̩̍лас̩̍ти, но по̩̍доз̩̍ре̩̍вал, что нас̩̍ме̩̍хать̩̍ся над ма̩̍гом +— не са̩̍мый луч̩̍ший спо̩̍соб о̩̍бес̩̍пе̩̍чить се̩̍бе дол̩̍го̩̍е и здо̩̍ро̩̍во̩̍е бу̩̍ду̩̍ще̩̍е. +У̩̍ди̩̍ви̩̍тель̩̍но, но пер̩̍вым ус̩̍ту̩̍пил Гар̩̍кин. Он вдруг по̩̍ту̩̍пил взгляд и скло̩̍нил го̩̍ло̩̍ву, пре̩̍дос̩̍та̩̍вив мне о̩̍боз̩̍ре̩̍вать мас̩̍су не̩̍че̩̍сан̩̍ных во̩̍лос у не̩̍го на ма̩̍куш̩̍ке. +— На̩̍вер̩̍но, ты прав, Скив, — го̩̍лос е̩̍го стал стран̩̍но мяг̩̍ким. — На̩̍вер̩̍но̩̍е, я по̩̍ка̩̍зы̩̍вал те̩̍бе все тру̩̍ды ма̩̍ги̩̍и, но не воз̩̍наг̩̍раж̩̍де̩̍ни̩̍е за них. Я пос̩̍то̩̍ян̩̍но за̩̍бы̩̍ва̩̍ю, как по̩̍дав̩̍ле̩̍на ма̩̍ги̩̍я в э̩̍тих кра̩̍ях. +Он под̩̍нял го̩̍ло̩̍ву, встре̩̍тил̩̍ся со мной взгля̩̍дом, и я сод̩̍рог̩̍нул̩̍ся как от у̩̍да̩̍ра. В глу̩̍би̩̍не е̩̍го глаз го̩̍рел ни̩̍ког̩̍да не ви̩̍ден̩̍ный мной рань̩̍ше о̩̍гонь. +— Знай же, Скив, что не все кра̩̍я по̩̍хо̩̍жи на э̩̍тот, и я не всег̩̍да был та̩̍ким как сей̩̍час. В кра̩̍ях, где ма̩̍ги̩̍ю приз̩̍на̩̍ют, а не стра̩̍шат̩̍ся, как здесь, те кто на̩̍хо̩̍дит̩̍ся у влас̩̍ти, у̩̍ва̩̍жа̩̍ют и за̩̍ка̩̍зы̩̍ва̩̍ют е̩̍е. Там у̩̍ме̩̍лый маг, дер̩̍жа̩̍щий̩̍ся на̩̍че̩̍ку, мо̩̍жет по̩̍жать в сто раз боль̩̍ше бо̩̍гатс̩̍тва, чем ты со̩̍би̩̍ра̩̍ешь̩̍ся до̩̍быть во̩̍ровс̩̍твом, и при̩̍об̩̍рес̩̍ти та̩̍ку̩̍ю власть, что… +Он вдруг о̩̍бор̩̍вал сво̩̍ю речь и по̩̍мо̩̍тал го̩̍ло̩̍вой, слов̩̍но про̩̍чи̩̍ща̩̍я е̩̍е. Ког̩̍да он сно̩̍ва от̩̍крыл гла̩̍за, о̩̍гонь, у̩̍ви̩̍ден̩̍ный мной ра̩̍не̩̍е яр̩̍ко го̩̍ря̩̍щим в е̩̍го гла̩̍зах спал до тле̩̍ни̩̍я. +— Но сло̩̍ва не про̩̍из̩̍во̩̍дят, я ви̩̍жу, на те̩̍бя впе̩̍чат̩̍ле̩̍ни̩̍я, не так ли, маль̩̍чу̩̍ган? Пой̩̍дем, я по̩̍ка̩̍жу те̩̍бе не̩̍боль̩̍шу̩̍ю де̩̍монс̩̍тра̩̍ци̩̍ю той влас̩̍ти, ко̩̍то̩̍ру̩̍ю ты од̩̍наж̩̍ды смо̩̍жешь дер̩̍жать в сво̩̍их ру̩̍ках — ес̩̍ли бу̩̍дешь прак̩̍ти̩̍ко̩̍вать̩̍ся в сво̩̍их у̩̍ро̩̍ках. +Ве̩̍се̩̍лость в е̩̍го го̩̍ло̩̍се бы̩̍ла при̩̍нуж̩̍ден̩̍ной. В от̩̍вет я кив̩̍ком вы̩̍ра̩̍зил сво̩̍е сог̩̍ла̩̍си̩̍е. По прав̩̍де го̩̍во̩̍ря, я не нуж̩̍дал̩̍ся ни в ка̩̍кой де̩̍монс̩̍тра̩̍ци̩̍и. Е̩̍го ти̩̍ха̩̍я крат̩̍ка̩̍я речь наг̩̍на̩̍ла на ме̩̍ня ку̩̍да боль̩̍ше бла̩̍го̩̍го̩̍вей̩̍но̩̍го стра̩̍ха, чем лю̩̍ба̩̍я гнев̩̍на̩̍я ти̩̍ра̩̍да и̩̍ли де̩̍монс̩̍тра̩̍ци̩̍я, но я не пос̩̍мел про̩̍ти̩̍во̩̍ре̩̍чить е̩̍му в та̩̍ко̩̍е вре̩̍мя. +Не ду̩̍ма̩̍ю, что̩̍бы он дейс̩̍тви̩̍тель̩̍но за̩̍ме̩̍тил мой от̩̍вет. Он у̩̍же ша̩̍гал к боль̩̍шой пен̩̍таг̩̍рам̩̍ме, нав̩̍сег̩̍да на̩̍чер̩̍тан̩̍ной на по̩̍лу хи̩̍жи̩̍ны. На хо̩̍ду он сде̩̍лал рас̩̍се̩̍ян̩̍ный жест, и пок̩̍ры̩̍та̩̍я са̩̍жей мед̩̍на̩̍я жа̩̍ров̩̍ня шмыг̩̍ну̩̍ла со сво̩̍е̩̍го мес̩̍та в уг̩̍лу встре̩̍тить е̩̍го в центр пен̩̍таг̩̍рам̩̍мы. +У ме̩̍ня хва̩̍ти̩̍ло вре̩̍ме̩̍ни на вос̩̍по̩̍ми̩̍на̩̍ни̩̍я о том, что э̩̍та жа̩̍ров̩̍ня и прив̩̍лек̩̍ла ме̩̍ня спер̩̍ва к Гар̩̍ки̩̍ну. Я вспом̩̍нил, как пер̩̍вый раз смот̩̍рел че̩̍рез ок̩̍но е̩̍го из̩̍буш̩̍ки, стре̩̍мясь раз̩̍гля̩̍деть цен̩̍ны̩̍е ве̩̍щи для пос̩̍ле̩̍ду̩̍ю̩̍щий кра̩̍жи. Я у̩̍ви̩̍дел Гар̩̍ки̩̍на та̩̍ким же, ка̩̍ким час̩̍то ви̩̍дел е̩̍го с тез пор, — бес̩̍по̩̍кой̩̍но рас̩̍ха̩̍жи̩̍ва̩̍ю̩̍щим взад‑впе̩̍ред по по̩̍ме̩̍ще̩̍ни̩̍ю, ут̩̍кнув̩̍шись но̩̍сом в кни̩̍гу. Э̩̍то са̩̍мо по се̩̍бе бы̩̍ло у̩̍ди̩̍ви̩̍тель̩̍ным зре̩̍ли̩̍щем, и̩̍бо чте̩̍ни̩̍е — не са̩̍мо̩̍е о̩̍быч̩̍но̩̍е вре̩̍мяп̩̍реп̩̍ро̩̍вож̩̍де̩̍ни̩̍е в э̩̍той об̩̍лас̩̍ти. Но мо̩̍е вни̩̍ма̩̍ни̩̍е зах̩̍ва̩̍ти̩̍ла жа̩̍ров̩̍ня. О̩̍на ска̩̍ка̩̍ла по по̩̍ме̩̍ще̩̍ни̩̍ю, сле̩̍ду̩̍я за Гар̩̍ки̩̍ным, слов̩̍но не̩̍тер̩̍пе̩̍ли̩̍вый ще̩̍нок, ко̩̍то̩̍рый нем̩̍но̩̍го слиш̩̍ком веж̩̍лив, что̩̍бы прфгнуть на сво̩̍е̩̍го хо̩̍зя̩̍и̩̍на для прив̩̍ле̩̍че̩̍ни̩̍я е̩̍го вни̩̍ма̩̍ни̩̍я. За̩̍тем Гар̩̍кин о̩̍тор̩̍вал̩̍ся от кни̩̍ги, за̩̍дум̩̍чи̩̍во ус̩̍та̩̍вил̩̍ся на свой ра̩̍бо̩̍чий стол, за̩̍тем кив̩̍нул, при̩̍няв ре̩̍ше̩̍ни̩̍е и сде̩̍лал жест. Из ку̩̍чи вся̩̍кой вся̩̍чи̩̍ны под̩̍нял̩̍ся гор̩̍шок с не̩̍о̩̍поз̩̍нан̩̍ным со̩̍дер̩̍жи̩̍мым и поп̩̍лыл к е̩̍го под̩̍жи̩̍дав̩̍шей ру̩̍ке. Он пой̩̍мал е̩̍го, сно̩̍ва све̩̍рил̩̍ся с кни̩̍гой и, не под̩̍ни̩̍ма̩̍я глаз, вы̩̍лил часть. Быс̩̍тра̩̍я, как кош̩̍ка, жа̩̍ров̩̍ня, про̩̍тис̩̍ну̩̍лась е̩̍му под ру̩̍ку и пой̩̍ма̩̍ла вы̩̍ли̩̍то̩̍е, преж̩̍де чем о̩̍но дос̩̍тиг̩̍ло по̩̍ла. Вот так то я и поз̩̍на̩̍ко̩̍мил̩̍ся с ма̩̍ги̩̍ей. +Что‑то рыв̩̍ком вер̩̍ну̩̍ло мо̩̍е вни̩̍ма̩̍ни̩̍е к нас̩̍то̩̍я̩̍ще̩̍му. Что и̩̍мен̩̍но? Я про̩̍ве̩̍рил, как и̩̍дут де̩̍ла у Гар̩̍ки̩̍на. Он все е̩̍ще тру̩̍дил̩̍ся, по̩̍лус̩̍кры̩̍тый пла̩̍ва̩̍ю̩̍щим об̩̍ла̩̍ком пу̩̍зырь̩̍ков и кув̩̍ши̩̍нов, что‑то бор̩̍мо̩̍ча, ког̩̍да вы̩̍дер̩̍ги̩̍вал о̩̍дин та̩̍кой из воз̩̍ду̩̍ха и до̩̍бав̩̍ля̩̍я е̩̍го со̩̍дер̩̍жи̩̍мо̩̍е в жа̩̍ров̩̍ню. Над чем бы он там ни тру̩̍дил̩̍ся, зре̩̍ли̩̍ще о̩̍бе̩̍ща̩̍ло быть зах̩̍ва̩̍ты̩̍ва̩̍ю̩̍щим. +За̩̍тем я сно̩̍ва ус̩̍лы̩̍шал е̩̍го — приг̩̍лу̩̍шен̩̍ный шаг за стен̩̍кой из̩̍буш̩̍ки. Но ведь э̩̍то бы̩̍ло не̩̍воз̩̍мож̩̍но! Гар̩̍кин всег̩̍да ус̩̍та̩̍нав̩̍ли̩̍вал… Я на̩̍чал ко̩̍пать̩̍ся в па̩̍мя̩̍ти и не мог вспом̩̍нить, ус̩̍та̩̍но̩̍вил ли Гар̩̍кин за̩̍щит̩̍ный по̩̍лог, преж̩̍де чем прис̩̍ту̩̍пить к ра̩̍бо̩̍те. Не̩̍ле̩̍по. Ос̩̍то̩̍рож̩̍ность бы̩̍ла пер̩̍вым и са̩̍мым важ̩̍ным де̩̍лом, что вдол̩̍бил в ме̩̍ня Гар̩̍кин, и час̩̍тью ос̩̍то̩̍рож̩̍нос̩̍ти яв̩̍ля̩̍лось ус̩̍та̩̍нов̩̍ле̩̍ни̩̍е за̩̍щит̩̍нго по̩̍ло̩̍га, ко̩̍то̩̍рый не̩̍об̩̍хо̩̍ди̩̍мо бы̩̍ло ус̩̍та̩̍но̩̍вить пе̩̍ред ра̩̍бо̩̍той. Он не мог за̩̍быть… но он был нес̩̍коль̩̍ко рас̩̍па̩̍лен и от̩̍вле̩̍чен. +Я все е̩̍ще ре̩̍шал, не сле̩̍ду̩̍ет ли мне прер̩̍вать труд Гар̩̍ки̩̍на, ког̩̍да он вдруг от̩̍сту̩̍пил на шаг от жа̩̍ров̩̍ни. Он па̩̍ра̩̍ли̩̍зо̩̍вал ме̩̍ня взгля̩̍дом и пре̩̍дуп̩̍реж̩̍де̩̍ни̩̍е за̩̍мер̩̍ло у ме̩̍ня в го̩̍ло̩̍ве. Не вре̩̍мя бы̩̍ло на̩̍вя̩̍зы̩̍вать при та̩̍кой си̩̍ту̩̍а̩̍ци̩̍и ре̩̍аль̩̍ность. В гла̩̍за е̩̍го вер̩̍нул̩̍ся о̩̍гонь, силь̩̍ней, чем преж̩̍де. +— Да̩̍же де̩̍монс̩̍тра̩̍ци̩̍я дол̩̍жна дать те̩̍бе у̩̍рок, — по̩̍у̩̍ча̩̍ю̩̍ще за̩̍я̩̍вил он. — Кон̩̍троль, Скив. Кон̩̍троль — э̩̍то оп̩̍лот ма̩̍ги̩̍и. Бес̩̍кон̩̍троль̩̍на̩̍я мощь — э̩̍то ка̩̍тас̩̍тро̩̍фа. Вот по̩̍че̩̍му ты прак̩̍ти̩̍ку̩̍ешь̩̍ся с пе̩̍ром, хо̩̍тя мо̩̍жешь пе̩̍ред̩̍ви̩̍гать ку̩̍да бо̩̍ле̩̍е круп̩̍ны̩̍е пред̩̍ме̩̍ты. Кон̩̍троль. Да̩̍же тво̩̍и скуд̩̍ны̩̍е си̩̍лы бы̩̍ли бы о̩̍пас̩̍ны без кон̩̍тро̩̍ля, и я не ста̩̍ну у̩̍чить те̩̍бя ов̩̍ла̩̍де̩̍вать боль̩̍ши̩̍ми си̩̍ла̩̍ми, по̩̍ка ты не на̩̍у̩̍чишь̩̍ся э̩̍то̩̍му кон̩̍тро̩̍лю. +Он шаг̩̍нул прочь из пен̩̍таг̩̍рам̩̍мы. +— Что̩̍бы про̩̍де̩̍монс̩̍три̩̍ро̩̍вать те̩̍бе цен̩̍ность кон̩̍тро̩̍ля, я сей̩̍час вы̩̍зо̩̍ву де̩̍мо̩̍на, су̩̍щес̩̍тво из дру̩̍го̩̍го ми̩̍ра. Он мо̩̍гуч, зло̩̍бен и жес̩̍ток и у̩̍бьет нас о̩̍бо̩̍их, ес̩̍ли е̩̍му дать шанс. И все же, нес̩̍мот̩̍ря на э̩̍то, бо̩̍ять̩̍ся нам е̩̍го не̩̍за̩̍чем, по̩̍то̩̍му что он бу̩̍дет на̩̍хо̩̍дит̩̍ся под кон̩̍тро̩̍лем. Он не смо̩̍жет при̩̍чи̩̍нить нам вре̩̍да, нам и̩̍ли е̩̍ще че̩̍му‑ни̩̍будь в э̩̍том ми̩̍ре, по̩̍ку̩̍да он со̩̍дер̩̍жит̩̍ся в э̩̍той пен̩̍таг̩̍рам̩̍ме. А те̩̍перь смот̩̍ри, Скив. Смот̩̍ри и у̩̍чись. +Ска̩̍зав э̩̍то, он о̩̍пять по̩̍вер̩̍нул̩̍ся к жа̩̍ров̩̍не. Он раз̩̍вел ру̩̍ки в сто̩̍ро̩̍ны, и ра̩̍зом о̩̍жи̩̍ли пять све̩̍чей по уг̩̍лам пен̩̍таг̩̍рам̩̍мы и ли̩̍ни̩̍и е̩̍е на̩̍ча̩̍ли пы̩̍лать жут̩̍ким го̩̍лу̩̍бым све̩̍том. Нес̩̍коль̩̍ко ми̩̍нут ца̩̍ри̩̍ла ти̩̍ши̩̍на, а за̩̍тем он стал ти̩̍хо и не̩̍раз̩̍бор̩̍чи̩̍во чи̩̍тать на̩̍рас̩̍пев зак̩̍ли̩̍на̩̍ни̩̍е. Из жа̩̍ров̩̍ни по̩̍я̩̍ви̩̍лась ни̩̍точ̩̍ка ды̩̍ма, но не под̩̍ня̩̍лась к по̩̍тол̩̍ку, а по̩̍ли̩̍лась на пол и на̩̍ча̩̍ла об̩̍ра̩̍зо̩̍вы̩̍вать не̩̍боль̩̍шо̩̍е об̩̍ла̩̍ко, ко̩̍то̩̍ро̩̍е пуль̩̍си̩̍ро̩̍ва̩̍ло и бур̩̍ли̩̍ло. Пе̩̍ни̩̍е Гар̩̍ки̩̍на сде̩̍ла̩̍лось е̩̍ще гром̩̍че, и об̩̍ла̩̍ко вы̩̍рос̩̍ло, по̩̍тем̩̍не̩̍ло. Жа̩̍ров̩̍ню у̩̍же поч̩̍ти не бы̩̍ло вид̩̍но, но там… в глу̩̍би̩̍не об̩̍ла̩̍ка… что‑то при̩̍об̩̍ре̩̍та̩̍ло о̩̍чер̩̍та̩̍ни̩̍я… +— Иш̩̍тван шлет те̩̍бе при̩̍вет, Гар̩̍кин! +При э̩̍тих сло̩̍вах я чуть бы̩̍ло не вып̩̍рыг̩̍нул из собс̩̍твен̩̍ной шку̩̍ры. О̩̍ни проз̩̍ву̩̍ча̩̍ли внут̩̍ри хи̩̍жи̩̍ны, но не из пен̩̍таг̩̍рам̩̍мы! Я рез̩̍ко о̩̍бер̩̍нул̩̍ся к их ис̩̍точ̩̍ни̩̍ку. Не̩̍пос̩̍редс̩̍твен̩̍но в две̩̍рях сто̩̍я̩̍ла ос̩̍ле̩̍пи̩̍тель̩̍на̩̍я фи̩̍гу̩̍ра в пы̩̍ла̩̍ю̩̍щем зо̩̍ло̩̍том пла̩̍ще. Ка̩̍кой‑то бе̩̍зум̩̍ный миг я ду̩̍мал, что э̩̍то де̩̍мон от̩̍ве̩̍тил на вы̩̍зов Гар̩̍ки̩̍на, но по̩̍том я у̩̍ви̩̍дел ар̩̍ба̩̍лет. Э̩̍то был, ко̩̍неч̩̍но, че̩̍ло̩̍век, спо̩̍ру нет, но за̩̍ря̩̍жен̩̍ный и взве̩̍ден̩̍ный ар̩̍ба̩̍лет в е̩̍го ру̩̍ках ма̩̍ло спо̩̍собс̩̍тво̩̍вал мо̩̍е̩̍му ду̩̍шев̩̍но̩̍му спо̩̍койс̩̍тви̩̍ю. +Гар̩̍кин да̩̍же не о̩̍бер̩̍нул̩̍ся. +— Не сей̩̍час, ду̩̍рак! — ряв̩̍кнул он. +— О̩̍хо̩̍та бы̩̍ла дол̩̍гой, Гар̩̍кин, — про̩̍дол̩̍жал тот, слов̩̍но не слы̩̍ша. — Ты хо̩̍ро̩̍шо спря̩̍тал̩̍ся, но не рас̩̍счи̩̍ты̩̍вал̩̍ся же ты в са̩̍мом де̩̍ле скрыть̩̍ся от… +— Ты сме̩̍ешь?! — у̩̍жас̩̍ный в сво̩̍ем гне̩̍ве Гар̩̍кин рез̩̍ко о̩̍бер̩̍нул̩̍ся. +Во̩̍шед̩̍ший у̩̍ви̩̍дел те̩̍перь ли̩̍цо Гар̩̍ки̩̍на, у̩̍ви̩̍дел е̩̍го гла̩̍за, е̩̍го ли̩̍цо ис̩̍ка̩̍зи̩̍лось в гро̩̍тес̩̍кной мас̩̍ке стра̩̍ха. Он реф̩̍лек̩̍тор̩̍но выс̩̍тре̩̍лил из ар̩̍ба̩̍ле̩̍та, но слиш̩̍ком поз̩̍дно. Я не ви̩̍дел, что и̩̍мен̩̍но сде̩̍лал Гар̩̍кин, но во̩̍шед̩̍ший вдруг ис̩̍чез в сло̩̍е пла̩̍ме̩̍ни. Он прон̩̍зи̩̍тель̩̍но зак̩̍ри̩̍чал в а̩̍го̩̍ни̩̍и, и у̩̍пал на пол. Пла̩̍мя ис̩̍чез̩̍ло так же вне̩̍зап̩̍но, как и по̩̍я̩̍вив̩̍шись, ос̩̍та̩̍вив толь̩̍ко ды̩̍мя̩̍щий̩̍ся труп, как до̩̍ка̩̍за̩̍тельс̩̍тво то̩̍го, что о̩̍но дейс̩̍тви̩̍тель̩̍но су̩̍щес̩̍тво̩̍ва̩̍ло. +Нес̩̍коль̩̍ко мгно̩̍ве̩̍ний я ос̩̍та̩̍вал̩̍ся при̩̍рос̩̍шим к мес̩̍ту преж̩̍де чем смог дви̩̍гать̩̍ся и̩̍ли го̩̍во̩̍рить. +— Гар̩̍кин… — про̩̍из̩̍нес я на̩̍ко̩̍нец. — Я… Гар̩̍кин! +Те̩̍ло е̩̍го меш̩̍ком ле̩̍жа̩̍ло на по̩̍лу. Я од̩̍ним прыж̩̍ком о̩̍чу̩̍тил̩̍ся ря̩̍дом с ним, но о̩̍чу̩̍тил̩̍ся че̩̍рес̩̍чур поз̩̍дно. Из е̩̍го гру̩̍ди тор̩̍ча̩̍ла ар̩̍ба̩̍лет̩̍на̩̍я стре̩̍ла. Гар̩̍кин дал мне свой пос̩̍лед̩̍ний у̩̍рок. +Ког̩̍да я наг̩̍нул̩̍ся кос̩̍нуть̩̍ся е̩̍го те̩̍ла, то за̩̍ме̩̍тил неч̩̍то та̩̍ко̩̍е, от че̩̍го кровь зас̩̍ты̩̍ла у ме̩̍ня в жи̩̍лах. Е̩̍го труп по̩̍лус̩̍кры̩̍вал по̩̍гас̩̍шу̩̍ю све̩̍чу в се̩̍вер̩̍ном уг̩̍лу пен̩̍таг̩̍рам̩̍мы. Ли̩̍ни̩̍и боль̩̍ше не пы̩̍ла̩̍ли го̩̍лу̩̍биз̩̍ной. За̩̍щит̩̍но̩̍е по̩̍ле зак̩̍ли̩̍на̩̍ний про̩̍па̩̍ло. +С му̩̍чи̩̍тель̩̍ным у̩̍си̩̍ли̩̍ем я под̩̍нял го̩̍ло̩̍ву и встре̩̍тил̩̍ся взгля̩̍дом с па̩̍рой жел̩̍тых глаз с кра̩̍пин̩̍ка̩̍ми зо̩̍ло̩̍та, не при̩̍над̩̍ле̩̍жа̩̍щих э̩̍то̩̍му ми̩̍ру. diff --git a/onefile/data/output-demo/writing.svg b/onefile/data/output-demo/writing.svg new file mode 100644 index 0000000..d847dd9 --- /dev/null +++ b/onefile/data/output-demo/writing.svg @@ -0,0 +1,922 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/onefile/data/output/.placeholder b/onefile/data/output/.placeholder new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/onefile/data/output/.placeholder diff --git a/onefile/data/scheme.png b/onefile/data/scheme.png new file mode 100644 index 0000000..8083ede Binary files /dev/null and b/onefile/data/scheme.png differ diff --git a/onefile/data/text-to-split.txt b/onefile/data/text-to-split.txt new file mode 100644 index 0000000..21db9e7 --- /dev/null +++ b/onefile/data/text-to-split.txt @@ -0,0 +1,100 @@ +Роберт Асприн +Ещё один великолепный миф + + +Эта книга посвящается Борку Неуничтожимому (известному меньшим смертным как Джордж Хант), чья грубоватая, но верная дружба помогла мне преодолеть много кризисов в прошлом… и, вероятно, в будущем! + +ГЛАВА 1 + +«Есть многое на свете, друг Горацио, что и не снилось нашим мудрецам». +Гамлет + +Одно из немногих все искупляющих качеств наставников, думаю я, заключается в том, что их при случае можно одурачить. Это было верно, когда мать учила меня читать, это было верно, когда отец пытался научить меня быть фермером, и верно теперь, когда я обучаюсь магии. +— Ты не практиковался! — прервал мои размышления резкий упрек Гаркина. +— А вот и нет! — возразил я. — Это просто трудное упражнение. +Словно в ответ левитируемое мною перо начало дрожать и колебаться в воздухе. +— Ты не сосредотачиваешься! — обвинил он меня. +— Это все ветер, — не согласился я. Мне хотелось добавить: «От твоего умного языка», но я не посмел. Гаркин в самом начале наших уроков продемонстрировал свое неумение ценить дерзких учеников. +— Ветер! — презрительно фыркнул он, передразнивая меня. — Вот так надо, болван! +Мой мысленный контакт с предметом моей сосредоточенности прервался и остановился, когда перо дернулось и взмыло к потолку. Оно дернулось и остановилось, словно вонзившись во что‑то, хотя все еще находилось в футе от деревянных стропил, а потом стало медленно вращаться в горизонтальной плоскости. Потом, оно также медленно стало вращаться вокруг своей оси, а затем поменяло концы и начало скользить по невидимому кругу, словно подхваченный смерчем лист. +Я рискнул взглянуть на Гаркина. Тот развалился в кресле, болтал ногами, явно посвятив свое внимание пожиранию жареной ножки ящероптицы, пойманной, могу добавить, мной. Ничего себе сосредоточенность! +Он вдруг поднял глаза и наши взгляды встретились. Отворачиваться было слишком поздно и я поэтому просто поглядел на него в ответ. +— Проголодался? — его измазанная жиром борода с проседью сделалась вдруг обрамленной волчьей усмешкой. — Тогда покажи мне, много ли ты практиковался? +Мне потребовалось всего мгновение, чтобы понять, что он имел ввиду, а затем я с отчаяньем поднял глаза. Перо кувыркалось к полу едва на высоте плеч от приземления. Заставив внезапное напряжение покинуть мое тело, я мысленно протянул руку… мягко… образуя подушку… не сшибая его… +Перо остановилось в двух ладонях от пола. Я услышал тихий смешок Гаркина, но не позволил этому нарушать мою сосредоточенность. Я три года не давал перу коснуться пола, оно не коснется его и сейчас. +Я медленно поднял его, пока оно не воспарило на уровне глаз. Обернув его своей мыслью, я стал вращать его вокруг своей оси, а затем заставил поменять концы. Покуда я проделывал с ним эти упражнения, его движения не были такими гладкими и уверенными, как тогда, когда этой задаче уделял внимание Гаркин, но оно безошибочно двигалось по заданному ему пути. +Хотя я не практиковался с пером, я все‑таки практиковался. Когда Гаркина не бывало поблизости или он был занят своими собственными исследованиями, я посвящал большую часть своего времени левитированию металлических предметов, а если точнее ключей. Каждому виду левитации присущи свои собственным проблемы. С металлом трудно работать, это материал инертный. Перо, бывшее когда‑то частью живого существа, откликалось легче… намного легче. Для поднятия металла требовалось усилие, для маневрирования пером требовалась легкость. Я предпочитал работать с металлом. Мне виделось более прямое применение этого умения в избранной мной профессии. +— Достаточно хорошо, мальчуган! А теперь положи его обратно в книгу. +Я улыбнулся про себя. По этой части я напрактиковался не из‑за ее потенциального применения, а потому, что это меня забавляло. +Книга лежала в раскрытом виде в конце рабочего стола. Я опустил перо по длинной ленивой спирали, давая ему слегка пройтись по страницам книги, поднял вверх по крутой дуге, остановил и повел обратно. А когда оно во второй раз приблизилось к книге, я освободил часть своего для броска к книге. Когда перо чиркнуло по страницам, книга захлопнулась, словно челюсти голодного хищника на метательном снаряде в пределах их досягаемости. +— Хммм… — протянул Гаркин. — Чуточку напоказ, но эффектно. +— Всего лишь малость того, что я разработал, пока практиковался, — небрежно бросил я, мысленно протянув руку к другой ножке ящероптицы. Однако вместо того, чтобы грациозно проплыть к моей руке, она осталась на деревянной тарелке, словно пустила туда корни. +— Не так быстро, мой маленький воришка. Значит, ты практиковался, да? — он задумчиво огладил бороду, не выпуская из рук полуобгрызенную кость. +— Разумеется. Разве не заметно? — мне пришло в голову, что Гаркина не так легко одурачить, как иногда кажется. +— В таком случае я хотел бы посмотреть, как ты зажжешь свечу. Если ты практиковался так много, как утверждаешь, это должно быть легко. +— Я не возражаю против попытки, но как вы сами говорили, одни уроки даются легче, чем другие. +Хотя я напускал на себя уверенный вид я все‑таки пал духом, когда в ответ на вызов Гаркина большая свеча поплыла к рабочему столу. За четыре года обучения я еще не имел успеха в этом особом упражнении. Если Гаркин не собирался подпускать меня к еде, пока я не преуспею, то видимо мне, долгое время придется ходить голодным. +— Послушай, э‑э, Гаркин, мне пришло в голову, что я, вероятно, лучше могу сосредоточится на полный желудок. +— А мне пришло в голову, что ты обманываешь. +— Разве мне нельзя… +— Приступай, Скив. +Коль скоро он употребил мое надлежащее имя, его было уже не поколебать. Уж это‑то я за эти годы усвоил хорошо. Мальчуган, вор, идиот, репоголовый — все эти имена хотя и унизительны, но пока он применял их, на него еще можно было повлиять, но как только он употреблял мое надлежащее имя, это становилось безнадежным. И впрямь огорчительное положение, когда звук твоего собственного имени становится дурным предзнаменованием. +Ну, если нельзя никак уклониться, то мне просто придется посвятить этому все силы. Для такого дела не может быть никаких полуусилий или поддельной сосредоточенности. Мне придется использовать каждую унцию своих сил и умения для вызывания мощи. +Я отвлеченно изучал свечу, отгораживая предстоящие усилия от своего сознания. Помещение, загроможденный рабочий стол, Гаркин, даже мой собственный голос постепенно исчезали из виду, когда я сфокусировался на свече, хотя я давным‑давно запомнил все ее особенности. +Она была толстой, почти шесть дюймов в поперечнике, чтобы стабилизировать ее десятидюймовую высоту. На ее поверхности я вырезал многочисленные мистические символы, старательно скопировав их по указанию Гаркина с его книг, хотя многие из них были частично уничтожены затвердевшими ручейками воска. Свеча горела много долгих часов, освещая мои занятия, но всегда зажигалась от очага, а не от моих усилий. +Негативное мышление. Прекрати. +На этот раз я зажгу свечу. Я зажгу ее потому, что нет никаких причин, чтобы не зажечь ее. +Сознательно углубляя дыхание, я начал накапливать мощь. Мой мозг сузился еще больше, пока в сознании не остался только свернувшийся, почерневший фитиль свечи. +Я — Скив. Мой отец связан прочными узами с землей. Моя мать была образованная женщина. Мой учитель — мастер‑маг. Я — Скив. Я зажгу эту свечу. +Я почувствовал, что сам становлюсь теплым, когда во мне начала возрастать энергия. Я сфокусировал жар на фитиле. Подобно своему отцу, я черпаю силу от земли. А знания, данные мне матерью, подобны линзе, они дают мне возможность сфокусировать то, что я приобрел. Мудрость моего учителя направляет мои усилия на те точки вселенной, которые вероятнее всего поддадутся моей силе, моей воле. Я — Скив. +Свеча оставалась незажженной. На лбу у меня выступил пот, я начал дрожать от напряжения. Нет, это неправильно. Мне не следует напрягаться. Расслабься. Не пытайся нажимать. Напряженность мешает течению. Дай энергии проходить свободно, служи ей пассивным проводником. Я заставил мышцы лица и плеч обмякнуть и удвоил свои усилия. +Течение стало заметно интенсивнее. Я почти видел, как энергия струится от меня к моей цели. Я вытянул палец, на котором еще больше сфокусировалась энергия. Свеча оставалась незажженной. +Я не могу этого сделать. Негативное мышление. Прекрати. Я — Скив. Я зажгу свечу. Мой отец… Нет. Негативное мышление. Не полагайся по части своей силы на других. Я зажгу свечу, потому что я — Скив. +За эту мысль я был вознагражден внезапным приливом энергии. Я умножил усилия, опьяняясь мощью. Я — Скив. Я сильнее любого из них. Я сбежал от попытки отца приковать меня к плугу, как он приковал моего брата. Моя мать умерла из‑за своего идеализма, но я использовал ее наставления для выживания. Мой учитель — доверчивый дурак, взявший в ученики вора. Я обставлю их всех. Я зажгу свечу. Я — Скив. +Теперь я плавал. Я осознавал, какими карликами делали мои способности тех, кто меня окружал. Неважно, зажгу я свечу или нет. Я — Скив. Я — могуч. +Я почти презрительно протянул мысленно руку к фитилю. Словно в ответ на мою волю появилось маленькое яркое тление. +Пораженный, я выпрямился, а потом моргнул, глядя на свечу. Когда я это сделал тление исчезло, оставив в ознаменование своего события маленький белый дымок. И я слишком поздно сообразил, что нарушил сосредоточенность. +— Великолепно, мальчуган! +Гаркин вдруг оказался рядом со мной, с энтузиазмом колотя меня по плечу. Долго ли он был тут, я не знал и не интересовался. +— Она погасла, — уныло проговорил я. +— Это неважно. Ты зажег ее. Теперь у тебя есть уверенность. В следующий раз это будет легче. Клянусь звездами, мы еще сделаем из тебя мага. Вот, ты, наверное, проголодался. +Я едва успел вовремя понять руку и перехватить ножку ящероптицы, прежде чем та шмякнула меня по морде. Она уже остыла. +— Не стану скрывать, мальчуган, я уже начал отчаиваться. Что сделало этот урок таким трудным? Разве тебе не приходило в голову, что ты можешь воспользоваться этим заклинанием для получения добавочного света, когда будешь взламывать замок, или же для устройства пожара, чтобы отвлечь внимание? +— Я думал об этом, но добавочный свет может как раз привлечь нежелательное внимание. Что касается отвлечения его, то боюсь, что при этом кто‑то пострадает. Я не хочу этого. Я просто… +Я остановился, слишком поздно сообразив, что говорю. Тяжелый удар гаркинского кулака отправил меня с табурета на пол. +— Так я и думал! Ты все еще замышляешь стать вором! Ты хочешь использовать мою магию для краж! +Он был ужасен в своей ярости, но на сей раз я дал ему отпор. +Ну и что из этого ? — зарычал я. — Это куда лучше, чем голодать. И вообще, что такого хорошего в том, чтобы быть магом? Я хочу сказать, что твой здешний образ жизни вызывает у меня просто неудержимое желание достичь того же. +Я показал на загроможденную комнату, составлявшую все внутреннее пространство хижины. +— Послушайте, как жалуется этот волчонок! — фыркнул Гаркин. — Он был достаточно хорош для тебя, когда зима выгнала тебя воровать из леса. «Это куда лучше, чем спать под кустом», — сказал ты. +— И все еще лучше. Вот почему я все еще здесь. Но я не собираюсь проводить здесь остаток своей жизни. Прятаться в лесной избушке — не мое представление о будущем. Ты жил, питаясь корнями и ягодами, пока я не пришел и не начал ловить мясо для очага. Может быть, ты, Гаркин, так и представляешь себе счастливую жизнь, но я — нет! +Несколько долгих мгновений мы жгли друг друга взглядами. Теперь, выплеснув свой гнев, я более чем малость побаивался. Хотя я и не имел широкого опыта в этой области, но подозревал, что насмехаться над магом +— не самый лучший способ обеспечить себе долгое и здоровое будущее. +Удивительно, но первым уступил Гаркин. Он вдруг потупил взгляд и склонил голову, предоставив мне обозревать массу нечесанных волос у него на макушке. +— Наверно, ты прав, Скив, — голос его стал странно мягким. — Наверное, я показывал тебе все труды магии, но не вознаграждение за них. Я постоянно забываю, как подавлена магия в этих краях. +Он поднял голову, встретился со мной взглядом, и я содрогнулся как от удара. В глубине его глаз горел никогда не виденный мной раньше огонь. +— Знай же, Скив, что не все края похожи на этот, и я не всегда был таким как сейчас. В краях, где магию признают, а не страшатся, как здесь, те кто находится у власти, уважают и заказывают ее. Там умелый маг, держащийся начеку, может пожать в сто раз больше богатства, чем ты собираешься добыть воровством, и приобрести такую власть, что… +Он вдруг оборвал свою речь и помотал головой, словно прочищая ее. Когда он снова открыл глаза, огонь, увиденный мной ранее ярко горящим в его глазах спал до тления. +— Но слова не производят, я вижу, на тебя впечатления, не так ли, мальчуган? Пойдем, я покажу тебе небольшую демонстрацию той власти, которую ты однажды сможешь держать в своих руках — если будешь практиковаться в своих уроках. +Веселость в его голосе была принужденной. В ответ я кивком выразил свое согласие. По правде говоря, я не нуждался ни в какой демонстрации. Его тихая краткая речь нагнала на меня куда больше благоговейного страха, чем любая гневная тирада или демонстрация, но я не посмел противоречить ему в такое время. +Не думаю, чтобы он действительно заметил мой ответ. Он уже шагал к большой пентаграмме, навсегда начертанной на полу хижины. На ходу он сделал рассеянный жест, и покрытая сажей медная жаровня шмыгнула со своего места в углу встретить его в центр пентаграммы. +У меня хватило времени на воспоминания о том, что эта жаровня и привлекла меня сперва к Гаркину. Я вспомнил, как первый раз смотрел через окно его избушки, стремясь разглядеть ценные вещи для последующий кражи. Я увидел Гаркина таким же, каким часто видел его с тез пор, — беспокойно расхаживающим взад‑вперед по помещению, уткнувшись носом в книгу. Это само по себе было удивительным зрелищем, ибо чтение — не самое обычное времяпрепровождение в этой области. Но мое внимание захватила жаровня. Она скакала по помещению, следуя за Гаркиным, словно нетерпеливый щенок, который немного слишком вежлив, чтобы прфгнуть на своего хозяина для привлечения его внимания. Затем Гаркин оторвался от книги, задумчиво уставился на свой рабочий стол, затем кивнул, приняв решение и сделал жест. Из кучи всякой всячины поднялся горшок с неопознанным содержимым и поплыл к его поджидавшей руке. Он поймал его, снова сверился с книгой и, не поднимая глаз, вылил часть. Быстрая, как кошка, жаровня, протиснулась ему под руку и поймала вылитое, прежде чем оно достигло пола. Вот так то я и познакомился с магией. +Что‑то рывком вернуло мое внимание к настоящему. Что именно? Я проверил, как идут дела у Гаркина. Он все еще трудился, полускрытый плавающим облаком пузырьков и кувшинов, что‑то бормоча, когда выдергивал один такой из воздуха и добавляя его содержимое в жаровню. Над чем бы он там ни трудился, зрелище обещало быть захватывающим. +Затем я снова услышал его — приглушенный шаг за стенкой избушки. Но ведь это было невозможно! Гаркин всегда устанавливал… Я начал копаться в памяти и не мог вспомнить, установил ли Гаркин защитный полог, прежде чем приступить к работе. Нелепо. Осторожность была первым и самым важным делом, что вдолбил в меня Гаркин, и частью осторожности являлось установление защитнго полога, который необходимо было установить перед работой. Он не мог забыть… но он был несколько распален и отвлечен. +Я все еще решал, не следует ли мне прервать труд Гаркина, когда он вдруг отступил на шаг от жаровни. Он парализовал меня взглядом и предупреждение замерло у меня в голове. Не время было навязывать при такой ситуации реальность. В глаза его вернулся огонь, сильней, чем прежде. +— Даже демонстрация должна дать тебе урок, — поучающе заявил он. — Контроль, Скив. Контроль — это оплот магии. Бесконтрольная мощь — это катастрофа. Вот почему ты практикуешься с пером, хотя можешь передвигать куда более крупные предметы. Контроль. Даже твои скудные силы были бы опасны без контроля, и я не стану учить тебя овладевать большими силами, пока ты не научишься этому контролю. +Он шагнул прочь из пентаграммы. +— Чтобы продемонстрировать тебе ценность контроля, я сейчас вызову демона, существо из другого мира. Он могуч, злобен и жесток и убьет нас обоих, если ему дать шанс. И все же, несмотря на это, бояться нам его незачем, потому что он будет находится под контролем. Он не сможет причинить нам вреда, нам или еще чему‑нибудь в этом мире, покуда он содержится в этой пентаграмме. А теперь смотри, Скив. Смотри и учись. +Сказав это, он опять повернулся к жаровне. Он развел руки в стороны, и разом ожили пять свечей по углам пентаграммы и линии ее начали пылать жутким голубым светом. Несколько минут царила тишина, а затем он стал тихо и неразборчиво читать нараспев заклинание. Из жаровни появилась ниточка дыма, но не поднялась к потолку, а полилась на пол и начала образовывать небольшое облако, которое пульсировало и бурлило. Пение Гаркина сделалось еще громче, и облако выросло, потемнело. Жаровню уже почти не было видно, но там… в глубине облака… что‑то приобретало очертания… +— Иштван шлет тебе привет, Гаркин! +При этих словах я чуть было не выпрыгнул из собственной шкуры. Они прозвучали внутри хижины, но не из пентаграммы! Я резко обернулся к их источнику. Непосредственно в дверях стояла ослепительная фигура в пылающем золотом плаще. Какой‑то безумный миг я думал, что это демон ответил на вызов Гаркина, но потом я увидел арбалет. Это был, конечно, человек, спору нет, но заряженный и взведенный арбалет в его руках мало способствовал моему душевному спокойствию. +Гаркин даже не обернулся. +— Не сейчас, дурак! — рявкнул он. +— Охота была долгой, Гаркин, — продолжал тот, словно не слыша. — Ты хорошо спрятался, но не рассчитывался же ты в самом деле скрыться от… +— Ты смеешь?! — ужасный в своем гневе Гаркин резко обернулся. +Вошедший увидел теперь лицо Гаркина, увидел его глаза, его лицо исказилось в гротескной маске страха. Он рефлекторно выстрелил из арбалета, но слишком поздно. Я не видел, что именно сделал Гаркин, но вошедший вдруг исчез в слое пламени. Он пронзительно закричал в агонии, и упал на пол. Пламя исчезло так же внезапно, как и появившись, оставив только дымящийся труп, как доказательство того, что оно действительно существовало. +Несколько мгновений я оставался приросшим к месту прежде чем смог двигаться или говорить. +— Гаркин… — произнес я наконец. — Я… Гаркин! +Тело его мешком лежало на полу. Я одним прыжком очутился рядом с ним, но очутился чересчур поздно. Из его груди торчала арбалетная стрела. Гаркин дал мне свой последний урок. +Когда я нагнулся коснуться его тела, то заметил нечто такое, от чего кровь застыла у меня в жилах. Его труп полускрывал погасшую свечу в северном углу пентаграммы. Линии больше не пылали голубизной. Защитное поле заклинаний пропало. +С мучительным усилием я поднял голову и встретился взглядом с парой желтых глаз с крапинками золота, не принадлежащих этому миру. diff --git a/onefile/data/writing.txt b/onefile/data/writing.txt new file mode 100644 index 0000000..b6c9055 --- /dev/null +++ b/onefile/data/writing.txt @@ -0,0 +1,20 @@ +х х х х х х х х х х х х +ж ж ж ж ж ж ж ж ж ж ж ж +Х Х Х Х Х Х Х Х Х Х Х Х +Ж Ж Ж Ж Ж Ж Ж Ж Ж Ж Ж Ж +й й й й й й й й й й й й +Й Й Й Й Й Й Й Й Й Й Й Й +ч ч ч ч ч ч ч ч ч ч ч ч +Ч Ч Ч Ч Ч Ч Ч Ч Ч Ч Ч Ч +ц ц ц ц ц ц ц ц ц ц ц ц +Ц Ц Ц Ц Ц Ц Ц Ц Ц Ц Ц Ц +щ щ щ щ щ щ щ щ щ щ щ щ +Щ Щ Щ Щ Щ Щ Щ Щ Щ Щ Щ Щ +ф ф ф ф ф ф ф ф ф ф ф ф +Ф Ф Ф Ф Ф Ф Ф Ф Ф Ф Ф Ф +э э э э э э э э э э э э +Э Э Э Э Э Э Э Э Э Э Э Э +ю ю ю ю ю ю ю ю ю ю ю ю +Ю Ю Ю Ю Ю Ю Ю Ю Ю Ю Ю Ю +ь ь ь ь ь ь ь ь ь ь ь ь +ъ ъ ъ ъ ъ ъ ъ ъ ъ ъ ъ ъ diff --git a/onefile/gen-barcode.c b/onefile/gen-barcode.c new file mode 100644 index 0000000..4885975 --- /dev/null +++ b/onefile/gen-barcode.c @@ -0,0 +1,158 @@ + +#include +#include +#include +#include + +#include "svg-save.inc.h" + + + +char codes[10][2][10] = { + { "0001101", "0100111" }, // 0 + { "0011001", "0110011" }, // 1 + { "0010011", "0011011" }, // 2 + { "0111101", "0100001" }, // 3 + { "0100011", "0011101" }, // 4 + { "0110001", "0111001" }, // 5 + { "0101111", "0000101" }, // 6 + { "0111011", "0010001" }, // 7 + { "0110111", "0001001" }, // 8 + { "0001011", "0010111" }, // 9 +}; + + +int seed; +double pb = 10; +double pw = 298; +double ph = 210; +double bh = 15; +double th = 5; +double step = 0.5; + + +unsigned long long genNum() + { return rand()%1000000ull*1000000 + rand()%1000000; } + + +void drawCode(FILE *f, unsigned long long num, double x, double y, double w, double h, double hh) { + char numA[10] = {}; + char numB[10] = {}; + char code[] = + "101" // prefix + "[digit][digit][digit][digit][digit][digit]" + "01010" // separator + "[digit][digit][digit][digit][digit][digit]" + "101"; // suffix + int count = sizeof(code) - 1; + char path[2048] = {}; + + sprintf(numA, "%06d", (int)(num/1000000ull%1000000)); + sprintf(numB, "%06d", (int)(num%1000000)); + + for(int i = 0; i < 6; ++i) { + memcpy(code + 3 + i*7 , codes[numA[i]-'0'][0], 6); + memcpy(code + 3 + 6*7 + 5 + i*7, codes[numB[i]-'0'][1], 6); + } + + saveState(); + noFill(); + strokeWidth(w); + char *p = path; + for(int i = 0; i < count; ++i) { + if (code[i] != '1') continue; + int xl = i < 3 + || (i >= 3 + 6*7 && i < 3 + 6*7 + 5) + || i >= 3 + 6*7 + 5 + 6*7; + double hhh = xl ? h + hh : h; + if (f) { + p += sprintf(p, "M %g %g l 0 %g ", x+i*w, y, hhh); + } else { + line(x+i*w, y, x+i*w, y + hhh); + } + } + + if (f) { + svgAddPath(f, path, w, FALSE); + svgAddText(f, x + (3+2)*w, y + h + hh, hh, numA); + svgAddText(f, x + (3+6*7+5+2)*w, y + h + hh, hh, numB); + } else { + textSize(hh); + textAlign(HALIGN_CENTER, VALIGN_TOP); + text(x + (3+3*7)*w, y + h, numA); + text(x + (3+6*7+5+3*7)*w, y + h, numB); + } + restoreState(); +} + + +void drawAllCodes(FILE *f) { + srand(seed); + + double cw = step*(3 + 6*7 + 5 + 6*7 + 3); + double minSpace = 9*step; + double w = pw - 2*pb; + double h = ph - 2*pb; + int cols = (int)(w/(cw + minSpace) + 0.001); + int rows = (int)(h/(bh + th + th) + 0.001); + if (cols <= 0 || rows <= 0) return; + + double exw = w - cols*cw; + double exh = h - rows*(bh+th); + if (cols > 1) exw /= cols - 1; + if (rows > 1) exh /= rows - 1; + + double cww = cw + exw; + double chh = bh + th + exh; + + for(int r = 0; r < rows; ++r) + for(int c = 0; c < cols; ++c) + drawCode(f, genNum(), pb + c*cww, pb + r*chh, step, bh, th); +} + + +void generate() { + seed = rand(); + FILE *f = svgBegin("data/output/generated-barcode.svg", pw, ph, 1); + drawAllCodes(f); + svgEnd(f); +} + + + +void init() { + generate(); +} + + +void draw() { + double w = windowGetWidth(); + double h = windowGetHeight(); + + double kw = w/pw; + double kh = h/ph; + double k = kw < kh ? kw : kh; + + if (keyWentDown("space")) + generate(); + + saveState(); + translate(w/2, h/2); + zoom(k); + translate(-pw/2, -ph/2); + noFill(); + strokeWidth(1); + rect(0, 0, pw, ph); + drawAllCodes(NULL); + restoreState(); +} + + +int main() { + windowSetVariableFrameRate(); + windowSetResizable(TRUE); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); +} + diff --git a/onefile/gen-dungeon.c b/onefile/gen-dungeon.c new file mode 100644 index 0000000..cbaa6c2 --- /dev/null +++ b/onefile/gen-dungeon.c @@ -0,0 +1,163 @@ + +#include +#include +#include +#include + + +#define TILESIZE 512 +#define ROWS 8 +#define COLS 12 + + +const double cellRatio = 1.0; +const double genRatio = 0.5; + +Animation tiles[1024]; + +int tilesCount = 0; + +Framebuffer framebuffer; +Animation fbAnim; + + +void groundLine(double len) { + double ky = 0.02; + double x = 0; + double y = 0; + moveTo(x, y); + while(x < len) { + double ny; + do { ny = (randomFloat()*2 - 1)*ky; } while(fabs(y + ny) > ky); + y += ny; + x += (1 + (randomFloat()*2 - 1)*0.5)*ky; + if (x >= len) { x = len; y = 0; } + lineTo(x, y); + } + strokePath(); +} + + +void generate() { + saveState(); + + target(framebuffer); + background(COLOR_TRANSPARENT); + clear(); + zoom(TILESIZE*cellRatio*genRatio); + + fill(colorByRGBA(1, 1, 1, 1)); + noStroke(); + double thikness = 0.02/cellRatio*genRatio; + strokeWidth(thikness); + + saveState(); + translate(0.5, 0.5); + if (tilesCount > 0) + for(int y = 0; y < ROWS; ++y) + for(int x = 0; x < COLS; ++x) { + saveState(); + translate(x, y); + zoom(1/cellRatio); + rectTextured(tiles[rand()%tilesCount], -0.5, -0.5, 1, 1); + restoreState(); + } + restoreState(); + + saveState(); + stroke(COLOR_BLACK); + double k = 1/3.0; + saveState(); + translate(0.5 - k, 0.5); + for(int i = 0; i < ROWS; ++i) { + saveState(); + translate(0, i - k); + groundLine(COLS + 2*k - 1); + restoreState(); + saveState(); + translate(0, i + k); + groundLine(COLS + 2*k - 1); + restoreState(); + } + restoreState(); + saveState(); + rotate(90); + scale(1, -1); + translate(0.5 - k, 0.5); + for(int i = 0; i < COLS; ++i) { + saveState(); + translate(0, i - k); + groundLine(ROWS + 2*k - 1); + restoreState(); + saveState(); + translate(0, i + k); + groundLine(ROWS + 2*k - 1); + restoreState(); + } + restoreState(); + restoreState(); + + viewportSave("data/output/generated-dungeon.png"); + restoreState(); +} + + +Animation subImage(int width, int height, unsigned char *pixels, int x, int y, int w, int h) { + if (x < 0 || y < 0 || x + w > width || y + h > height || !pixels) + return NULL; + unsigned char *px = calloc(4, w*h); + for(int i = 0; i < h; ++i) + memcpy(px + i*w*4, pixels + ((y+i)*width + x)*4, w*4); + Animation anim = createAnimationFromImage(w, h, px, FALSE); + free(px); + return anim; +} + + +void init() { + int w = 0; + int h = 0; + unsigned char *px = NULL; + imageLoad("data/dungeon.png", &w, &h, &px); + int s = TILESIZE + TILESIZE/2; + for(int y = 0; y < 2; ++y) + for(int x = 0; x < 6; ++x) + if ((tiles[tilesCount] = subImage(w, h, px, x*TILESIZE + TILESIZE/4, y*TILESIZE + TILESIZE/4, TILESIZE, TILESIZE))) + ++tilesCount; + free(px); + + w = ceil(TILESIZE*COLS*cellRatio*genRatio - 0.001); + h = ceil(TILESIZE*ROWS*cellRatio*genRatio - 0.001); + framebuffer = createFramebuffer(w, h); + fbAnim = createAnimationFromFramebuffer(framebuffer); + + generate(); +} + + +void draw() { + background(COLOR_WHITE); + + double w = windowGetWidth(); + double h = windowGetHeight(); + + if (keyWentDown("space")) generate(); + + saveState(); + translate(w/2, h/2); + zoom(w/COLS); + fill(COLOR_WHITE); + noStroke(); + rectTextured(fbAnim, -COLS/2.0, -ROWS/2.0, COLS, ROWS); + restoreState(); +} + + +int main() { + windowSetVariableFrameRate(); + windowSetResizable(TRUE); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); +} + diff --git a/onefile/gen-dungeon2.c b/onefile/gen-dungeon2.c new file mode 100644 index 0000000..1059d2b --- /dev/null +++ b/onefile/gen-dungeon2.c @@ -0,0 +1,243 @@ + +#include +#include +#include +#include + + +#define WIDTH 285.0 +#define HEIGHT 200.0 +#define RES 10.0 +#define THIKNESS 0.5 +#define ALPHA 0.1 + +#define CELLSIZE 22.5 +#define ITEMSIZE 10.0 +#define WALLSIZE 10.0 +#define WALLJITTER 1.0 +#define WALLBORDER 2.0 + +#define SRCRES 10.0 + +#define SCALE 1.0 + + +double itemScale; +double cellScale; + + +Animation itemTiles[1024]; +Animation cellTiles[1024]; + +int itemTilesCount = 0; +int cellTilesCount = 0; + +int rows; +int cols; + +Framebuffer framebuffer; +Animation fbAnim; + + +void shuffle(Animation *anims, int count) { + for(int i = 0; i < 2*count; ++i) { + int a = randomNumber(0, count-1); + int b = randomNumber(0, count-1); + if (a != b) { + Animation anim = anims[a]; + anims[a] = anims[b]; + anims[b] = anim; + } + } +} + + +void groundLine(double len, double k) { + double x = 0; + double y = 0; + moveTo(x, y); + while(x < len) { + double ny; + do { ny = (randomFloat()*2 - 1)*k; } while(fabs(y + ny) > k); + y += ny; + x += (1 + (randomFloat()*2 - 1)*0.5)*2*k; + if (x >= len) { x = len; y = 0; } + lineTo(x, y); + } + strokePath(); +} + + +void generate() { + int fw = framebufferGetWidth(framebuffer); + int fh = framebufferGetHeight(framebuffer); + + saveState(); + target(framebuffer); + background(COLOR_TRANSPARENT); + clear(); + + translate(fw/2, fh/2); + zoom(RES); + fill(COLOR_WHITE); + noStroke(); + + double step = CELLSIZE + WALLSIZE; + translate(-(cols-1)*step/2, -(rows-1)*step/2); + + double itemStep = (CELLSIZE - ITEMSIZE)/2; + double cellSize = CELLSIZE*cellScale; + double itemSize = ITEMSIZE*itemScale; + + int cellId = cellTilesCount; + int itemId = itemTilesCount; + for(int y = 0; y < rows; ++y) { + for(int x = 0; x < cols; ++x) { + saveState(); + translate(x*step, y*step); + if (cellTilesCount > 0 && randomFloat()*100 < 10) { + if (cellId >= cellTilesCount) { + shuffle(cellTiles, cellTilesCount); + cellId = 0; + } + rectTextured(cellTiles[cellId++], -cellSize/2, -cellSize/2, cellSize, cellSize); + } else + if (itemTilesCount > 0) { + int cnt = 4; + if (cnt < itemTilesCount) cnt = rand()%itemTilesCount; + for(int i = 0; i < 4; ++i) { + saveState(); + translate(i%2 ? itemStep : -itemStep, i/2 ? itemStep : -itemStep); + if (itemId >= itemTilesCount) { + shuffle(itemTiles, itemTilesCount); + itemId = 0; + } + rectTextured(itemTiles[itemId++], -itemSize/2, -itemSize/2, itemSize, itemSize); + restoreState(); + } + } + restoreState(); + } + } + + noFill(); + stroke(COLOR_BLACK); + strokeWidth(THIKNESS); + double k = WALLJITTER; + double wallStep = CELLSIZE/2 + k + WALLBORDER; + saveState(); + translate(-wallStep, 0); + double len = (cols-1)*step + 2*wallStep; + for(int i = 0; i < rows; ++i) { + saveState(); + translate(0, i*step - wallStep); + groundLine(len, k); + translate(0, 2*wallStep); + groundLine(len, k); + restoreState(); + } + restoreState(); + saveState(); + rotate(90); + scale(1, -1); + translate(-wallStep, 0); + len = (rows-1)*step + 2*wallStep; + for(int i = 0; i < cols; ++i) { + saveState(); + translate(0, i*step - wallStep); + groundLine(len, k); + translate(0, 2*wallStep); + groundLine(len, k); + restoreState(); + } + restoreState(); + + viewportSave("data/output/generated-dungeon2.png"); + restoreState(); +} + + +Animation subImage(int width, int height, unsigned char *pixels, int x, int y, int w, int h) { + if (x < 0 || y < 0 || x + w > width || y + h > height || !pixels) + return NULL; + unsigned char *px = calloc(4, w*h); + for(int i = 0; i < h; ++i) + memcpy(px + i*w*4, pixels + ((y+i)*width + x)*4, w*4); + Animation anim = createAnimationFromImage(w, h, px, FALSE); + free(px); + return anim; +} + + +void init() { + int w = 0; + int h = 0; + unsigned char *px = NULL; + imageLoad("data/dungeon2.png", &w, &h, &px); + + int is = 128; + int cs = 256; + itemScale = is/(ITEMSIZE*SRCRES); + cellScale = cs/(CELLSIZE*SRCRES); + + int ix0 = (int)round((1*ITEMSIZE + (1 - itemScale)*ITEMSIZE/2)*SRCRES); + int iy0 = ix0; + int idx = (int)round(2*ITEMSIZE*SRCRES); + int idy = idx; + + int cx0 = (int)round((1*ITEMSIZE + (1 - cellScale)*CELLSIZE/2)*SRCRES); + int cy0 = (int)round((5*ITEMSIZE + (1 - cellScale)*CELLSIZE/2)*SRCRES); + int cdx = (int)round(3*ITEMSIZE*SRCRES); + + int y = iy0; + for(int i = 0; i < 6; ++i) + if ((itemTiles[itemTilesCount] = subImage(w, h, px, ix0 + i*idx, y, is, is))) + ++itemTilesCount; + y += idy; + for(int i = 0; i < 5; ++i) + if ((itemTiles[itemTilesCount] = subImage(w, h, px, ix0 + i*idx, y, is, is))) + ++itemTilesCount; + y = cy0; + for(int i = 0; i < 3; ++i) + if ((cellTiles[cellTilesCount] = subImage(w, h, px, cx0 + i*cdx, y, cs, cs))) + ++cellTilesCount; + free(px); + + rows = HEIGHT/(CELLSIZE + WALLSIZE); + cols = WIDTH/(CELLSIZE + WALLSIZE); + + int fw = ceil(WIDTH*RES - 0.001); + int fh = ceil(HEIGHT*RES - 0.001); + framebuffer = createFramebuffer(fw, fh); + fbAnim = createAnimationFromFramebuffer(framebuffer); + + generate(); +} + + +void draw() { + background(COLOR_WHITE); + + double w = windowGetWidth(); + double h = windowGetHeight(); + + if (keyWentDown("space")) generate(); + + saveState(); + translate(w/2, h/2); + zoom(w/WIDTH); + fill(COLOR_WHITE); + noStroke(); + rectTextured(fbAnim, -WIDTH/2.0, -HEIGHT/2.0, WIDTH, HEIGHT); + restoreState(); +} + + +int main() { + windowSetVariableFrameRate(); + windowSetResizable(TRUE); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); +} + diff --git a/onefile/gen-dungeon3.c b/onefile/gen-dungeon3.c new file mode 100644 index 0000000..e840462 --- /dev/null +++ b/onefile/gen-dungeon3.c @@ -0,0 +1,297 @@ + +#include +#include +#include +#include + + +#define WIDTH 285.0 +#define HEIGHT 200.0 +#define RES 10.0 +#define THIKNESS 0.5 +#define ALPHA 0.1 + +#define CELLSIZE 22.5 +#define ITEMSIZE 10.0 +#define WALLSIZE 15.0 +#define WALLJITTER 0.5 +#define WALLWIDTH 1.0 + +#define SRCRES 10.0 + +#define SCALE 1.0 + + +double itemScale; +double cellScale; + + +Animation itemTiles[1024]; +Animation cellTiles[1024]; +int map[256][256]; + +int itemTilesCount = 0; +int cellTilesCount = 0; + +int rows; +int cols; +unsigned int jitterSeed; + + +Framebuffer framebuffer; +Animation fbAnim; + + +void shuffle(int *numbers, int count) { + for(int i = 0; i < count; ++i) + numbers[i] = i; + for(int i = 0; i < 2*count; ++i) { + int a = randomNumber(0, count-1); + int b = randomNumber(0, count-1); + int n = numbers[a]; + numbers[a] = numbers[b]; + numbers[b] = n; + } +} + + +int lastItem = 1000000; +int itemNumbers[1024]; +int genItem() { + if (lastItem >= itemTilesCount) + { shuffle(itemNumbers, itemTilesCount); lastItem = 0; } + return itemNumbers[lastItem++]; +} + + +int lastCell = 1000000; +int cellNumbers[1024]; +int genCell() { + if (lastCell >= cellTilesCount) + { shuffle(cellNumbers, cellTilesCount); lastCell = 0; } + return cellNumbers[lastCell++]; +} + + +void generateMap() { + for(int y = 0; y < rows; ++y) + for(int x = 0; x < cols; ++x) + map[y][x] = 1000 + genItem(); + + if (rows >= 2 && cols >= 2 && cellTilesCount > 0) { + int cnt = (rows-1)*(cols-1)/4/10; + cnt += randomNumber(0, cnt/2) - cnt/4; + for(int i = 0; i < cnt; ++i) { + int x = randomNumber(0, cols-2); + int y = randomNumber(0, rows-2); + if ( map[y][x] < 2000 + && map[y][x+1] < 2000 + && map[y+1][x] < 2000 + && map[y+1][x+1] < 2000 ) + { + map[y][x] = 2000 + genCell(); + map[y][x+1] = map[y+1][x] = map[y+1][x+1] = 3000; + } + } + } +} + + +void jitter(double x, double y, double k, double *outX, double *outY) { + unsigned int ix = (unsigned int)(int)round(x*2*RES); + unsigned int iy = (unsigned int)(int)round(y*2*RES); + unsigned int random = ix ^ ((iy << 16) | (iy >> 16)); + random ^= jitterSeed; + + random = (unsigned int)((unsigned long long)random * 48271 % 0x7fffffff); + double randomX = random/(double)0x7fffffff; + random = (unsigned int)((unsigned long long)random * 48271 % 0x7fffffff); + double randomY = random/(double)0x7fffffff; + + *outX = x + (randomX*2 - 1)*k; + *outY = y + (randomY*2 - 1)*k; +} + + +void jitterLine(double x0, double y0, double x1, double y1, double kl, double k) { + double dx = x1 - x0; + double dy = y1 - y0; + double len = sqrt(dx*dx + dy*dy); + int count = round(len/kl) + 1; + if (count < 2) count = 2; + double kx = dx/(count-1); + double ky = dy/(count-1); + for(int i = 0; i < count; ++i) { + double x = x0 + i*kx; + double y = y0 + i*ky; + jitter(x, y, k, &x, &y); + if (i) lineTo(x, y); else moveTo(x, y); + } + strokePath(); +} + + +void drawMap() { + int fw = framebufferGetWidth(framebuffer); + int fh = framebufferGetHeight(framebuffer); + + saveState(); + target(framebuffer); + background(COLOR_TRANSPARENT); + clear(); + + translate(fw/2, fh/2); + zoom(RES); + + double step = WALLSIZE; + translate(-(cols-1)*step/2, -(rows-1)*step/2); + + double cellSize = CELLSIZE*cellScale; + double itemSize = ITEMSIZE*itemScale; + fill(COLOR_WHITE); + noStroke(); + for(int y = 0; y < rows; ++y) { + for(int x = 0; x < cols; ++x) { + int m = map[y][x]; + if (m < 1000) { + } else + if (m < 2000) { + double xx = x*step - itemSize/2; + double yy = y*step - itemSize/2; + rectTextured(itemTiles[m - 1000], xx, yy, itemSize, itemSize); + } else + if (m < 3000) { + double xx = x*step + step/2 - cellSize/2; + double yy = y*step + step/2 - cellSize/2; + rectTextured(cellTiles[m - 2000], xx, yy, cellSize, cellSize); + } + } + } + + noFill(); + stroke(COLOR_BLACK); + strokeWidth(THIKNESS); + jitterSeed = randomNumber(0, 0x7fffffff); + double t = WALLWIDTH/2.0; + double k = WALLJITTER; + double kl = 2*k; + for(int y = 0; y < rows; ++y) { + for(int x = 0; x <= cols; ++x) { + if (x > 0 && x < cols) { + if (map[y][x-1] >= 2000 && map[y][x-1] < 3000) continue; + if (y > 0 && map[y][x-1] >= 3000 && map[y-1][x-1] >= 2000 && map[y-1][x-1] < 3000) continue; + } + double xx = (x - 0.5)*step; + double yy = (y - 0.5)*step; + jitterLine(xx-t, yy, xx-t, yy+step, kl, k); + jitterLine(xx+t, yy, xx+t, yy+step, kl, k); + } + } + for(int x = 0; x < cols; ++x) { + for(int y = 0; y <= rows; ++y) { + if (y > 0 && y < rows) { + if (map[y-1][x] >= 2000 && map[y-1][x] < 3000) continue; + if (x > 0 && map[y-1][x] >= 3000 && map[y-1][x-1] >= 2000 && map[y-1][x-1] < 3000) continue; + } + double xx = (x - 0.5)*step; + double yy = (y - 0.5)*step; + jitterLine(xx, yy-t, xx+step, yy-t, kl, k); + jitterLine(xx, yy+t, xx+step, yy+t, kl, k); + } + } + + viewportSave("data/output/generated-dungeon3.png"); + restoreState(); +} + + +void generate() { + generateMap(); + drawMap(); +} + + +Animation subImage(int width, int height, unsigned char *pixels, int x, int y, int w, int h) { + if (x < 0 || y < 0 || x + w > width || y + h > height || !pixels) + return NULL; + unsigned char *px = calloc(4, w*h); + for(int i = 0; i < h; ++i) + memcpy(px + i*w*4, pixels + ((y+i)*width + x)*4, w*4); + Animation anim = createAnimationFromImage(w, h, px, FALSE); + free(px); + return anim; +} + + +void init() { + int w = 0; + int h = 0; + unsigned char *px = NULL; + imageLoad("data/dungeon3.png", &w, &h, &px); + + int is = 128; + int cs = 256; + itemScale = is/(ITEMSIZE*SRCRES); + cellScale = cs/(CELLSIZE*SRCRES); + + int ix0 = (int)round((1*ITEMSIZE + (1 - itemScale)*ITEMSIZE/2)*SRCRES); + int iy0 = ix0; + int idx = (int)round(2*ITEMSIZE*SRCRES); + int idy = idx; + + int cx0 = (int)round((1*ITEMSIZE + (1 - cellScale)*CELLSIZE/2)*SRCRES); + int cy0 = (int)round((5*ITEMSIZE + (1 - cellScale)*CELLSIZE/2)*SRCRES); + int cdx = (int)round(3*ITEMSIZE*SRCRES); + + int y = iy0; + for(int i = 0; i < 6; ++i) + if ((itemTiles[itemTilesCount] = subImage(w, h, px, ix0 + i*idx, y, is, is))) + ++itemTilesCount; + y += idy; + for(int i = 0; i < 6; ++i) + if ((itemTiles[itemTilesCount] = subImage(w, h, px, ix0 + i*idx, y, is, is))) + ++itemTilesCount; + y = cy0; + for(int i = 0; i < 3; ++i) + if ((cellTiles[cellTilesCount] = subImage(w, h, px, cx0 + i*cdx, y, cs, cs))) + ++cellTilesCount; + free(px); + + cols = (int)floor(WIDTH/WALLSIZE + 0.001) - 1; + rows = (int)floor(HEIGHT/WALLSIZE + 0.001) - 1; + + int fw = ceil(WIDTH*RES - 0.001); + int fh = ceil(HEIGHT*RES - 0.001); + framebuffer = createFramebuffer(fw, fh); + fbAnim = createAnimationFromFramebuffer(framebuffer); + + generate(); +} + + +void draw() { + background(COLOR_WHITE); + + double w = windowGetWidth(); + double h = windowGetHeight(); + + if (keyWentDown("space")) generate(); + + saveState(); + translate(w/2, h/2); + zoom(w/WIDTH); + fill(COLOR_WHITE); + noStroke(); + rectTextured(fbAnim, -WIDTH/2.0, -HEIGHT/2.0, WIDTH, HEIGHT); + restoreState(); +} + + +int main() { + windowSetVariableFrameRate(); + windowSetResizable(TRUE); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); +} + diff --git a/onefile/gen-galaxy.c b/onefile/gen-galaxy.c new file mode 100644 index 0000000..2635969 --- /dev/null +++ b/onefile/gen-galaxy.c @@ -0,0 +1,59 @@ + +#include +#include + + +void init() { +} + + +void put(double level) { + double x = level; + double e = exp(1+level); + double l = log(1+level); + + double angle = l*360*4; + double dist = x*2; + double radius = (1-l)*0.2; + + int cnt = 2; + + saveState(); + rotate(angle); + for(int i = 0; i < cnt; ++i) { + rotate(360.0/cnt); + saveState(); + translate(dist, 0); + circle(0, 0, radius); + restoreState(); + } + restoreState(); +} + + +void draw() { + double w = windowGetWidth(); + double h = windowGetHeight(); + + saveState(); + noStroke(); + fill(COLOR_BLACK); + translate(w/2, h/2); + zoom(100); + + int cnt = 100; + for(int i = 0; i < cnt; ++i) + put((i+1)/(double)cnt); + + restoreState(); +} + + +int main() { + windowSetResizable(TRUE); + windowSetVariableFrameRate(); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); + return 0; +} diff --git a/onefile/gen-maze.c b/onefile/gen-maze.c new file mode 100644 index 0000000..709df4b --- /dev/null +++ b/onefile/gen-maze.c @@ -0,0 +1,94 @@ + +#include + + +#define ROWS 12 +#define COLS 18 + + +int map[ROWS][COLS][3]; + + +int visit(int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS) return FALSE; + + if (map[r][c][2]) return randomFloat()*100 < 2; + map[r][c][2] = TRUE; + + int ways[4] = {0, 1, 2, 3}; + for(int i = 0; i < 4; ++i) { + int a = randomNumber(0, 3); + int b = randomNumber(0, 3); + int n = ways[a]; + ways[a] = ways[b]; + ways[b] = n; + } + + for(int i = 0; i < 4; ++i) { + switch(ways[i]) { + case 0: + if (visit(r, c-1)) map[r ][c ][0] = TRUE; + break; + case 1: + if (visit(r, c+1)) map[r ][c+1][0] = TRUE; + break; + case 2: + if (visit(r-1, c)) map[r ][c ][1] = TRUE; + break; + case 3: + if (visit(r+1, c)) map[r+1][c ][1] = TRUE; + break; + } + } + + return TRUE; +} + + +void generate() { + for(int r = 0; r < ROWS; ++r) + for(int c = 0; c < COLS; ++c) + for(int i = 0; i < 3; ++i) + map[r][c][i] = 0; + visit(0, 0); +} + + + +void init() { + generate(); +} + + +void draw() { + double w = windowGetWidth(); + double h = windowGetHeight(); + + if (keyWentDown("space")) generate(); + + saveState(); + translate(w/2, h/2); + zoom(w/(COLS + 1)); + translate(-COLS/2.0, -ROWS/2.0); + + strokeWidth(0.1); + for(int r = 0; r < ROWS; ++r) { + for(int c = 0; c < COLS; ++c) { + if (!map[r][c][0]) line(c, r, c, r+1); + if (!map[r][c][1]) line(c, r, c+1, r); + } + } + line(COLS, 0, COLS, ROWS); + line(0, ROWS, COLS, ROWS); + + restoreState(); +} + + +int main() { + windowSetVariableFrameRate(); + windowSetResizable(TRUE); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); +} diff --git a/onefile/gen-round-grid.c b/onefile/gen-round-grid.c new file mode 100644 index 0000000..5adefd0 --- /dev/null +++ b/onefile/gen-round-grid.c @@ -0,0 +1,115 @@ + +#include +#include + + +#define WIDTH 285.0 +#define HEIGHT 200.0 +#define RES 10.0 +#define THIKNESS 0.25 +#define ALPHA 0.50 + +#define DIAMETER 10.0 + + +int cols; +int rows; +double border; + + +Framebuffer framebuffer; +Animation fbAnim; + + + +void generate() { + double d = DIAMETER; + double r = d/2.0; + + saveState(); + target(framebuffer); + clear(); + + zoom(RES); + translate(WIDTH/2.0, HEIGHT/2.0); + noFill(); + double c = 1 - ALPHA; + stroke(colorByRGBA(c, c, c, 1)); + strokeWidth(THIKNESS); + + translate(-(cols-1)*d, -(rows-1)*d); + + double xx = (cols-1)*d*2; + double yy = (rows-1)*d*2; + circle( 0, 0, r); + circle( 0, yy, r); + circle(xx, 0, r); + circle(xx, yy, r); + + for(int y = 0; y < rows; ++y) { + double py = y*d*2 - r; + for(int x = 1; x < (cols-1)*4; ++x) { + arc(x*r-d, py, d, d, -120, 0); + arc(x*r , py, d, d, 60, 180); + } + arc(-r , py, d, d, 90, 150); + arc(0 , py, d, d, 60, 120); + arc(xx-d, py, d, d,-120, -60); + arc(xx-r, py, d, d, -90, -30); + } + + for(int x = 0; x < cols; ++x) { + double px = x*d*2 - r; + for(int y = 1; y < (rows-1)*4; ++y) { + arc(px, y*r-d, d, d, -30, 90); + arc(px, y*r , d, d, 150, 270); + } + arc(px, -r , d, d, 180, 240); + arc(px, 0 , d, d, 150, 210); + arc(px, yy-d, d, d, -30, 30); + arc(px, yy-r, d, d, 0, 60); + } + + viewportSave("data/output/generated-round-grid.png"); + restoreState(); +} + + +void init() { + double border = 2*THIKNESS; + cols = ((int)floor((WIDTH - 2*border)/DIAMETER + 0.001) + 1)/2; + rows = ((int)floor((HEIGHT - 2*border)/DIAMETER + 0.001) + 1)/2; + + int w = ceil(WIDTH*RES - 0.001); + int h = ceil(HEIGHT*RES - 0.001); + framebuffer = createFramebuffer(w, h); + fbAnim = createAnimationFromFramebuffer(framebuffer); + + generate(); +} + + +void draw() { + double w = windowGetWidth(); + double h = windowGetHeight(); + + if (keyWentDown("space")) generate(); + + saveState(); + translate(w/2, h/2); + zoom(w/WIDTH); + fill(COLOR_WHITE); + noStroke(); + rectTextured(fbAnim, -WIDTH/2.0, -HEIGHT/2.0, WIDTH, HEIGHT); + restoreState(); +} + + +int main() { + windowSetVariableFrameRate(); + windowSetResizable(TRUE); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); +} + diff --git a/onefile/gen-scheme.c b/onefile/gen-scheme.c new file mode 100644 index 0000000..8aa05fd --- /dev/null +++ b/onefile/gen-scheme.c @@ -0,0 +1,127 @@ + +#include +#include +#include +#include + + +#define TILESIZE 512 +#define ROWS 8 +#define COLS 12 + + +const double cellRatio = 5/6.0; + +Animation colTiles[128]; +Animation rowTiles[128]; + +int colTilesCount = 0; +int rowTilesCount = 0; + +Framebuffer framebuffer; +Animation fbAnim; + + +void generate() { + saveState(); + + target(framebuffer); + background(COLOR_TRANSPARENT); + clear(); + zoom(TILESIZE*cellRatio); + + double k = 0.1; + fill(colorByRGBA(1, 1, 1, 1)); + noStroke(); + + saveState(); + translate(1, 0.5); + if (rowTilesCount > 0) + for(int y = 0; y < ROWS; ++y) + for(int x = 0; x < COLS-1; ++x) { + saveState(); + translate(x, y); + zoom(1/cellRatio); + rectTextured(rowTiles[rand()%rowTilesCount], -0.5, -0.5, 1, 1); + restoreState(); + } + restoreState(); + + saveState(); + translate(0.5, 1); + if (rowTilesCount > 0) + for(int x = 0; x < COLS; ++x) + for(int y = 0; y < ROWS-1; ++y) { + saveState(); + translate(x, y); + zoom(1/cellRatio); + rectTextured(colTiles[rand()%colTilesCount], -0.5, -0.5, 1, 1); + restoreState(); + } + restoreState(); + + viewportSave("data/output/generated-scheme.png"); + restoreState(); +} + + +Animation subImage(int width, int height, unsigned char *pixels, int x, int y, int w, int h) { + if (x < 0 || y < 0 || x + w > width || y + h > height || !pixels) + return NULL; + unsigned char *px = calloc(4, w*h); + for(int i = 0; i < h; ++i) + memcpy(px + i*w*4, pixels + ((y+i)*width + x)*4, w*4); + Animation anim = createAnimationFromImage(w, h, px, FALSE); + free(px); + return anim; +} + + +void init() { + int w = 0; + int h = 0; + unsigned char *px = NULL; + imageLoad("data/scheme.png", &w, &h, &px); + for(int i = 0; i < 4; ++i) + if ((rowTiles[rowTilesCount] = subImage(w, h, px, i*TILESIZE, 0*TILESIZE, TILESIZE, TILESIZE))) + ++rowTilesCount; + for(int i = 0; i < 4; ++i) + if ((colTiles[colTilesCount] = subImage(w, h, px, i*TILESIZE, 1*TILESIZE, TILESIZE, TILESIZE))) + ++colTilesCount; + free(px); + + w = ceil(TILESIZE*COLS*cellRatio - 0.001); + h = ceil(TILESIZE*ROWS*cellRatio - 0.001); + framebuffer = createFramebuffer(w, h); + fbAnim = createAnimationFromFramebuffer(framebuffer); + + generate(); +} + + +void draw() { + background(COLOR_WHITE); + + double w = windowGetWidth(); + double h = windowGetHeight(); + + if (keyWentDown("space")) generate(); + + saveState(); + translate(w/2, h/2); + zoom(w/COLS); + fill(COLOR_WHITE); + noStroke(); + rectTextured(fbAnim, -COLS/2.0, -ROWS/2.0, COLS, ROWS); + restoreState(); +} + + +int main() { + windowSetVariableFrameRate(); + windowSetResizable(TRUE); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); +} + diff --git a/onefile/line.c b/onefile/line.c new file mode 100644 index 0000000..e1a6ad5 --- /dev/null +++ b/onefile/line.c @@ -0,0 +1,609 @@ + +#include +#include +#include +#include + + + + +typedef struct { + double x, y; + double color[4]; +} Vertex; + +int cs = 5, vw = 120, vh = 90; +int lx1, ly1, lx2, ly2, lx3, ly3; +Vertex tv1, tv2, tv3; + + +void grid(int w, int h) { + saveState(); + strokeWidth(0.1); + stroke(colorByNameA("gray", 0.5)); + for(int x = 0; x <= w; ++x) line(x, 0, x, h); + for(int y = 0; y <= h; ++y) line(0, y, w, y); + restoreState(); +} + + +void dot(int x, int y) + { rect(x, y, 1, 1); } + +void dotColored(int x, int y, double *color) { + saveState(); + fill(colorByRGBA(color[0], color[1], color[2], color[3])); + dot(x, y); + restoreState(); +} + + +void lineBase(int x1, int y1, int x2, int y2) { + int x_start_line; // put the lowest x in start line and the highest in end line + int x_end_line; + int y_start_line; // put the lowest y in start line and the highest in end line + int need_reflect; + int dist_point_to_line; + int switch_roles = FALSE; + int delta_x; + int delta_y; + int temp; + + // take care of end cases when the two points share an index value + // take care of end cases when the two points share an index value// take care of end cases when the two points share an index value// take care of end cases when the two points share an index value + // need to take care of the case where the point is outside of the window frame + + // Initialization for the Bershenham algorithm: + x_start_line = x1, y_start_line = y1; + x_end_line = x1; + delta_x = (x2 - x1); + if (delta_x < 0) + delta_x = delta_x * (-1); // make delta_x positive + delta_y = (y2 - y1); + if (delta_y < 0) + delta_y = delta_y * (-1); // make delta_y positive + if (delta_x != 0 || delta_y != 0) + { + // first case: 01 or incline<-1 + { // so we need to change the x and y roles + if (y1 < y2) // the start point is p1 and switch the x and y + { + x_start_line = y1; // swith roles of x and y + y_start_line = x1; + x_end_line = y2; + need_reflect = (x1 < x2) ? 1 : -1; + } + else // the start point is p2 and switch the x and y roles + { + x_start_line = y2; + y_start_line = x2; + x_end_line = y1; + need_reflect = (x2 < x1) ? 1 : -1; + } + switch_roles = TRUE; + temp = delta_x; // switch the delta_x and delta_y because the roles have changed + delta_x = delta_y; + delta_y = temp; + } + dist_point_to_line = 2 * delta_y - delta_x; + // Testing Barriers: + if (x_end_line > vw) // need to add barrier for viewport height without causing lines to disappear + x_end_line = vw; + if (x_start_line < 0) + x_start_line = 0; + if (y_start_line < 0) + y_start_line = 0; + for (int i = x_start_line; i <= x_end_line; i++) // go over the incline from start point to end point and draw the line + { // put each pixel in the right place + switch_roles ? dot(y_start_line, i) : dot(i, y_start_line); + // new stuff for edge walking + + // end of new stuff + if (dist_point_to_line > 0) + { + y_start_line = y_start_line + need_reflect; + dist_point_to_line = dist_point_to_line - 2 * delta_x; + } + dist_point_to_line = dist_point_to_line + 2 * delta_y; + } //x_start_line,x_end_line,switch_roles,y_start_line,need_reflect,dist_point_to_line,delta_x,delta_y + } +} + + +void lineNew(int x1, int y1, int x2, int y2) { + if (y1 > y2) { int x = x1, y = y1; x1 = x2; y1 = y2; x2 = x; y2 = y; } + + int x = x1; + int dx = x2 - x1; + int dy = y2 - y1; + if (dy > 0) { + int sign_x = dx < 0 ? -1 : 1; + int step_x = dx/dy; + int sub_step_x = abs(dx)%dy; + int sub_step_accum = -dy/2; + for(int y = y1; y < y2; ++y) { + dot(x, y); + + x += step_x; + sub_step_accum += sub_step_x; + if (sub_step_accum > 0) { + sub_step_accum -= dy; + x += sign_x; + } + } + } else { + dot(x1, y1); + } + + dot(x2, y2); +} + + +void row(int y, int x1, int x2) { + if (x1 > x2) { int x = x1; x1 = x2; x2 = x; } + if (x1 < 0) x1 = 0; + if (x2 > vw) x2 = vw; + for(int x = x1; x < x2; ++x) dot(x, y); +} + + +void triangle(int vx1, int vy1, int vx2, int vy2, int vx3, int vy3) { + if (vy1 > vy3) { int x = vx1, y = vy1; vx1 = vx3; vy1 = vy3; vx3 = x; vy3 = y; } + if (vy2 > vy3) { int x = vx2, y = vy2; vx2 = vx3; vy2 = vy3; vx3 = x; vy3 = y; } + if (vy1 > vy2) { int x = vx1, y = vy1; vx1 = vx2; vy1 = vy2; vx2 = x; vy2 = y; } + + // split triangle to two triangles by the middle vertex + // + // *__ + // / --__ - top triangle here + // /-------__-* - split triangle by this line + // / __-- + // / __-- - bottom triangle here + // *-- + + // include pixels at left and top sides, but exclude at right and bottom + // it required to seamless clue neighboring triangles without overlapping + + if (vy1 >= vy3) return; + if (vy1 >= vh || vy3 <= 0) return; + if (vx1 >= vw && vx2 >= vw && vx3 >= vw) return; + if (vx1 <= 0 && vx2 <= 0 && vx3 <= 0 ) return; + + // configure line from top vertex to bottom vertex + int x1 = vx1; + int dx1 = vx3 - vx1; + int dy1 = vy3 - vy1; + int sign_x1 = dx1 < 0 ? -1 : 1; + int step_x1 = dx1/dy1; + int sub_step_x1 = abs(dx1)%dy1; + int sub_step_accum1 = -dy1/2; + + // draw top triangle + if (vy2 > vy1) { + // configure line from top vertex to middle vertex + int x2 = vx1; + int dx2 = vx2 - vx1; + int dy2 = vy2 - vy1; + int sign_x2 = dx2 < 0 ? -1 : 1; + int step_x2 = dx2/dy2; + int sub_step_x2 = abs(dx2)%dy2; + int sub_step_accum2 = -dy2/2; + + // skip invisible rows + int y1 = vy1, y2 = vy2; + if (y1 < 0) { + const int rows = (y2 < 0 ? y2 : 0) - y1; + y1 += rows; + x1 += rows*step_x1; + sub_step_accum1 += rows*sub_step_x1; + if (sub_step_accum1 > 0) { + const int count = sub_step_accum1/dy1 + 1; + sub_step_accum1 -= count*dy1; + x1 += count*sign_x1; + } + + x2 += rows*step_x2; + sub_step_accum2 += rows*sub_step_x2; + if (sub_step_accum2 > 0) { + const int count = sub_step_accum2/dy2 + 1; + sub_step_accum2 -= count*dy2; + x2 += count*sign_x2; + } + } + if (y2 > vh) y2 = vh; + + // iterate over visible rows + for(int y = y1; y < y2; ++y) { + row(y, x1, x2); + + // increment line 1 + x1 += step_x1; + sub_step_accum1 += sub_step_x1; + if (sub_step_accum1 > 0) + { sub_step_accum1 -= dy1; x1 += sign_x1; } + + // increment line 2 + x2 += step_x2; + sub_step_accum2 += sub_step_x2; + if (sub_step_accum2 > 0) + { sub_step_accum2 -= dy2; x2 += sign_x2; } + } + } + + // draw bottom triangle + if (vy3 > vy2) { + // configure line from middle vertex to bottom vertex + int x2 = vx2; + int dx2 = vx3 - vx2; + int dy2 = vy3 - vy2; + int sign_x2 = dx2 < 0 ? -1 : 1; + int step_x2 = dx2/dy2; + int sub_step_x2 = abs(dx2)%dy2; + int sub_step_accum2 = -dy2/2; + + // skip invisible rows + int y1 = vy2, y2 = vy3; + if (y1 < 0) { + const int rows = -y1; + y1 += rows; + x1 += rows*step_x1; + sub_step_accum1 += rows*sub_step_x1; + if (sub_step_accum1 > 0) { + const int count = sub_step_accum1/dy1 + 1; + sub_step_accum1 -= count*dy1; + x1 += count*sign_x1; + } + + x2 += rows*step_x2; + sub_step_accum2 += rows*sub_step_x2; + if (sub_step_accum2 > 0) { + const int count = sub_step_accum2/dy2 + 1; + sub_step_accum2 -= count*dy2; + x2 += count*sign_x2; + } + } + if (y2 > vh) y2 = vh; + + // iterate over visible rows + for(int y = y1; y < y2; ++y) { + row(y, x1, x2); + + // increment line 1 + x1 += step_x1; + sub_step_accum1 += sub_step_x1; + if (sub_step_accum1 > 0) + { sub_step_accum1 -= dy1; x1 += sign_x1; } + + // increment line 2 + x2 += step_x2; + sub_step_accum2 += sub_step_x2; + if (sub_step_accum2 > 0) + { sub_step_accum2 -= dy2; x2 += sign_x2; } + } + } +} + + +void rowNew(int y, int x1, int x2, double *color, double *inc) { + if (x1 < x2) { + if (x1 >= vw || x2 <= 0) return; + if (x1 < 0) { + int skip = -x1; + for(int i = 0; i < 4; ++i) color[i] += skip*inc[i]; + x1 += skip; + } + if (x2 > vw) x2 = vw; + for(int x = x1; x < x2; ++x) { + dotColored(x, y, color); + for(int i = 0; i < 4; ++i) color[i] += inc[i]; + } + } else { + if (x2 >= vw || x1 <= 0) return; + if (x1 > vw) { + int skip = vw - x1; + for(int i = 0; i < 4; ++i) color[i] += skip*inc[i]; + x1 += skip; + } + if (x2 < 0) x2 = 0; + for(int x = x1-1; x >= x2; --x) { + for(int i = 0; i < 4; ++i) color[i] -= inc[i]; + dotColored(x, y, color); + } + } +} + + +void triangleNew(const Vertex *v1, const Vertex *v2, const Vertex *v3) { + if (v1->y > v3->y) { const Vertex *v = v1; v1 = v3; v3 = v; } + if (v2->y > v3->y) { const Vertex *v = v2; v2 = v3; v3 = v; } + if (v1->y > v2->y) { const Vertex *v = v1; v1 = v2; v2 = v; } + + // split triangle to two triangles by the middle vertex + // + // *__ + // / --__ - top triangle here + // /-------__-* - split triangle by this line + // / __-- + // / __-- - bottom triangle here + // *-- + + // include pixels at left and top sides, but exclude at right and bottom + // it required to seamless clue neighboring triangles without overlapping + + const int vx1 = (int)floor(v1->x); + const int vy1 = (int)floor(v1->y); + const int vx2 = (int)floor(v2->x); + const int vy2 = (int)floor(v2->y); + const int vx3 = (int)floor(v3->x); + const int vy3 = (int)floor(v3->y); + + const int dx31 = vx3 - vx1; + const int dy31 = vy3 - vy1; + + const int dx21 = vx2 - vx1; + const int dy21 = vy2 - vy1; + + const int dx32 = vx3 - vx2; + const int dy32 = vy3 - vy2; + + if (dy31 <= 0) return; + if (vy1 >= vh || vy3 <= 0) return; + if (vx1 >= vw && vx2 >= vw && vx3 >= vw) return; + if (vx1 <= 0 && vx2 <= 0 && vx3 <= 0 ) return; + + // calculate area + const int double_area = dx31*dy31 - dx21*dy21 + dx32*dy32 - 2*dx32*dy31; + if (!double_area) return; + const double one_div_2a = 1.0/double_area; + + // configure line from top vertex to bottom vertex + const int sign_x1 = dx31 < 0 ? -1 : 1; + const int step_x1 = dx31/dy31; + const int sub_step_x1 = abs(dx31)%dy31; + int x1 = vx1; + int sub_step_accum1 = -dy31/2; + + // configure interpolation coefficients for the middle vertex + const double k2_dx = one_div_2a*dy31; + const double k2_dy = one_div_2a*(dy31*step_x1 - dx31); + double k2 = 0; + + // draw top triangle + if (vy2 > vy1) { + // configure line from top vertex to middle vertex + const int sign_x2 = dx21 < 0 ? -1 : 1; + const int step_x2 = dx21/dy21; + const int sub_step_x2 = abs(dx21)%dy21; + int x2 = vx1; + int sub_step_accum2 = -dy21/2; + + // configure interpolation coefficients for the bottom vertex + const double k3_dx = -one_div_2a*dy21; + const double k3_dy = -one_div_2a*(dy21*step_x1 - dx21); + double color[4], inc[4]; + double k3 = 0; + for(int i = 0; i < 4; ++i) + inc[i] = k2_dx*v2->color[i] + k3_dx*v3->color[i] - (k2_dx + k3_dx)*v1->color[i]; + + // skip invisible rows + int y1 = vy1, y2 = vy2; + if (y1 < 0) { + const int rows = (y2 < 0 ? y2 : 0) - y1; + y1 += rows; + x1 += rows*step_x1; + k2 += rows*k2_dy; + k3 += rows*k3_dy; + sub_step_accum1 += rows*sub_step_x1; + if (sub_step_accum1 > 0) { + const int count = sub_step_accum1/dy31 + 1; + sub_step_accum1 -= count*dy31; + x1 += count*sign_x1; + k2 += count*sign_x1*k2_dx; + k3 += count*sign_x1*k3_dx; + } + + x2 += rows*step_x2; + sub_step_accum2 += rows*sub_step_x2; + if (sub_step_accum2 > 0) { + const int count = sub_step_accum2/dy21 + 1; + sub_step_accum2 -= count*dy21; + x2 += count*sign_x2; + } + } + if (y2 > vh) y2 = vh; + + // iterate over visible rows + for(int y = y1; y < y2; ++y) { + double k1 = 1 - k2 - k3; + for(int i = 0; i < 4; ++i) + color[i] = k1*v1->color[i] + k2*v2->color[i] + k3*v3->color[i]; + rowNew(y, x1, x2, color, inc); + + // increment line 1 + x1 += step_x1; + k2 += k2_dy; + k3 += k3_dy; + sub_step_accum1 += sub_step_x1; + if (sub_step_accum1 > 0) { + sub_step_accum1 -= dy31; + x1 += sign_x1; + k2 += sign_x1*k2_dx; + k3 += sign_x1*k3_dx; + } + + // increment line 2 + x2 += step_x2; + sub_step_accum2 += sub_step_x2; + if (sub_step_accum2 > 0) + { sub_step_accum2 -= dy21; x2 += sign_x2; } + } + } + + // draw bottom triangle + if (vy3 > vy2 && vy2 < vh) { + // configure line from middle vertex to bottom vertex + const int sign_x2 = dx32 < 0 ? -1 : 1; + const int step_x2 = dx32/dy32; + const int sub_step_x2 = abs(dx32)%dy32; + int sub_step_accum2 = -dy32/2; + int x2 = vx2; + + // configure interpolation coefficients for the top vertex + const double k1_dx = -one_div_2a*dy32; + const double k1_dy = -one_div_2a*(dy32*step_x1 - dx32); + double color[4], inc[4]; + double k1 = -one_div_2a*dy32*(x1 - vx2); + for(int i = 0; i < 4; ++i) + inc[i] = k1_dx*v1->color[i] + k2_dx*v2->color[i] - (k1_dx + k2_dx)*v3->color[i]; + + // skip invisible rows + int y1 = vy2, y2 = vy3; + if (y1 < 0) { + const int rows = -y1; + y1 += rows; + x1 += rows*step_x1; + k1 += rows*k1_dy; + k2 += rows*k2_dy; + sub_step_accum1 += rows*sub_step_x1; + if (sub_step_accum1 > 0) { + const int count = sub_step_accum1/dy31 + 1; + sub_step_accum1 -= count*dy31; + x1 += count*sign_x1; + k1 += count*sign_x1*k1_dx; + k2 += count*sign_x1*k2_dx; + } + + x2 += rows*step_x2; + sub_step_accum2 += rows*sub_step_x2; + if (sub_step_accum2 > 0) { + const int count = sub_step_accum2/dy32 + 1; + sub_step_accum2 -= count*dy32; + x2 += count*sign_x2; + } + } + if (y2 > vh) y2 = vh; + + // iterate over visible rows + for(int y = y1; y < y2; ++y) { + double k3 = 1 - k1 - k2; + for(int i = 0; i < 4; ++i) + color[i] = k1*v1->color[i] + k2*v2->color[i] + k3*v3->color[i]; + rowNew(y, x1, x2, color, inc); + + // increment line 1 + x1 += step_x1; + k1 += k1_dy; + k2 += k2_dy; + sub_step_accum1 += sub_step_x1; + if (sub_step_accum1 > 0) { + sub_step_accum1 -= dy31; + x1 += sign_x1; + k1 += sign_x1*k1_dx; + k2 += sign_x1*k2_dx; + } + + // increment line 2 + x2 += step_x2; + sub_step_accum2 += sub_step_x2; + if (sub_step_accum2 > 0) + { sub_step_accum2 -= dy32; x2 += sign_x2; } + } + } +} + + +void setTriangle(int x1, int y1, int x2, int y2, int x3, int y3) { + tv1.x = lx1 = x1; tv1.y = ly1 = y1; + tv2.x = lx2 = x2; tv2.y = ly2 = y2; + tv3.x = lx3 = x3; tv3.y = ly3 = y3; + printf("setTriangle(%2d, %2d, %2d, %2d, %2d, %2d);\n", lx1, ly1, lx2, ly2, lx3, ly3); +} + +void setLine(int x1, int y1, int x2, int y2) { + lx1 = x1; ly1 = y1; + lx2 = x2; ly2 = y2; + printf("setLine(%2d, %2d, %2d, %2d);\n", lx1, ly1, lx2, ly2); +} + +void generateLine() { + setLine( randomNumber(0, vw-1), randomNumber(0, vh-1), + randomNumber(0, vw-1), randomNumber(0, vh-1) ); +} + +void generateTriangle() { + setTriangle( randomNumber(-vw/2, vw+vw/2), randomNumber(-vh/2, vh+vh/2), + randomNumber(-vw/2, vw+vw/2), randomNumber(-vh/2, vh+vh/2), + randomNumber(-vw/2, vw+vw/2), randomNumber(-vh/2, vh+vh/2) ); +} + + +void init() { + tv1.color[0] = tv1.color[3] = 1; + tv2.color[1] = tv2.color[3] = 1; + tv3.color[2] = tv3.color[3] = 1; + + //generateLine(); + //setLine( 2, 18, 14, 1); + //setLine(14, 1, 2, 18); + //setLine( 0, 0, 12, 7); + //generateTriangle(); + setTriangle(94, 155, 116, 46, 236, 96); +} + + +void deinit() { } + + +void draw() { + double w = windowGetWidth(); + double h = windowGetHeight(); + + saveState(); + translate(0.5*w, 0.5*h); + zoom(cs); + translate(-0.5*vw, -0.5*vh); + grid(vw, vh); + + if (keyWentDown("space")) generateTriangle(); + + + noStroke(); + fill(colorByNameA("blue", 0.5)); + triangle(lx1, ly1, lx2, ly2, lx3, ly3); + //triangleNew(&tv1, &tv2, &tv3); + + stroke(colorByName("black")); + strokeWidth(0.1); + dotColored(lx1, ly1, tv1.color); + dotColored(lx2, ly2, tv2.color); + dotColored(lx3, ly3, tv3.color); + + restoreState(); +} + + +int main() { + windowSetVariableFrameRate(); + windowSetResizable(TRUE); + windowSetSize(2*cs*vw, 2*cs*vh); + windowSetInit(&init); + windowSetDeinit(&deinit); + windowSetDraw(&draw); + windowRun(); +} diff --git a/onefile/rubber.c b/onefile/rubber.c new file mode 100644 index 0000000..7b0dd7f --- /dev/null +++ b/onefile/rubber.c @@ -0,0 +1,160 @@ + +#include +#include +#include + + +#define MAX_BALLS 1000 +#define MAX_LINKS 10000 +#define BALL_RADIUS 1.0 +#define BALL_FADE 0.8 +#define LINK_K 0.05 + + +typedef struct { + double x, y; + double vx, vy; +} Ball; + + +typedef struct { + double dx, dy; + Ball *a, *b; +} Link; + + +Ball balls[MAX_BALLS]; +Link links[MAX_LINKS]; +int ballsCount = 0; +int linksCount = 0; + +double remainDt; + +Ball *mball; +double mdx, mdy; +double mx, my; + + +void ballUpdate(Ball *ball, double dt) { + double fade = pow(BALL_FADE, dt); + ball->x += ball->vx*dt; + ball->y += ball->vy*dt; + ball->vx *= fade; + ball->vy *= fade; +} + + +void linkUpdate(Link *link, double dt) { + double dx = link->b->x - link->a->x; + double dy = link->b->y - link->a->y; + + double dvx = (dx - link->dx)*0.5*LINK_K; + double dvy = (dy - link->dy)*0.5*LINK_K; + + link->a->vx += dvx; link->b->vx -= dvx; + link->a->vy += dvy; link->b->vy -= dvy; +} + + +void update(double dt) { + remainDt += dt; + while(remainDt > 0.001) { + remainDt -= 0.001; + for(int i = 0; i < linksCount; ++i) + linkUpdate(&links[i], dt); + for(int i = 0; i < ballsCount; ++i) + ballUpdate(&balls[i], dt); + } +} + + +void init() { + int cnt = 10; + double dist = 3*BALL_RADIUS; + for(int i = 0; i < cnt; ++i) { + for(int j = 0; j < cnt; ++j) { + Ball *ball = &balls[ballsCount++]; + ball->x = (i - 0.5*(cnt - 1))*dist; + ball->y = (j - 0.5*(cnt - 1))*dist; + if (i > 0) { + Link *link = &links[linksCount++]; + link->a = ball - cnt; + link->b = ball; + link->dx = dist; + } + if (j > 0) { + Link *link = &links[linksCount++]; + link->a = ball - 1; + link->b = ball; + link->dy = dist; + } + } + } +} + + +void draw() { + double dt = windowGetFrameTime(); + double w = windowGetWidth(); + double h = windowGetHeight(); + + saveState(); + translate(w/2, h/2); + zoom(20); + + mx = mouseTransformedX(); + my = mouseTransformedY(); + if (mouseDown("left")) { + if (!mball) { + for(int i = ballsCount-1; i >= 0; --i) { + Ball *ball = &balls[i]; + double dx = mx - ball->x; + double dy = my - ball->y; + if (dx*dx + dy*dy <= BALL_RADIUS*BALL_RADIUS) { + mball = ball; + mdx = dx; + mdy = dy; + break; + } + } + } + } else { + mball = NULL; + } + + + update(dt); + + if (mball) { + mball->x = mx - mdx; + mball->y = my - mdy; + mball->vx = 0; + mball->vy = 0; + } + + stroke(0xffe666ff); + strokeWidth(BALL_RADIUS*0.1); + noFill(); + for(int i = 0; i < linksCount; ++i) { + Link *link = &links[i]; + line(link->a->x, link->a->y, link->b->x, link->b->y); + } + + noStroke(); + fill(0xffe666ff); + for(int i = 0; i < ballsCount; ++i) { + Ball *ball = &balls[i]; + circle(ball->x, ball->y, BALL_RADIUS); + } + + restoreState(); +} + + +int main() { + windowSetResizable(TRUE); + windowSetVariableFrameRate(); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); +} diff --git a/onefile/scheme.png b/onefile/scheme.png new file mode 100644 index 0000000..8083ede Binary files /dev/null and b/onefile/scheme.png differ diff --git a/onefile/svg-path.inc.h b/onefile/svg-path.inc.h new file mode 100644 index 0000000..7666e7f --- /dev/null +++ b/onefile/svg-path.inc.h @@ -0,0 +1,222 @@ + +#define SP_PRECISION 0.5 + + +int spStarted = FALSE; +double spStartX = 0, spStartY = 0; +double spPenX = 0, spPenY = 0; + + +void spSkipSpace(const char **pos) + { while(**pos && isspace(**pos)) ++*pos; } + + +double spReadNum(const char **pos) { + spSkipSpace(pos); + char *end = ""; + double x = strtod(*pos, &end); + *pos = end; + return x; +} + + +double spMax(double a, double b) + { return a > b ? a : b; } + + +void spMoveTo(double x, double y) { + if (spStarted) strokePath(); + spStarted = TRUE; + moveTo(x, y); + spStartX = spPenX = x; + spStartY = spPenY = y; +} + + +void spLineTo(double x, double y) { + if (!spStarted) spMoveTo(spPenX, spPenY); + lineTo(x, y); + spPenX = x; + spPenY = y; +} + + +void spClose() { + if (spStarted) { lineTo(spStartX, spStartY); closePath(); } + spMoveTo(spStartX, spStartY); +} + + +void spQuadraticTo(double x1, double y1, double x2, double y2) { + double dx = spMax(x1 - spPenX, x2 - spPenX); + double dy = spMax(y1 - spPenY, y2 - spPenY); + if (dx*dx + dy*dy <= SP_PRECISION*SP_PRECISION) { + spLineTo(x2, y2); + return; + } + + double xx0 = 0.5*(spPenX + x1); + double yy0 = 0.5*(spPenY + y1); + + double xx1 = 0.5*(x1 + x2); + double yy1 = 0.5*(y1 + y2); + + double xxx0 = 0.5*(xx0 + xx1); + double yyy0 = 0.5*(yy0 + yy1); + + spQuadraticTo(xx0, yy0, xxx0, yyy0); + spQuadraticTo(xx1, yy1, x2, y2); +} + + +void spCurveTo(double x1, double y1, double x2, double y2, double x3, double y3) { + double dx = spMax(spMax(x1 - spPenX, x2 - spPenX), x3 - spPenX); + double dy = spMax(spMax(y1 - spPenY, y2 - spPenY), y3 - spPenY); + if (dx*dx + dy*dy <= SP_PRECISION*SP_PRECISION) { + spLineTo(x2, y2); + return; + } + + double xx0 = 0.5*(spPenX + x1); + double yy0 = 0.5*(spPenY + y1); + + double xx1 = 0.5*(x1 + x2); + double yy1 = 0.5*(y1 + y2); + + double xx2 = 0.5*(x2 + x3); + double yy2 = 0.5*(y2 + y3); + + double xxx0 = 0.5*(xx0 + xx1); + double yyy0 = 0.5*(yy0 + yy1); + + double xxx1 = 0.5*(xx1 + xx2); + double yyy1 = 0.5*(yy1 + yy2); + + double xxxx0 = 0.5*(xxx0 + xxx1); + double yyyy0 = 0.5*(yyy0 + yyy1); + + spCurveTo(xx0, yy0, xxx0, yyy0, xxxx0, yyyy0); + spCurveTo(xxx1, yyy1, xx2, yy2, x3, y3); +} + + +void spParse(const char *path) { + double x1, x2, x3; + double y1, y2, y3; + const char *c = path; + spSkipSpace(&c); + while(*c) { + switch(*c++) { + case 'M': + x1 = spReadNum(&c); + y1 = spReadNum(&c); + spMoveTo(x1, y1); + break; + case 'm': + x1 = spPenX + spReadNum(&c); + y1 = spPenY + spReadNum(&c); + spMoveTo(x1, y1); + break; + case 'L': + x1 = spReadNum(&c); + y1 = spReadNum(&c); + spLineTo(x1, y1); + break; + case 'l': + x1 = spPenX + spReadNum(&c); + y1 = spPenY + spReadNum(&c); + spLineTo(x1, y1); + break; + case 'Q': + x1 = spReadNum(&c); + y1 = spReadNum(&c); + x2 = spReadNum(&c); + y2 = spReadNum(&c); + spQuadraticTo(x1, y1, x2, y2); + break; + case 'q': + x1 = spPenX + spReadNum(&c); + y1 = spPenY + spReadNum(&c); + x2 = spPenX + spReadNum(&c); + y2 = spPenY + spReadNum(&c); + spQuadraticTo(x1, y1, x2, y2); + break; + case 'C': + x1 = spReadNum(&c); + y1 = spReadNum(&c); + x2 = spReadNum(&c); + y2 = spReadNum(&c); + x3 = spReadNum(&c); + y3 = spReadNum(&c); + spCurveTo(x1, y1, x2, y2, x3, y3); + break; + case 'c': + x1 = spPenX + spReadNum(&c); + y1 = spPenY + spReadNum(&c); + x2 = spPenX + spReadNum(&c); + y2 = spPenY + spReadNum(&c); + x3 = spPenX + spReadNum(&c); + y3 = spPenY + spReadNum(&c); + spCurveTo(x1, y1, x2, y2, x3, y3); + break; + case 'Z': + case 'z': + spClose(); + break; + default: + printf("unknown path command \'%c\'\n", *(c - 1)); + } + spSkipSpace(&c); + } +} + + +void spTrack(const char *path, double *x, double *y) { + double sx = *x, sy = *y; + const char *c = path; + spSkipSpace(&c); + while(*c) { + switch(*c++) { + case 'M': + sx = (*x = spReadNum(&c)); + sy = (*y = spReadNum(&c)); + break; + case 'm': + sx = (*x += spReadNum(&c)); + sy = (*y += spReadNum(&c)); + break; + + case 'C': + spReadNum(&c); + spReadNum(&c); + case 'Q': + spReadNum(&c); + spReadNum(&c); + case 'L': + *x = spReadNum(&c); + *y = spReadNum(&c); + break; + + case 'c': + spReadNum(&c); + spReadNum(&c); + case 'q': + spReadNum(&c); + spReadNum(&c); + case 'l': + *x += spReadNum(&c); + *y += spReadNum(&c); + break; + + case 'Z': + case 'z': + *x = sx; + *y = sy; + break; + default: + printf("unknown path command \'%c\'\n", *(c - 1)); + } + spSkipSpace(&c); + } +} + diff --git a/onefile/svg-save.inc.h b/onefile/svg-save.inc.h new file mode 100644 index 0000000..3838bd2 --- /dev/null +++ b/onefile/svg-save.inc.h @@ -0,0 +1,32 @@ + + +FILE* svgBegin(const char *filename, double widthMM, double heightMM, double unitsPerMM) { + FILE *f = fopen(filename, "w"); + if (f) { + fprintf(f, "\n", + widthMM, heightMM, widthMM*unitsPerMM, heightMM*unitsPerMM ); + } + return f; +} + + +void svgEnd(FILE *f) { + if (!f) return; + fprintf(f, "\n"); + fclose(f); +} + + +void svgAddText(FILE *f, double x, double y, double size, const char *text) { + if (!f) return; + fprintf(f, "%s\n", x, y, size, text); +} + + +void svgAddPath(FILE *f, const char *path, double width, int round) { + if (!f) return; + fprintf(f, "\n", + (round ? "stroke-linecap:round;stroke-linejoin:round;" : ""), + width, path ); +} diff --git a/onefile/triangle-fractal.c b/onefile/triangle-fractal.c new file mode 100644 index 0000000..3546e46 --- /dev/null +++ b/onefile/triangle-fractal.c @@ -0,0 +1,74 @@ + +#include +#include + + +#define COUNT 9 + + +unsigned int colors[3] = { COLOR_RED, COLOR_BLUE, COLOR_YELLOW }; + + +void drawTriangle(double x, double y, double radius, double angle, unsigned int color, int level) { + if (level <= 0) { + saveState(); + strokeWidth(radius * 0.1); + fill(color); + regularPolygon(x, y, 3, radius, angle); + restoreState(); + } else { + --level; + double r = radius/((COUNT + 3)/2); + radius -= r; + + saveState(); + noStroke(); + fill(colorWithAlpha(color, 0.5)); + regularPolygon(x, y, 3, radius - r - r, angle); + restoreState(); + + double a0 = angle*PI/180; + double da = 2*PI/3; + for(int i = 0; i < 3; ++i) { + double x0 = x + radius*cos(a0 + i*da); + double y0 = y + radius*sin(a0 + i*da); + double x1 = x + radius*cos(a0 + i*da + da); + double y1 = y + radius*sin(a0 + i*da + da); + double dx = cos(a0 + i*da + da/2)*r/2; + double dy = sin(a0 + i*da + da/2)*r/2; + for(int j = 0; j < COUNT; ++j) { + double k = (double)j/(COUNT + 1); + double xx = x0*(1 - k) + x1*k; + double yy = y0*(1 - k) + y1*k; + if (j % 2) { xx -= dx; yy -= dy; } + double a = angle + j*180; + drawTriangle(xx, yy, r, a, colors[i], level); + } + } + } +} + + +void init() { +} + + +void draw() { + double w = windowGetWidth(); + double h = windowGetHeight(); + + saveState(); + translate(w/2, h/2); + drawTriangle(0, 0, 400, 0, colors[0], 2); + restoreState(); +} + + +int main() { + windowSetResizable(TRUE); + windowSetVariableFrameRate(); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); +} + diff --git a/onefile/word-splitter.c b/onefile/word-splitter.c new file mode 100644 index 0000000..120a792 --- /dev/null +++ b/onefile/word-splitter.c @@ -0,0 +1,120 @@ + + +#include +#include +#include +#include + + +typedef struct { + int mode; + char c[4]; +} UChar; + +char *ca[] = { "а", "е", "ё", "и", "о", "у", "ы", "э", "ю", "я", + "А", "Е", "Ё", "И", "О", "У", "Ы", "Э", "Ю", "Я" }; +char *cb[] = { "б", "в", "г", "д", "ж", "з", "й", "к", "л", "м", "н", "п", "р", "с", "т", "ф", "х", "ц", "ч", "ш", "щ", + "Б", "В", "Г", "Д", "Ж", "З", "Й", "К", "Л", "М", "Н", "П", "Р", "С", "Т", "Ф", "Х", "Ц", "Ч", "Ш", "Щ" }; +char *cc[] = { "ъ", "ь", + "Ъ", "Ь" }; + + +void splitWord(UChar *word, FILE *fo) { + UChar *pos = word; + UChar *pa = NULL; + int count = 0; + while(word->c[0]) { + if (word->mode == 1) { + if (pa) { + int cnt = count / 2; + while(pos <= pa) fputs((pos++)->c, fo); + while(cnt > 0) { + fputs((pos++)->c, fo); + if (pos->mode != 3) --cnt; + } + //fprintf(fo, "[%d]", count); + fputs("\u030D\u0329", fo); + //fputs("|", fo); + } + pa = word; + count = 0; + } else + if (pa && word->mode != 3) { + ++count; + } + ++word; + } + while(pos < word) fputs((pos++)->c, fo); +} + + +int findStr(char *str, char **list, int count) { + while(count-- > 0) + if (strcmp(str, *list++) == 0) return 1; + return FALSE; +} + + +void readUChar(UChar *uc, FILE *fi) { + memset(uc, 0, sizeof(*uc)); + int c = fgetc(fi); + int cnt = 1; + if (c > 128 + 64 + 32 + 16) cnt = 4; else + if (c > 128 + 64 + 32) cnt = 3; else + if (c > 128 + 64) cnt = 2; else + if (c <= 0) return; + + uc->c[0] = c; + for(int i = 1; i < cnt; ++i) + uc->c[i] = fgetc(fi); + + if (findStr(uc->c, ca, sizeof(ca)/sizeof(*ca))) uc->mode = 1; else + if (findStr(uc->c, cb, sizeof(cb)/sizeof(*cb))) uc->mode = 2; else + if (findStr(uc->c, cc, sizeof(cc)/sizeof(*cc))) uc->mode = 3; + //printf("readUChar: m%d [%s], %d\n", uc->mode, uc->c, (int)uc->c[0]); +} + + +void splitFile(FILE *fi, FILE *fo) { + UChar word[1024]; + UChar *pc = word, *pe = word + sizeof(word)/sizeof(*word), uc = {}; + while(1) { + readUChar(&uc, fi); + if (uc.mode && pc < pe) { + memcpy(pc++, &uc, sizeof(uc)); + } else { + if (pc > word) { + pc->c[0] = 0; + splitWord(word, fo); + pc = word; + } + if (!uc.c[0]) break; + fputs(uc.c, fo); + } + } +} + + +void init() { + FILE *fi = fopen("data/text-to-split.txt", "r"); + FILE *fo = fopen("data/output/splitted-text.txt", "w"); + if (fi && fo) splitFile(fi, fo); + if (fi) fclose(fi); + if (fo) fclose(fo); +} + + +void draw() { + text(10, 10, "loaded from: data/text-to-split.txt\n" + "saved to: data/output/splitted-text.txt"); +} + + +int main() { + windowSetResizable(TRUE); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); + return 0; +} + diff --git a/onefile/writing.c b/onefile/writing.c new file mode 100644 index 0000000..85faefa --- /dev/null +++ b/onefile/writing.c @@ -0,0 +1,450 @@ + + +#include +#include +#include + +#include + + +#include "svg-path.inc.h" +#include "svg-save.inc.h" + +#define MSPACE "m 60 0" +#define MDOTSP "m 20 0" +#define MUP "m 10 -25 " +#define MUP3 "m 30 -75 " +#define MDN "m -10 25 " +#define MUPE "m 4 -10 " +#define MDNE "m -4 10 " + +#define Co1 "q 6 -15 -4 -15 " +#define Co2 "q -10 0 -16 15 " +#define Co3 "q -6 15 4 15 " +#define Co4 "q 10 0 16 -15 " + +#define CO1 "q 10 -25 -5 -25 " +#define CO2 "q -15 0 -25 25 " +#define CO3 "q -10 25 5 25 " +#define CO4 "q 15 0 25 -25 " + +#define CO1B "q 15 0 5 25 " +#define CO2B "q 10 -25 25 -25 " +#define CO3B "q -15 0 -5 -25 " +#define CO4B "q -10 25 -25 25 " + +#define CFO1 CO1 CO2 CO3 CO4 +#define CFO2 CO2 CO3 CO4 CO1 +#define CFO3 CO3 CO4 CO1 CO2 +#define CFO4 CO4 CO1 CO2 CO3 + +#define CLU "q 50 -50 30 0 " +#define CLD "q -20 50 30 0 " +#define CLLD "q -10 25 0 25 q 15 0 40 -50 " + +#define CKD1 "c 14 -35 44 -35 38 -20 " +#define CKD2 "q -18 45 32 -5 " + +#define CJL0 "q -10 25 0 25 " +#define CJL CJL0 "q 15 0 50 -50 " +#define CJR CJL0 "c 15 0 40 -25 44 -35 " Co1 Co2 Co3 Co4 "l 6 -15 " + +#define CGD0 CLD CDN "q -10 25 -20 25 " +#define CGD CGD0 "c -20 0 20 -25 45 -50 " +#define CGDE CGD0 "c -20 0 19 -25 39 -35 " + +#define CQU1 "c 25 -25 51 -40 61 -65 " +#define CQU2 "q 4 -10 -6 -10 q -10 0 -20 25 l -20 50 " +#define CQU CQU1 CQU2 + +#define CQD1 CDN2 +#define CQD2 "q -10 25 -20 25 q -10 0 -6 -10 " +#define CQD3 "c 10 -25 36 -40 61 -65 " +#define CQD3E "c 10 -25 35 -40 55 -50 " +#define CQD CQD1 CQD2 CQD3 + +#define CC1 "q 5 0 8 5 " +#define CC2 "q -3 -5 -8 -5 " CO2 CO3 +#define CC CC1 CC2 +#define CXL CO1B CO4B "q -5 0 -8 -5 q 3 5 8 5 " + +#define Cb0 "l -4 10 " Co3 Co4 Co1 +#define Cb Cb0 Co2 Co3 + +#define CUP "l 10 -25 " +#define CDN "l -10 25 " +#define CUP2 "l 20 -50 " +#define CDN2 "l -20 50 " + +#define CEE "m -18 -65 l 2 -2 m 10 0 l -2 2 m 8 65 " +#define CYY "m -28 -65 q 3 5 17 -5 m 11 70 " + +#define CDot "m 2 0 q 0 2 -2 2 q -2 0 -2 -2 q 0 -2 2 -2 q 2 0 2 2 " +#define CComma CDot "q -1 5 -8 10 m 8 -10 " + + +#define C_O1 "q 20 -50 0 -50 " +#define C_O2 "q -20 0 -40 50 " +#define C_O3 "q -20 50 0 50 " +#define C_O4 "q 20 0 40 -50 " + +#define C_O1B "q 20 0 0 50 " +#define C_O2B "q 20 -50 40 -50 " +#define C_O3B "q -20 0 0 -50 " +#define C_O4B "q -20 50 -40 50 " + +#define C_XL1 "m 36 -90 q 10 -10 18 -10 " +#define C_XL2 C_O1B C_O4B +#define C_XL3 "q -7.5 0 -14 -10 q 3.5 10 14 10 " +#define C_XL C_XL1 C_XL2 C_XL3 + +#define C_XR1 "q 7.5 0 14 10 " +#define C_XR2 "q -3.5 -10 -14 -10 " +#define C_XR3 C_O2 C_O3 +#define C_XR C_XR1 C_XR2 C_XR3 + +#define C_AL0 "m 4 -10 q 1 10 11 10 " +#define C_AL C_AL0 "q 30 0 95 -100 " + +#define C_LU CLU +#define C_LD CLD +#define C_LLD "q -10 25 0 25 q 15 0 70 -100 " + +#define C_J1 "m 64 -85 l -24 60 " +#define C_J2 "l -4 10 q -6 15 -21 15 q -5 0 -11 -10 m 11 10 " +#define C_JJ2 "l -4 10 q -6 15 -21 15 q -5 0 -11 -10 q 1 10 11 10 " +#define C_J C_J1 C_J2 + +#define C_T "m 28 -70 c -10 -10 -8 -30 12 -30 " + +#define C_E "m 69 -90 " C_XR2 "q -15 0 -23 20 q -3 25 15 25 q -25 0 -35 25 q -12 30 13 30 " + +#define C_3 C_XL1 "q 15 0 7 20 q -10 25 -25 25 q 15 0 5 25 q -12 30 -32 30 " C_XL3 + +#define C_B1 "m 60 -100 " CDN CDN2 CQD2 "q 56 -90 71 -90 " +#define C_B2 "q 15 0 7 20 q -10 25 -30 25 " +#define C_B3 "q 25 0 15 25 q -12 30 -32 30 q -5 0 -8 -5 q 3 5 8 5 " +#define C_B C_B1 C_B2 C_B3 + +#define C_EE "m 31 -115 l 2 -2 m 10 0 l -2 2 m -41 115 " +#define C_YY "m -26.5 -15 q 3 5 17 -5 m 9.5 20 " + + +#define LLBO MUP "q 25 -25 35 -25 " + + +#define LBL { MUP CLU } +#define LBO { "m 35 -50 ", LLBO } +#define LBA { "m 40 -25 ", LLBO CO1B } +#define LBQ { MUP CQU } +#define LBE { MUPE "c 46 -23 56 -40 41 -40 " } +#define LBN { "m 20 -50 " CDN, MUP "l 25 -25 " CDN } +#define LBX { "m 18 -45 q 7 -5 12 -5 ", LLBO } +#define LBC { "m 38 -45 ", LLBO CC1 } +#define LBJ { MUP CJL } +#define LBr { MUP "l 25 -25 q 5 25 30 0 " } +#define LBR { MUP CJR } + +#define LEL { CLD MDN, NULL, "c -14 35 4 25 24 15 " MDNE } +#define LELY { CLD MDN CYY, NULL, "c -14 35 4 25 24 15 " MDNE CYY } +#define LEO { "m 15 0 ", "q 15 0 40 -25 " MDN, "q 19 0 39 -10 " MDNE } +#define LEE { "q 19 0 39 -10 " MDNE, "q 15 0 40 -25 " MDN } +#define LEEE { "q 19 0 39 -10 " MDNE CEE, "q 15 0 40 -25 " MDN CEE } +#define LEQ { CQD2 CQD3 MDN, NULL, CQD2 CQD3E MDNE } +#define LEX { "q 15 0 40 -25 " MDN, NULL, "q 19 0 39 -10 " MDNE } +#define LEK { CKD2 MDN, NULL, "c -12 30 2 20 22 10 " MDNE } +#define LEG { CGD MDN, NULL, CGDE MDNE } + + +#define L_EO1 { "m 30 0 ", "q 15 0 40 -25 " MDN, "q 19 0 39 -10 " MDNE } +#define L_EO2 { "m 60 0 ", "q 15 0 40 -25 " MDN, "q 19 0 39 -10 " MDNE } +#define L_EO3 { "m 75 0 ", "q 15 0 40 -25 " MDN, "q 19 0 39 -10 " MDNE } + +#define L_EA { "q 20 -50 -10 -50 c -20 0 -16 40 14 40 q 21 0 36 -15 " MDN, NULL, \ + "q 20 -50 -10 -50 c -20 0 -18 45 12 45 q 22 0 32 -5 " MDNE } + + + +typedef struct { + const char *base; + const char *link0; + const char *link1; +} Link; + +typedef struct { + const char *name; + Link begin; + const char *mid; + Link end; + int linkMode; + const char *alias; +} Letter; + + +Letter letters[] = { + { "а", LBA, CFO1 CUP CDN, LEL }, + { "б", LBA, CFO1 CUP2 "q 10 -25 25 -25 l 15 0 m -85 100", LEO }, + { "в", LBQ, CFO3 CO3, LEO }, + { "г", LBL, "", LEL }, + { "д", LBA, CFO1 CUP CDN CQD1, LEQ }, + { "е", LBE, CO2 CO3, LEE, 1 }, + { "ё", LBE, CO2 CO3, LEEE, 1 }, + { "ж", LBX, CXL "q 15 0 55 -50 " CDN2 "q 40 -50 55 -50 " CC, LEX }, + { "з", LBX, CO1B "q -3 7.5 -19 15 q 18 -7.5 1 35 ", LEQ }, + { "и", LBN, CLLD CDN, LEL }, + { "й", LBN, CLLD CDN, LELY }, + { "к", LBN, CDN "q 48 -70 46 -40 m -46 40 " CKD1, LEK }, + { "л", LBJ, CDN, LEL }, + { "м", LBJ, CDN CLLD CDN, LEL }, + { "н", LBN, CDN CUP "q 30 0 40 -25" CDN, LEL }, + { "о", LBO, CFO2 CO2 CO3, LEO }, + { "п", LBN, CDN CUP CLU, LEL }, + { "р", LBN, "l -30 75 l 30 -75 " CLU, LEL }, + { "с", LBC, CC2, LEX }, + { "т", LBN, CDN CUP CLU CDN CUP CLU, LEL }, + { "у", LBN, CLLD CDN CQD1, LEQ }, + { "ф", LBA, CFO1 CUP CDN CFO3 "l -30 75 m 35 -50 ", LEO }, + { "х", LBX, CXL CO4 CO2B CC, LEX }, + { "ц", LBN, CLLD CDN, LEG }, + { "ч", LBr, CDN, LEL }, + { "ш", LBN, CLLD CDN CLLD CDN, LEL }, + { "щ", LBN, CLLD CDN CLLD CDN, LEG }, + { "ъ", LBr, CDN Cb, LEO }, + { "ы", LBN, Cb0 "q 20 0 28 -20 " CDN, LEL }, + { "ь", LBN, Cb, LEO }, + { "э", LBX, CXL "m 10 -25 l 15 0 m -25 25 ", LEO }, + { "ю", LBN, CDN CUP "c 30 0 40 -25 55 -25 " CFO2 CO2 CO3, LEO }, + { "я", LBR, CDN, LEL }, + + + //{ "А", {""}, C_AL "m -64 60 l 50 0 m 14 -60 " CDN2 CDN, LEL, 3 }, + { "А", {""}, C_AL CDN2 CDN2, L_EA, 3 }, + { "Б", {""}, "m 53 -85 l -24 60 " CQD2 "q 33 -45 48 -45 " C_B3 "m -30 0 " C_T "l 50 0 m -60 100 ", L_EO2, 3 }, + { "В", {""}, C_B, L_EO1, 3 }, + { "Г", {""}, C_J "m -10 0 " C_T "l 60 0 m -90 100 ", L_EO3, 3 }, + { "Д", {""}, C_J1 CO4B "c -15 0 -9 -15 1 -15 c 10 0 14 15 24 15 " C_O4 "q 20 -50 -15 -50 q -25 0 -37 30 m 12 70 ", L_EO1, 3 }, + { "Е", {""}, C_E, LEX, 3 }, + { "Ё", {""}, C_E C_EE, LEX, 3 }, + { "Ж", {""}, C_XL "q 30 0 85 -100 " CDN2 CDN2 "q 55 -100 85 -100 " C_XR, LEX, 3 }, + { "З", {""}, C_3, L_EO1, 3 }, + { "И", {""}, MUP3 C_LU CDN2 C_LLD CDN2 CDN, LEL, 3 }, + { "Й", {""}, MUP3 C_LU CDN2 C_LLD C_YY CDN2 CDN, LEL, 3 }, + { "К", {""}, "m 40 -75 l 25 -25 " CDN CDN2 CQD2 "c 4 -10 76 -90 86 -90 q 2.5 0 1 10 m -56 55 " CO2B CO1B "l -4 10 ", LEL, 3 }, + { "Л", {""}, C_AL CDN2 CDN, LEL, 3 }, + { "М", {""}, C_AL CDN2 CDN C_LLD CDN2 CDN, LEL, 3 }, + { "Н", {""}, "m 40 -75 l 25 -25 " CDN CDN2 CQD2 "c 8 -20 96 -60 102 -80 " CQU2, LEL, 3 }, + { "О", {""}, "m 20 0 " C_O4 C_O1 C_O2 C_O3, L_EO1, 3 }, + { "П", {""}, C_J "m -5 0 " C_T "l 60 0 m -90 100 " C_J1, LEL, 3 }, + { "Р", {""}, C_J "m 18 -70 c -5 -5 7 -30 32 -30 c 35 0 25 30 15 35 m -65 65 ", L_EO2, 3 }, + { "С", {""}, "m 74 -90 " C_XR2 C_XR3, LEX, 3 }, + { "Т", {""}, C_J "m -5 0 " C_T "l 80 0 m -110 100 " C_J1 CDN "m -10 0 " C_J1, LEL, 3 }, + { "У", {""}, MUP3 C_LU CLLD CDN2 CDN C_JJ2, LEO, 3 }, + { "Ф", {""}, "m 60 -75 " CFO1 CUP CDN CFO3 CDN2 C_J2, L_EO2, 3 }, + { "Х", {""}, C_XL C_O4 C_O2B C_XR, LEX, 3 }, + { "Ц", {""}, MUP3 C_LU CDN2 C_LLD CDN2 CDN, LEG, 3 }, + { "Ч", {""}, MUP3 C_LU CLLD CDN2 CDN, LEL, 3 }, + { "Ш", {""}, MUP3 C_LU CDN2 C_LLD CDN2 CDN C_LLD CDN2 CDN, LEL, 3 }, + { "Щ", {""}, MUP3 C_LU CDN2 C_LLD CDN2 CDN C_LLD CDN2 CDN, LEG, 3 }, + + { "Э", {""}, C_XL "m 20 -50 l 20 0 m -40 50 ", L_EO1, 3 }, + { "Ю", {""}, "m 40 -75 l 25 -25 " CDN CDN2 CQD2 "c 8 -20 46 -35 56 -40 " C_O3 C_O4 C_O1 C_O2 C_O3, L_EO1, 3 }, + { "Я", {""}, C_AL0 "c 15 0 55 -50 65 -75 " CFO1 CUP CDN2 CDN, LEL, 3 }, + + { "-", {""}, MDOTSP "m 10 -25 l 40 0 m -10 25 " MDOTSP, {""}, 2 }, + { ".", {""}, MDOTSP CDot MDOTSP, {""}, 2 }, + { ",", {""}, MDOTSP CComma MDOTSP, {""}, 2 }, + { "\"",{""}, "m 40 -100 " CComma "m 10 0 " CComma "m -40 100 " MDOTSP, {""}, 2 }, + { "»", {}, NULL, {}, 0, "\"" }, + { "“", {}, NULL, {}, 0, "\"" }, + { "«", {""}, MDOTSP CComma "m 10 0 " CComma MDOTSP, {""}, 2 }, + { "„", {}, NULL, {}, 0, "«" }, + + { "!", {""}, MDOTSP "m 40 -100 l -34 85 m -6 15 " CDot MDOTSP, {""}, 2 }, + { "?", {""}, MDOTSP "m 15 -75 " CO2B CO1B "c -10 25 -29 35 -39 60 m -6 15 " CDot MDOTSP, {""}, 2 }, +}; + +char path[1024*1024]; +double pathX0 = 60, pathRowStep = 175, pathWidth = 5; +char *pathEnd; +double pathX, pathY; +FILE *pathFile; +int pathMode; + + +void putPath(const char *str) { + if (!str || !*str) return; + if (!*path) { + char buf[256] = {}; + sprintf(buf, "M %g %g ", pathX, pathY); + char *c = buf; + while(*c) *pathEnd++ = *c++; + } + spTrack(str, &pathX, &pathY); + while(*str) *pathEnd++ = *str++; + *pathEnd = 0; +} + + +void splitPath(int mode) { + if (mode >= 2) { + if (pathMode >= 2) { + if (*path) svgAddPath(pathFile, path, pathWidth, TRUE); + *(pathEnd = path) = 0; + } + } else + if (mode >= 1) { + pathX = pathX0; + pathY += pathRowStep; + if (pathMode >= 1) { + if (*path) svgAddPath(pathFile, path, pathWidth, TRUE); + *(pathEnd = path) = 0; + } else + if (*path) { + char buf[256] = {}; + sprintf(buf, "M %g %g ", pathX, pathY); + putPath(buf); + } + } else { + if (*path) svgAddPath(pathFile, path, pathWidth, TRUE); + //*(pathEnd = path) = 0; + } +} + + +void textToPath(const char *text) { + pathEnd = path; + *pathEnd = 0; + + int cnt = sizeof(letters)/sizeof(*letters); + Letter *prev = NULL; + while(*text) { + Letter *curr = NULL; + do { + for(int i = 0; i < cnt; ++i) { + Letter *l = &letters[i]; + const char *t = curr ? curr->alias : text; + const char *ln = l->name; + while(*t && *ln && *t == *ln) ++t, ++ln; + if (!*ln) { + if (!curr) text = t; + curr = l; + break; + } + } + } while(curr && curr->alias); + + + if (prev) { + const char *link = curr && curr->linkMode == 1 ? prev->end.link1 : prev->end.link0; + putPath(curr && curr->linkMode != 2 && curr->linkMode != 3 && link ? link : prev->end.base); + } + + splitPath(2); + + if (curr) { + const char *link = curr->linkMode == 1 ? curr->begin.link1 : curr->begin.link0; + putPath(prev && prev->linkMode != 2 && link ? link : curr->begin.base); + putPath(curr->mid); + } else + if (*text == '\n') { + splitPath(1); + ++text; + } else { + putPath(MSPACE); + ++text; + } + + prev = curr; + } + if (prev) putPath(prev->end.base); + splitPath(0); +} + + +int textFileToSVG(const char *textfile, const char *svgfile) { + FILE *sf = fopen(textfile, "r"); + if (!sf) return FALSE; + char buf[1024*1024] = {}; + char *str = buf; + while(TRUE) { + int c = fgetc(sf); + if (c <= 0) break; + *str++ = c; + } + fclose(sf); + + pathX = pathX0; + pathY = pathRowStep; + pathEnd = path; + pathFile = svgBegin(svgfile, 300, 300, 10); + if (!pathFile) return FALSE; + + textToPath(buf); + svgEnd(pathFile); + + pathX = pathX0; + pathY = pathRowStep; + pathEnd = path; + pathFile = NULL; + + return TRUE; +} + + +void init() { + pathMode = 2; + pathRowStep = 200; + textFileToSVG("data/writing.txt", "data/output/writing.svg"); + + pathRowStep = 175; + pathMode = 0; + pathX = pathX0; + pathY = pathRowStep; + pathEnd = path; + pathFile = svgBegin("data/output/generated-writing.svg", 300, 300, 10); + + textToPath( + "ааа ббб ввв ггг ддд еее ёёё\n" + "жжж ззз иии ййй ккк ллл ммм\n" + "ннн ооо ппп ррр ссс ттт ууу\n" + "ффф ххх ццц ччч шшш щщщ ъъъ\n" + "ыыы ььь эээ ююю яяя\n" + "ае бе де ке хе це ье еа\n" + "ААа ББб ВВв ГГг ДДд ЕЕе\n" + "ЁЁё ЖЖж ЗЗз ИИи ЙЙй ККк\n" + "ЛЛл ММм ННн ООо ППп РРр\n" + "ССс ТТт УУу ФФф ХХх ЦЦц\n" + "ЧЧч ШШш ЩЩщ ЪЪъ ЫЫы ЬЬь\n" + "ЭЭэ ЮЮю ЯЯя\n" + "Ае, Бе. Ге? Же! «Ие - Ке» \"Це\" „Ье“ т-т\n" ); + + svgEnd(pathFile); + pathFile = NULL; +} + + +void draw() { + saveState(); + zoom(0.4); + //translate(150, 150); + + stroke(COLOR_BLACK); + strokeWidth(5); + noFill(); + + spMoveTo(0, 0); + spParse(path); + restoreState(); +} + + +int main() { + windowSetVariableFrameRate(); + windowSetResizable(TRUE); + windowSetSize(1250, 800); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); + return 0; +} +