-
Notifications
You must be signed in to change notification settings - Fork 21
/
Yutils.lua
3539 lines (3526 loc) · 133 KB
/
Yutils.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
--[[
Copyright (c) 2014, Christoph "Youka" Spanknebel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------------------------------------------
Version: 14th November 2014, 15:45 (GMT+1)
Yutils
table
copy(t[, depth]) -> table
tostring(t) -> string
utf8
charrange(s, i) -> number
chars(s) -> function
len(s) -> number
math
arc_curve(x, y, cx, cy, angle) -> 8, 16, 24 or 32 numbers
bezier(pct, pts) -> number, number, number
create_matrix() -> table
get_data() -> table
set_data(matrix) -> table
identity() -> table
multiply(matrix2) -> table
translate(x, y, z) -> table
scale(x, y, z) -> table
rotate(axis, angle) -> table
inverse() -> [table]
transform(x, y, z[, w]) -> number, number, number, number
degree(x1, y1, z1, x2, y2, z2) -> number
distance(x, y[, z]) -> number
line_intersect(x0, y0, x1, y1, x2, y2, x3, y3, strict) -> number, number|nil|inf
ortho(x1, y1, z1, x2, y2, z2) -> number, number, number
randomsteps(min, max, step) -> number
round(x[, dec]) -> number
stretch(x, y, z, length) -> number, number, number
trim(x, min, max) -> number
algorithm
frames(starts, ends, dur) -> function
lines(text) -> function
shape
bounding(shape) -> number, number, number, number
detect(width, height, data[, compare_func]) -> table
filter(shape, filter) -> string
flatten(shape) -> string
glue(src_shape, dst_shape[, transform_callback]) -> string
move(shape, x, y) -> string
split(shape, max_len) -> string
to_outline(shape, width_xy[, width_y][, mode]) -> string
to_pixels(shape) -> table
transform(shape, matrix) -> string
ass
convert_time(ass_ms) -> number|string
convert_coloralpha(ass_r_a[, g, b[, a] ]) -> 1,3,4 numbers|string
interpolate_coloralpha(pct, ...) -> string
create_parser([ass_text]) -> table
parse_line(line) -> boolean
meta() -> table
styles() -> table
dialogs([extended]) -> table
decode
create_bmp_reader(filename) -> table
file_size() -> number
width() -> number
height() -> number
bit_depth() -> number
data_size() -> number
row_size() -> number
bottom_up() -> boolean
data_raw() -> string
data_packed() -> table
data_text() -> string
create_wav_reader(filename) -> table
file_size() -> number
channels_number() -> number
sample_rate() -> number
byte_rate() -> number
block_align() -> number
bits_per_sample() -> number
samples_per_channel() -> number
min_max_amplitude() -> number, number
sample_from_ms(ms) -> number
ms_from_sample(sample) -> number
position([pos]) -> number
samples_interlaced(n) -> table
samples(n) -> table
create_frequency_analyzer(samples, sample_rate) -> table
frequencies() -> table
frequency_weight(freq) -> number
frequency_range_weight(freq_min, freq_max) -> number
create_font(family, bold, italic, underline, strikeout, size[, xscale][, yscale][, hspace]) -> table
metrics() -> table
text_extents(text) -> table
text_to_shape(text) -> string
list_fonts([with_filenames]) -> table
]]
-- Configuration
local FP_PRECISION = 3 -- Floating point precision by numbers behind point (for shape points)
local CURVE_TOLERANCE = 1 -- Angle in degree to define a curve as flat
local MAX_CIRCUMFERENCE = 1.5 -- Circumference step size to create round edges out of lines
local MITER_LIMIT = 200 -- Maximal length of a miter join
local SUPERSAMPLING = 8 -- Anti-aliasing precision for shape to pixels conversion
local FONT_PRECISION = 64 -- Font scale for better precision output from native font system
local LIBASS_FONTHACK = true -- Scale font data to fontsize? (no effect on windows)
local LIBPNG_PATH = "libpng" -- libpng dynamic library location or shortcut (for system library loading function)
--local LIBPNG_PATH = "C:\\Users\\victo\\Desktop\\Kara Effector 3.6\\lpng\\pngrutil.c"
--local LIBPNG_PATH = include( "png" )
-- Load FFI interface
local ffi = require("ffi")
-- Check OS & load fitting system libraries
local advapi, pangocairo, fontconfig
if ffi.os == "Windows" then
-- WinGDI already loaded in C namespace by default
-- Load advanced winapi library
advapi = ffi.load("Advapi32")
-- Set C definitions for WinAPI
ffi.cdef([[
enum{CP_UTF8 = 65001};
enum{MM_TEXT = 1};
enum{TRANSPARENT = 1};
enum{
FW_NORMAL = 400,
FW_BOLD = 700
};
enum{DEFAULT_CHARSET = 1};
enum{OUT_TT_PRECIS = 4};
enum{CLIP_DEFAULT_PRECIS = 0};
enum{ANTIALIASED_QUALITY = 4};
enum{DEFAULT_PITCH = 0x0};
enum{FF_DONTCARE = 0x0};
enum{
PT_MOVETO = 0x6,
PT_LINETO = 0x2,
PT_BEZIERTO = 0x4,
PT_CLOSEFIGURE = 0x1
};
typedef unsigned int UINT;
typedef unsigned long DWORD;
typedef DWORD* LPDWORD;
typedef const char* LPCSTR;
typedef const wchar_t* LPCWSTR;
typedef wchar_t* LPWSTR;
typedef char* LPSTR;
typedef void* HANDLE;
typedef HANDLE HDC;
typedef int BOOL;
typedef BOOL* LPBOOL;
typedef unsigned int size_t;
typedef HANDLE HFONT;
typedef HANDLE HGDIOBJ;
typedef long LONG;
typedef wchar_t WCHAR;
typedef unsigned char BYTE;
typedef BYTE* LPBYTE;
typedef int INT;
typedef long LPARAM;
static const int LF_FACESIZE = 32;
static const int LF_FULLFACESIZE = 64;
typedef struct{
LONG tmHeight;
LONG tmAscent;
LONG tmDescent;
LONG tmInternalLeading;
LONG tmExternalLeading;
LONG tmAveCharWidth;
LONG tmMaxCharWidth;
LONG tmWeight;
LONG tmOverhang;
LONG tmDigitizedAspectX;
LONG tmDigitizedAspectY;
WCHAR tmFirstChar;
WCHAR tmLastChar;
WCHAR tmDefaultChar;
WCHAR tmBreakChar;
BYTE tmItalic;
BYTE tmUnderlined;
BYTE tmStruckOut;
BYTE tmPitchAndFamily;
BYTE tmCharSet;
}TEXTMETRICW, *LPTEXTMETRICW;
typedef struct{
LONG cx;
LONG cy;
}SIZE, *LPSIZE;
typedef struct{
LONG left;
LONG top;
LONG right;
LONG bottom;
}RECT;
typedef const RECT* LPCRECT;
typedef struct{
LONG x;
LONG y;
}POINT, *LPPOINT;
typedef struct{
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
WCHAR lfFaceName[LF_FACESIZE];
}LOGFONTW, *LPLOGFONTW;
typedef struct{
LOGFONTW elfLogFont;
WCHAR elfFullName[LF_FULLFACESIZE];
WCHAR elfStyle[LF_FACESIZE];
WCHAR elfScript[LF_FACESIZE];
}ENUMLOGFONTEXW, *LPENUMLOGFONTEXW;
enum{
FONTTYPE_RASTER = 1,
FONTTYPE_DEVICE = 2,
FONTTYPE_TRUETYPE = 4
};
typedef int (__stdcall *FONTENUMPROC)(const ENUMLOGFONTEXW*, const void*, DWORD, LPARAM);
enum{ERROR_SUCCESS = 0};
typedef HANDLE HKEY;
typedef HKEY* PHKEY;
enum{HKEY_LOCAL_MACHINE = 0x80000002};
typedef enum{KEY_READ = 0x20019}REGSAM;
int MultiByteToWideChar(UINT, DWORD, LPCSTR, int, LPWSTR, int);
int WideCharToMultiByte(UINT, DWORD, LPCWSTR, int, LPSTR, int, LPCSTR, LPBOOL);
HDC CreateCompatibleDC(HDC);
BOOL DeleteDC(HDC);
int SetMapMode(HDC, int);
int SetBkMode(HDC, int);
size_t wcslen(const wchar_t*);
HFONT CreateFontW(int, int, int, int, int, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPCWSTR);
HGDIOBJ SelectObject(HDC, HGDIOBJ);
BOOL DeleteObject(HGDIOBJ);
BOOL GetTextMetricsW(HDC, LPTEXTMETRICW);
BOOL GetTextExtentPoint32W(HDC, LPCWSTR, int, LPSIZE);
BOOL BeginPath(HDC);
BOOL ExtTextOutW(HDC, int, int, UINT, LPCRECT, LPCWSTR, UINT, const INT*);
BOOL EndPath(HDC);
int GetPath(HDC, LPPOINT, LPBYTE, int);
BOOL AbortPath(HDC);
int EnumFontFamiliesExW(HDC, LPLOGFONTW, FONTENUMPROC, LPARAM, DWORD);
LONG RegOpenKeyExA(HKEY, LPCSTR, DWORD, REGSAM, PHKEY);
LONG RegCloseKey(HKEY);
LONG RegEnumValueW(HKEY, DWORD, LPWSTR, LPDWORD, LPDWORD, LPDWORD, LPBYTE, LPDWORD);
]])
else -- Unix
-- Attempt to load pangocairo library
pcall(function()
pangocairo = ffi.load("pangocairo-1.0.so") -- Extension must be appended because of dot already in filename
-- Set C definitions for pangocairo
ffi.cdef([[
typedef enum{
CAIRO_FORMAT_INVALID = -1,
CAIRO_FORMAT_ARGB32 = 0,
CAIRO_FORMAT_RGB24 = 1,
CAIRO_FORMAT_A8 = 2,
CAIRO_FORMAT_A1 = 3,
CAIRO_FORMAT_RGB16_565 = 4,
CAIRO_FORMAT_RGB30 = 5
}cairo_format_t;
typedef void cairo_surface_t;
typedef void cairo_t;
typedef void PangoLayout;
typedef void* gpointer;
static const int PANGO_SCALE = 1024;
typedef void PangoFontDescription;
typedef enum{
PANGO_WEIGHT_THIN = 100,
PANGO_WEIGHT_ULTRALIGHT = 200,
PANGO_WEIGHT_LIGHT = 300,
PANGO_WEIGHT_NORMAL = 400,
PANGO_WEIGHT_MEDIUM = 500,
PANGO_WEIGHT_SEMIBOLD = 600,
PANGO_WEIGHT_BOLD = 700,
PANGO_WEIGHT_ULTRABOLD = 800,
PANGO_WEIGHT_HEAVY = 900,
PANGO_WEIGHT_ULTRAHEAVY = 1000
}PangoWeight;
typedef enum{
PANGO_STYLE_NORMAL,
PANGO_STYLE_OBLIQUE,
PANGO_STYLE_ITALIC
}PangoStyle;
typedef void PangoAttrList;
typedef void PangoAttribute;
typedef enum{
PANGO_UNDERLINE_NONE,
PANGO_UNDERLINE_SINGLE,
PANGO_UNDERLINE_DOUBLE,
PANGO_UNDERLINE_LOW,
PANGO_UNDERLINE_ERROR
}PangoUnderline;
typedef int gint;
typedef gint gboolean;
typedef void PangoContext;
typedef unsigned int guint;
typedef struct{
guint ref_count;
int ascent;
int descent;
int approximate_char_width;
int approximate_digit_width;
int underline_position;
int underline_thickness;
int strikethrough_position;
int strikethrough_thickness;
}PangoFontMetrics;
typedef void PangoLanguage;
typedef struct{
int x;
int y;
int width;
int height;
}PangoRectangle;
typedef enum{
CAIRO_STATUS_SUCCESS = 0
}cairo_status_t;
typedef enum{
CAIRO_PATH_MOVE_TO,
CAIRO_PATH_LINE_TO,
CAIRO_PATH_CURVE_TO,
CAIRO_PATH_CLOSE_PATH
}cairo_path_data_type_t;
typedef union{
struct{
cairo_path_data_type_t type;
int length;
}header;
struct{
double x, y;
}point;
}cairo_path_data_t;
typedef struct{
cairo_status_t status;
cairo_path_data_t* data;
int num_data;
}cairo_path_t;
cairo_surface_t* cairo_image_surface_create(cairo_format_t, int, int);
void cairo_surface_destroy(cairo_surface_t*);
cairo_t* cairo_create(cairo_surface_t*);
void cairo_destroy(cairo_t*);
PangoLayout* pango_cairo_create_layout(cairo_t*);
void g_object_unref(gpointer);
PangoFontDescription* pango_font_description_new(void);
void pango_font_description_free(PangoFontDescription*);
void pango_font_description_set_family(PangoFontDescription*, const char*);
void pango_font_description_set_weight(PangoFontDescription*, PangoWeight);
void pango_font_description_set_style(PangoFontDescription*, PangoStyle);
void pango_font_description_set_absolute_size(PangoFontDescription*, double);
void pango_layout_set_font_description(PangoLayout*, PangoFontDescription*);
PangoAttrList* pango_attr_list_new(void);
void pango_attr_list_unref(PangoAttrList*);
void pango_attr_list_insert(PangoAttrList*, PangoAttribute*);
PangoAttribute* pango_attr_underline_new(PangoUnderline);
PangoAttribute* pango_attr_strikethrough_new(gboolean);
PangoAttribute* pango_attr_letter_spacing_new(int);
void pango_layout_set_attributes(PangoLayout*, PangoAttrList*);
PangoContext* pango_layout_get_context(PangoLayout*);
const PangoFontDescription* pango_layout_get_font_description(PangoLayout*);
PangoFontMetrics* pango_context_get_metrics(PangoContext*, const PangoFontDescription*, PangoLanguage*);
void pango_font_metrics_unref(PangoFontMetrics*);
int pango_font_metrics_get_ascent(PangoFontMetrics*);
int pango_font_metrics_get_descent(PangoFontMetrics*);
int pango_layout_get_spacing(PangoLayout*);
void pango_layout_set_text(PangoLayout*, const char*, int);
void pango_layout_get_pixel_extents(PangoLayout*, PangoRectangle*, PangoRectangle*);
void cairo_save(cairo_t*);
void cairo_restore(cairo_t*);
void cairo_scale(cairo_t*, double, double);
void pango_cairo_layout_path(cairo_t*, PangoLayout*);
void cairo_new_path(cairo_t*);
cairo_path_t* cairo_copy_path(cairo_t*);
void cairo_path_destroy(cairo_path_t*);
]])
end)
-- Attempt to load fontconfig library
pcall(function()
fontconfig = ffi.load("fontconfig")
-- Set C definitions for fontconfig
ffi.cdef([[
typedef void FcConfig;
typedef void FcPattern;
typedef struct{
int nobject;
int sobject;
const char** objects;
}FcObjectSet;
typedef struct{
int nfont;
int sfont;
FcPattern** fonts;
}FcFontSet;
typedef enum{
FcResultMatch,
FcResultNoMatch,
FcResultTypeMismatch,
FcResultNoId,
FcResultOutOfMemory
}FcResult;
typedef unsigned char FcChar8;
typedef int FcBool;
FcConfig* FcInitLoadConfigAndFonts(void);
FcPattern* FcPatternCreate(void);
void FcPatternDestroy(FcPattern*);
FcObjectSet* FcObjectSetBuild(const char*, ...);
void FcObjectSetDestroy(FcObjectSet*);
FcFontSet* FcFontList(FcConfig*, FcPattern*, FcObjectSet*);
void FcFontSetDestroy(FcFontSet*);
FcResult FcPatternGetString(FcPattern*, const char*, int, FcChar8**);
FcResult FcPatternGetBool(FcPattern*, const char*, int, FcBool*);
]])
end)
end
-- Load PNG decode library (at least try it)
local libpng
pcall(function()
libpng = ffi.load(LIBPNG_PATH)
-- Set C definitions for libpng
ffi.cdef([[
static const int PNG_SIGNATURE_SIZE = 8;
typedef unsigned char png_byte;
typedef png_byte* png_bytep;
typedef const png_bytep png_const_bytep;
typedef unsigned int png_size_t;
typedef char png_char;
typedef png_char* png_charp;
typedef const png_charp png_const_charp;
typedef void png_void;
typedef png_void* png_voidp;
typedef struct png_struct* png_structp;
typedef const png_structp png_const_structp;
typedef struct png_info* png_infop;
typedef const png_infop png_const_infop;
typedef unsigned int png_uint_32;
typedef void (__cdecl *png_error_ptr)(png_structp, png_const_charp);
typedef void (__cdecl *png_rw_ptr)(png_structp, png_bytep, png_size_t);
enum{
PNG_TRANSFORM_STRIP_16 = 0x1,
PNG_TRANSFORM_PACKING = 0x4,
PNG_TRANSFORM_EXPAND = 0x10,
PNG_TRANSFORM_BGR = 0x80
};
enum{
PNG_COLOR_MASK_COLOR = 2,
PNG_COLOR_MASK_ALPHA = 4
};
enum{
PNG_COLOR_TYPE_RGB = PNG_COLOR_MASK_COLOR,
PNG_COLOR_TYPE_RGBA = PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA
};
void* memcpy(void*, const void*, size_t);
int png_sig_cmp(png_const_bytep, png_size_t, png_size_t);
png_structp png_create_read_struct(png_const_charp, png_voidp, png_error_ptr, png_error_ptr);
void png_destroy_read_struct(png_structp*, png_infop*, png_infop*);
png_infop png_create_info_struct(png_structp);
void png_set_read_fn(png_structp, png_voidp, png_rw_ptr);
void png_read_png(png_structp, png_infop, int, png_voidp);
int png_set_interlace_handling(png_structp);
void png_read_update_info(png_structp, png_infop);
png_uint_32 png_get_image_width(png_const_structp, png_const_infop);
png_uint_32 png_get_image_height(png_const_structp, png_const_infop);
png_byte png_get_color_type(png_const_structp, png_const_infop);
png_size_t png_get_rowbytes(png_const_structp, png_const_infop);
png_bytep* png_get_rows(png_const_structp, png_const_infop);
]])
end)
-- Helper functions
local unpack = table.unpack or unpack
local function rotate2d(x, y, angle)
local ra = math.rad(angle)
return math.cos(ra)*x - math.sin(ra)*y,
math.sin(ra)*x + math.cos(ra)*y
end
local function bton(s)
-- Get numeric presentation (=byte) of string characters
local bytes, n = {s:byte(1,-1)}, 0
-- Combine bytes to unsigned integer number
for i = 0, #s-1 do
n = n + bytes[1+i] * 256^i
end
return n
end
local function utf8_to_utf16(s)
-- Get resulting utf16 characters number (+ null-termination)
local wlen = ffi.C.MultiByteToWideChar(ffi.C.CP_UTF8, 0x0, s, -1, nil, 0)
-- Allocate array for utf16 characters storage
local ws = ffi.new("wchar_t[?]", wlen)
-- Convert utf8 string to utf16 characters
ffi.C.MultiByteToWideChar(ffi.C.CP_UTF8, 0x0, s, -1, ws, wlen)
-- Return utf16 C string
return ws
end
local function utf16_to_utf8(ws)
-- Get resulting utf8 characters number (+ null-termination)
local slen = ffi.C.WideCharToMultiByte(ffi.C.CP_UTF8, 0x0, ws, -1, nil, 0, nil, nil)
-- Allocate array for utf8 characters storage
local s = ffi.new("char[?]", slen)
-- Convert utf16 string to utf8 characters
ffi.C.WideCharToMultiByte(ffi.C.CP_UTF8, 0x0, ws, -1, s, slen, nil, nil)
-- Return utf8 Lua string
return ffi.string(s)
end
-- Create library table
local Yutils
Yutils = {
-- Table sublibrary
table = {
demo = function( s )
return utf8_to_utf16( s )
end,
-- Copies table deep
copy = function(t, depth)
-- Check argument
if type(t) ~= "table" or depth ~= nil and not(type(depth) == "number" and depth >= 1) then
error("table and optional depth expected", 2)
end
-- Copy & return
local function copy_recursive(old_t)
local new_t = {}
for key, value in pairs(old_t) do
new_t[key] = type(value) == "table" and copy_recursive(value) or value
end
return new_t
end
local function copy_recursive_n(old_t, depth)
local new_t = {}
for key, value in pairs(old_t) do
new_t[key] = type(value) == "table" and depth >= 2 and copy_recursive_n(value, depth-1) or value
end
return new_t
end
return depth and copy_recursive_n(t, depth) or copy_recursive(t)
end,
-- Converts table to string
tostring = function(t)
-- Check argument
if type(t) ~= "table" then
error("table expected", 2)
end
-- Result storage
local result, result_n = {}, 0
-- Convert to string!
local function convert_recursive(t, space)
for key, value in pairs(t) do
if type(key) == "string" then
key = string.format("%q", key)
end
if type(value) == "string" then
value = string.format("%q", value)
end
result_n = result_n + 1
result[result_n] = string.format("%s[%s] = %s", space, key, value)
if type(value) == "table" then
convert_recursive(value, space .. "\t")
end
end
end
convert_recursive(t, "")
-- Return result as string
return table.concat(result, "\n")
end
},
-- UTF8 sublibrary
utf8 = {
--[[
UTF32 -> UTF8
--------------
U-00000000 -
U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
]]
-- UTF8 character range at string codepoint
charrange = function(s, i)
-- Check arguments
if type(s) ~= "string" or type(i) ~= "number" or i < 1 or i > #s then
error("string and string index expected", 2)
end
-- Evaluate codepoint to range
local byte = s:byte(i)
return not byte and 0 or
byte < 192 and 1 or
byte < 224 and 2 or
byte < 240 and 3 or
byte < 248 and 4 or
byte < 252 and 5 or
6
end,
-- Creates iterator through UTF8 characters
chars = function(s)
-- Check argument
if type(s) ~= "string" then
error("string expected", 2)
end
-- Return utf8 characters iterator
local char_i, s_pos, s_len = 0, 1, #s
return function()
if s_pos <= s_len then
local cur_pos = s_pos
s_pos = s_pos + Yutils.utf8.charrange(s, s_pos)
if s_pos-1 <= s_len then
char_i = char_i + 1
return char_i, s:sub(cur_pos, s_pos-1)
end
end
end
end,
-- Get UTF8 characters number in string
len = function(s)
-- Check argument
if type(s) ~= "string" then
error("string expected", 2)
end
-- Count UTF8 characters
local n = 0
for _ in Yutils.utf8.chars(s) do
n = n + 1
end
return n
end
},
-- Math sublibrary
math = {
-- Converts an arc to 1-4 cubic bezier curve(s)
arc_curve = function(x, y, cx, cy, angle)
-- Check arguments
if type(x) ~= "number" or type(y) ~= "number" or type(cx) ~= "number" or type(cy) ~= "number" or type(angle) ~= "number" or
angle < -360 or angle > 360 then
error("start & center point and valid angle (-360<=x<=360) expected", 2)
end
-- Something to do?
if angle ~= 0 then
-- Factor for bezier control points distance to node points
local kappa = 4 * (math.sqrt(2) - 1) / 3
-- Relative points to center
local rx0, ry0, rx1, ry1, rx2, ry2, rx3, ry3, rx03, ry03 = x - cx, y - cy
-- Define arc clock direction & set angle to positive range
local cw = angle > 0 and 1 or -1
if angle < 0 then
angle = -angle
end
-- Create curves in 90 degree chunks
local curves, curves_n, angle_sum, cur_angle_pct = {}, 0, 0
repeat
-- Get arc end point
cur_angle_pct = math.min(angle - angle_sum, 90) / 90
rx3, ry3 = rotate2d(rx0, ry0, cw * 90 * cur_angle_pct)
-- Get arc start to end vector
rx03, ry03 = rx3 - rx0, ry3 - ry0
-- Scale arc vector to curve node <-> control point distance
rx03, ry03 = Yutils.math.stretch(rx03, ry03, 0, math.sqrt(Yutils.math.distance(rx03, ry03)^2/2) * kappa)
-- Get curve control points
rx1, ry1 = rotate2d(rx03, ry03, cw * -45 * cur_angle_pct)
rx1, ry1 = rx0 + rx1, ry0 + ry1
rx2, ry2 = rotate2d(-rx03, -ry03, cw * 45 * cur_angle_pct)
rx2, ry2 = rx3 + rx2, ry3 + ry2
-- Insert curve to output
curves[curves_n+1], curves[curves_n+2], curves[curves_n+3], curves[curves_n+4],
curves[curves_n+5], curves[curves_n+6], curves[curves_n+7], curves[curves_n+8] =
cx + rx0, cy + ry0, cx + rx1, cy + ry1, cx + rx2, cy + ry2, cx + rx3, cy + ry3
curves_n = curves_n + 8
-- Prepare next curve
rx0, ry0 = rx3, ry3
angle_sum = angle_sum + 90
until angle_sum >= angle
-- Return curve points as tuple
return unpack(curves)
end
end,
-- Get point on n-degree bezier curve
bezier = function(pct, pts)
-- Check arguments
if type(pct) ~= "number" or pct < 0 or pct > 1 or type(pts) ~= "table" then
error("percent number and points table expected", 2)
end
local pts_n = #pts
if pts_n < 2 then
error("at least 2 points expected", 2)
end
for _, value in ipairs(pts) do
if type(value[1]) ~= "number" or type(value[2]) ~= "number" or (value[3] ~= nil and type(value[3]) ~= "number") then
error("points have to be tables with 2 or 3 numbers", 2)
end
end
-- Pick a fitting fast calculation
local pct_inv = 1 - pct
if pts_n == 2 then -- Linear curve
return pct_inv * pts[1][1] + pct * pts[2][1],
pct_inv * pts[1][2] + pct * pts[2][2],
pts[1][3] and pts[2][3] and pct_inv * pts[1][3] + pct * pts[2][3] or 0
elseif pts_n == 3 then -- Quadratic curve
return pct_inv * pct_inv * pts[1][1] + 2 * pct_inv * pct * pts[2][1] + pct * pct * pts[3][1],
pct_inv * pct_inv * pts[1][2] + 2 * pct_inv * pct * pts[2][2] + pct * pct * pts[3][2],
pts[1][3] and pts[2][3] and pct_inv * pct_inv * pts[1][3] + 2 * pct_inv * pct * pts[2][3] + pct * pct * pts[3][3] or 0
elseif pts_n == 4 then -- Cubic curve
return pct_inv * pct_inv * pct_inv * pts[1][1] + 3 * pct_inv * pct_inv * pct * pts[2][1] + 3 * pct_inv * pct * pct * pts[3][1] + pct * pct * pct * pts[4][1],
pct_inv * pct_inv * pct_inv * pts[1][2] + 3 * pct_inv * pct_inv * pct * pts[2][2] + 3 * pct_inv * pct * pct * pts[3][2] + pct * pct * pct * pts[4][2],
pts[1][3] and pts[2][3] and pts[3][3] and pts[4][3] and pct_inv * pct_inv * pct_inv * pts[1][3] + 3 * pct_inv * pct_inv * pct * pts[2][3] + 3 * pct_inv * pct * pct * pts[3][3] + pct * pct * pct * pts[4][3] or 0
else -- pts_n > 4
-- Factorial
local function fac(n)
local k = 1
for i=2, n do
k = k * i
end
return k
end
-- Calculate coordinate
local ret_x, ret_y, ret_z = 0, 0, 0
local n, bern, pt = pts_n - 1
for i=0, n do
pt = pts[1+i]
-- Bernstein polynom
bern = fac(n) / (fac(i) * fac(n - i)) * --Binomial coefficient
pct^i * pct_inv^(n - i)
ret_x = ret_x + pt[1] * bern
ret_y = ret_y + pt[2] * bern
ret_z = ret_z + (pt[3] or 0) * bern
end
return ret_x, ret_y, ret_z
end
end,
-- Creates 3d matrix
create_matrix = function()
-- Matrix data
local matrix = {1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1}
-- Matrix object
local obj
obj = {
-- Get matrix data
get_data = function()
return Yutils.table.copy(matrix)
end,
-- Set matrix data
set_data = function(new_matrix)
-- Check arguments
if type(new_matrix) ~= "table" or #new_matrix ~= 16 then
error("4x4 matrix expected", 2)
end
for _, value in ipairs(new_matrix) do
if type(value) ~= "number" then
error("matrix must contain only numbers", 2)
end
end
-- Replace old matrix
matrix = Yutils.table.copy(new_matrix)
-- Return this object
return obj
end,
-- Set matrix to identity
identity = function()
-- Set matrix to default / no transformation
matrix[1] = 1
matrix[2] = 0
matrix[3] = 0
matrix[4] = 0
matrix[5] = 0
matrix[6] = 1
matrix[7] = 0
matrix[8] = 0
matrix[9] = 0
matrix[10] = 0
matrix[11] = 1
matrix[12] = 0
matrix[13] = 0
matrix[14] = 0
matrix[15] = 0
matrix[16] = 1
-- Return this object
return obj
end,
-- Multiplies matrix with given one
multiply = function(matrix2)
-- Check arguments
if type(matrix2) ~= "table" or #matrix2 ~= 16 then
error("4x4 matrix expected", 2)
end
for _, value in ipairs(matrix2) do
if type(value) ~= "number" then
error("matrix must contain only numbers", 2)
end
end
-- Multipy matrices to create new one
local new_matrix = {0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0}
for i=1, 16 do
for j=0, 3 do
new_matrix[i] = new_matrix[i] + matrix[1 + (i-1) % 4 + j * 4] * matrix2[1 + math.floor((i-1) / 4) * 4 + j]
end
end
-- Replace old matrix with multiply result
matrix = new_matrix
-- Return this object
return obj
end,
-- Applies translation to matrix
translate = function(x, y, z)
-- Check arguments
if type(x) ~= "number" or type(y) ~= "number" or type(z) ~= "number" then
error("3 translation values expected", 2)
end
-- Add translation to matrix
obj.multiply({1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
x, y, z, 1})
-- Return this object
return obj
end,
-- Applies scale to matrix
scale = function(x, y, z)
-- Check arguments
if type(x) ~= "number" or type(y) ~= "number" or type(z) ~= "number" then
error("3 scale factors expected", 2)
end
-- Add scale to matrix
obj.multiply({x, 0, 0, 0,
0, y, 0, 0,
0, 0, z, 0,
0, 0, 0, 1})
-- Return this object
return obj
end,
-- Applies rotation to matrix
rotate = function(axis, angle)
-- Check arguments
if (axis ~= "x" and axis ~= "y" and axis ~= "z") or type(angle) ~= "number" then
error("axis (as string) and angle (in degree) expected", 2)
end
-- Convert angle from degree to radian
angle = math.rad(angle)
-- Rotate by axis
if axis == "x" then
obj.multiply({1, 0, 0, 0,
0, math.cos(angle), -math.sin(angle), 0,
0, math.sin(angle), math.cos(angle), 0,
0, 0, 0, 1})
elseif axis == "y" then
obj.multiply({math.cos(angle), 0, math.sin(angle), 0,
0, 1, 0, 0,
-math.sin(angle), 0, math.cos(angle), 0,
0, 0, 0, 1})
else -- axis == "z"
obj.multiply({math.cos(angle), -math.sin(angle), 0, 0,
math.sin(angle), math.cos(angle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1})
end
-- Return this object
return obj
end,
-- Inverses matrix
inverse = function()
-- Create inversion matrix
local inv_matrix = {
matrix[6] * matrix[11] * matrix[16] - matrix[6] * matrix[15] * matrix[12] - matrix[7] * matrix[10] * matrix[16] + matrix[7] * matrix[14] * matrix[12] +matrix[8] * matrix[10] * matrix[15] - matrix[8] * matrix[14] * matrix[11],
-matrix[2] * matrix[11] * matrix[16] + matrix[2] * matrix[15] * matrix[12] + matrix[3] * matrix[10] * matrix[16] - matrix[3] * matrix[14] * matrix[12] - matrix[4] * matrix[10] * matrix[15] + matrix[4] * matrix[14] * matrix[11],
matrix[2] * matrix[7] * matrix[16] - matrix[2] * matrix[15] * matrix[8] - matrix[3] * matrix[6] * matrix[16] + matrix[3] * matrix[14] * matrix[8] + matrix[4] * matrix[6] * matrix[15] - matrix[4] * matrix[14] * matrix[7],
-matrix[2] * matrix[7] * matrix[12] + matrix[2] * matrix[11] * matrix[8] +matrix[3] * matrix[6] * matrix[12] - matrix[3] * matrix[10] * matrix[8] - matrix[4] * matrix[6] * matrix[11] + matrix[4] * matrix[10] * matrix[7],
-matrix[5] * matrix[11] * matrix[16] + matrix[5] * matrix[15] * matrix[12] + matrix[7] * matrix[9] * matrix[16] - matrix[7] * matrix[13] * matrix[12] - matrix[8] * matrix[9] * matrix[15] + matrix[8] * matrix[13] * matrix[11],
matrix[1] * matrix[11] * matrix[16] - matrix[1] * matrix[15] * matrix[12] - matrix[3] * matrix[9] * matrix[16] + matrix[3] * matrix[13] * matrix[12] + matrix[4] * matrix[9] * matrix[15] - matrix[4] * matrix[13] * matrix[11],
-matrix[1] * matrix[7] * matrix[16] + matrix[1] * matrix[15] * matrix[8] + matrix[3] * matrix[5] * matrix[16] - matrix[3] * matrix[13] * matrix[8] - matrix[4] * matrix[5] * matrix[15] + matrix[4] * matrix[13] * matrix[7],
matrix[1] * matrix[7] * matrix[12] - matrix[1] * matrix[11] * matrix[8] - matrix[3] * matrix[5] * matrix[12] + matrix[3] * matrix[9] * matrix[8] + matrix[4] * matrix[5] * matrix[11] - matrix[4] * matrix[9] * matrix[7],
matrix[5] * matrix[10] * matrix[16] - matrix[5] * matrix[14] * matrix[12] - matrix[6] * matrix[9] * matrix[16] + matrix[6] * matrix[13] * matrix[12] + matrix[8] * matrix[9] * matrix[14] - matrix[8] * matrix[13] * matrix[10],
-matrix[1] * matrix[10] * matrix[16] + matrix[1] * matrix[14] * matrix[12] + matrix[2] * matrix[9] * matrix[16] - matrix[2] * matrix[13] * matrix[12] - matrix[4] * matrix[9] * matrix[14] + matrix[4] * matrix[13] * matrix[10],
matrix[1] * matrix[6] * matrix[16] - matrix[1] * matrix[14] * matrix[8] - matrix[2] * matrix[5] * matrix[16] + matrix[2] * matrix[13] * matrix[8] + matrix[4] * matrix[5] * matrix[14] - matrix[4] * matrix[13] * matrix[6],
-matrix[1] * matrix[6] * matrix[12] + matrix[1] * matrix[10] * matrix[8] + matrix[2] * matrix[5] * matrix[12] - matrix[2] * matrix[9] * matrix[8] - matrix[4] * matrix[5] * matrix[10] + matrix[4] * matrix[9] * matrix[6],
-matrix[5] * matrix[10] * matrix[15] + matrix[5] * matrix[14] * matrix[11] + matrix[6] * matrix[9] * matrix[15] - matrix[6] * matrix[13] * matrix[11] - matrix[7] * matrix[9] * matrix[14] + matrix[7] * matrix[13] * matrix[10],
matrix[1] * matrix[10] * matrix[15] - matrix[1] * matrix[14] * matrix[11] - matrix[2] * matrix[9] * matrix[15] + matrix[2] * matrix[13] * matrix[11] + matrix[3] * matrix[9] * matrix[14] - matrix[3] * matrix[13] * matrix[10],
-matrix[1] * matrix[6] * matrix[15] + matrix[1] * matrix[14] * matrix[7] + matrix[2] * matrix[5] * matrix[15] - matrix[2] * matrix[13] * matrix[7] - matrix[3] * matrix[5] * matrix[14] + matrix[3] * matrix[13] * matrix[6],
matrix[1] * matrix[6] * matrix[11] - matrix[1] * matrix[10] * matrix[7] - matrix[2] * matrix[5] * matrix[11] + matrix[2] * matrix[9] * matrix[7] + matrix[3] * matrix[5] * matrix[10] - matrix[3] * matrix[9] * matrix[6]
}
-- Calculate determinant
local det = matrix[1] * inv_matrix[1] +
matrix[5] * inv_matrix[2] +
matrix[9] * inv_matrix[3] +
matrix[13] * inv_matrix[4]
-- Matrix inversion possible?
if det ~= 0 then
-- Invert matrix
det = 1 / det
for i=1, 16 do
matrix[i] = inv_matrix[i] * det
end
-- Return this object
return obj
end
end,
-- Applies matrix to point
transform = function(x, y, z, w)
-- Check arguments
if type(x) ~= "number" or type(y) ~= "number" or type(z) ~= "number" or (w ~= nil and type(w) ~= "number") then
error("point (3 or 4 numbers) expected", 2)
end
-- Set 4th coordinate
if not w then
w = 1
end
-- Calculate new point
return x * matrix[1] + y * matrix[5] + z * matrix[9] + w * matrix[13],
x * matrix[2] + y * matrix[6] + z * matrix[10] + w * matrix[14],
x * matrix[3] + y * matrix[7] + z * matrix[11] + w * matrix[15],
x * matrix[4] + y * matrix[8] + z * matrix[12] + w * matrix[16]
end
}
return obj
end,
-- Degree between two 3d vectors
degree = function(x1, y1, z1, x2, y2, z2)
-- Check arguments
if type(x1) ~= "number" or type(y1) ~= "number" or type(z1) ~= "number" or
type(x2) ~= "number" or type(y2) ~= "number" or type(z2) ~= "number" then
error("2 vectors (as 6 numbers) expected", 2)
end
-- Calculate degree
local degree = math.deg(
math.acos(
(x1 * x2 + y1 * y2 + z1 * z2) /
(Yutils.math.distance(x1, y1, z1) * Yutils.math.distance(x2, y2, z2))
)
)
-- Return with sign by clockwise direction
return (x1*y2 - y1*x2) < 0 and -degree or degree
end,
-- Length of vector
distance = function(x, y, z)
-- Check arguments
if type(x) ~= "number" or type(y) ~= "number" or z ~= nil and type(z) ~= "number" then
error("one vector (2 or 3 numbers) expected", 2)
end
-- Calculate length
return z and math.sqrt(x*x + y*y + z*z) or math.sqrt(x*x + y*y)
end,
line_intersect = function(x0, y0, x1, y1, x2, y2, x3, y3, strict)
-- Check arguments
if type(x0) ~= "number" or type(y0) ~= "number" or type(x1) ~= "number" or type(y1) ~= "number" or
type(x2) ~= "number" or type(y2) ~= "number" or type(x3) ~= "number" or type(y3) ~= "number" or
strict ~= nil and type(strict) ~= "boolean" then
error("two lines and optional strictness flag expected", 2)
end
-- Get line vectors & check valid lengths
local x10, y10, x32, y32 = x0 - x1, y0 - y1, x2 - x3, y2 - y3
if x10 == 0 and y10 == 0 or x32 == 0 and y32 == 0 then
error("lines mustn't have zero length", 2)
end
-- Calculate determinant and check for parallel lines
local det = x10 * y32 - y10 * x32
if det ~= 0 then
-- Calculate line intersection (endless line lengths)
local pre, post = (x0 * y1 - y0 * x1), (x2 * y3 - y2 * x3)
local ix, iy = (pre * x32 - x10 * post) / det, (pre * y32 - y10 * post) / det
-- Check for line intersection with given line lengths
if strict then
local s, t = x10 ~= 0 and (ix - x1) / x10 or (iy - y1) / y10, x32 ~= 0 and (ix - x3) / x32 or (iy - y3) / y32
if s < 0 or s > 1 or t < 0 or t > 1 then
return 1/0 -- inf
end
end
-- Return intersection point
return ix, iy
end
end,
-- Get orthogonal vector of 2 given vectors
ortho = function(x1, y1, z1, x2, y2, z2)
-- Check arguments