diff --git a/docs/xml/modules/classes/metaclass.xml b/docs/xml/modules/classes/metaclass.xml index 1f05747bb..26ba9172f 100644 --- a/docs/xml/modules/classes/metaclass.xml +++ b/docs/xml/modules/classes/metaclass.xml @@ -222,6 +222,17 @@ STRING + + Objects + Returns an allocated list of all objects that belong to this class. + Get + LONG [] + +

This field will compile a list of all objects that belong to the class. The list is sorted with the oldest object appearing first.

+

The resulting array must be terminated with FreeResource after use.

+
+
+ OpenCount The total number of active objects that are linked back to the MetaClass. @@ -242,17 +253,6 @@ - - PrivateObjects - Returns an allocated list of all objects that belong to this class. - Get - LONG [] - -

This field will compile a list of all objects that belong to the class. The list is sorted with the oldest object appearing first.

-

The resulting array must be terminated with FreeResource after use.

-
-
- RootModule Returns a direct reference to the RootModule object that hosts the class. diff --git a/docs/xml/modules/classes/vector.xml b/docs/xml/modules/classes/vector.xml index 1bd44d8d6..643cc89ff 100644 --- a/docs/xml/modules/classes/vector.xml +++ b/docs/xml/modules/classes/vector.xml @@ -245,9 +245,11 @@ TracePath Returns the coordinates for a vector path, using callbacks. - ERR vecTracePath(OBJECTPTR Object, FUNCTION * Callback) + ERR vecTracePath(OBJECTPTR Object, FUNCTION * Callback, DOUBLE Scale, LONG Transform) The function to call with each coordinate of the path. + Set to 1.0 (recommended) to trace the path at a scale of 1 to 1. + Set to TRUE if all transforms applicable to the vector should be applied to the path.

Any vector that generates a path can be traced by calling this method. Tracing allows the caller to follow the path for each pixel that would be drawn if the path were to be rendered with a stroke size of 1. The prototype of the callback function is ERR Function(OBJECTPTR Vector, LONG Index, LONG Command, DOUBLE X, DOUBLE Y, APTR Meta).

@@ -256,6 +258,7 @@
Operation successful. + The vector does not define a path. Function call missing argument value(s)
@@ -563,6 +566,16 @@
+ + PathTimestamp + This counter is modified each time the path is regenerated. + Read + INT + +

The PathTimestamp can be used as a basic means of recording the state of the vector's path, and checking that state for changes at a later time. For more active monitoring and response, clients should subscribe to the PATH_CHANGED event.

+
+
+ Prev The previous vector in the branch, or NULL. diff --git a/docs/xml/modules/core.xml b/docs/xml/modules/core.xml index bb2582295..670d5a049 100644 --- a/docs/xml/modules/core.xml +++ b/docs/xml/modules/core.xml @@ -3449,10 +3449,11 @@ SetField(Object, FID_Statement|TSTR, "string"); Type of the field - + Between 0 and 359.999 Between 0 and 1.0 Between 0 and 1.0. Corresponds to Value, Lightness or Brightness + Alpha blending value from 0 to 1.0. diff --git a/include/parasol/main.h b/include/parasol/main.h index 3262031b6..b588863c0 100644 --- a/include/parasol/main.h +++ b/include/parasol/main.h @@ -63,10 +63,10 @@ class ScopedAccessMemory { // C++ wrapper for automatically releasing shared mem // Defer() function for calling lambdas at end-of-scope template struct deferred_call { - deferred_call(const deferred_call& that) = delete; - deferred_call& operator=(const deferred_call& that) = delete; - deferred_call(deferred_call&& that) = delete; - deferred_call(FUNC&& f) : func(std::forward(f)) { } + deferred_call(const deferred_call &that) = delete; + deferred_call & operator = (const deferred_call &that) = delete; + deferred_call(deferred_call &&that) = delete; + deferred_call(FUNC &&f) : func(std::forward(f)) { } ~deferred_call() { func(); } @@ -74,7 +74,7 @@ template struct deferred_call { FUNC func; }; -template deferred_call Defer(F&& f) { +template deferred_call Defer(F &&f) { return deferred_call(std::forward(f)); } @@ -128,7 +128,7 @@ class ScopedObjectLock { // C++ wrapper for automatically releasing an object //******************************************************************************************************************** // Resource guard for any allocation that can be freed with FreeResource(). Retains the resource ID rather than the -// pointer to ensure that termination is safe even if the original resource gets terminated elsewhere. +// pointer to ensure that termination is safe, even if the original resource gets terminated elsewhere. // // Usage: pf::GuardedResource resource(thing) @@ -145,13 +145,14 @@ class GuardedResource { }; //******************************************************************************************************************** -// The object equivalent of GuardedResource. Also guarantees safety for object termination. +// The object equivalent of GuardedResource. Also guarantees safety for object termination. The use of +// GuardedObject is considered essential for interoperability with the C++ class destruction model. template class GuardedObject { private: C * count; // Count of GuardedObjects accessing the same resource. Can be LONG (non-threaded) or std::atomic_int - T * object; + T * object; // Pointer to the Parasol object being guarded. Use '*' or '->' operators to access. public: OBJECTID id; // Object UID @@ -234,7 +235,7 @@ class GuardedObject { else { pf::Log log(__FUNCTION__); log.warning(ERR::InUse); } } - constexpr bool empty() { return !object; } + constexpr bool empty() { return !object; } // Returns true if no object is being guarded. T * operator->() { return object; }; // Promotes underlying methods and fields T * & operator*() { return object; }; // To allow object pointer referencing when calling functions diff --git a/include/parasol/modules/core.h b/include/parasol/modules/core.h index 6bfb9d11b..6e760a953 100644 --- a/include/parasol/modules/core.h +++ b/include/parasol/modules/core.h @@ -1354,6 +1354,7 @@ struct HSV { DOUBLE Hue; // Between 0 and 359.999 DOUBLE Saturation; // Between 0 and 1.0 DOUBLE Value; // Between 0 and 1.0. Corresponds to Value, Lightness or Brightness + DOUBLE Alpha; // Alpha blending value from 0 to 1.0. }; struct FRGB { @@ -2796,6 +2797,11 @@ struct BaseClass { // Must be 64-bit aligned return obj ? true : false; } + inline ERR setArray(ULONG FieldID, FLOAT *Value, LONG Size) { return SetArray(this, (FIELD)FieldID|TFLOAT, Value, Size); } + inline ERR setArray(ULONG FieldID, DOUBLE *Value, LONG Size) { return SetArray(this, (FIELD)FieldID|TDOUBLE, Value, Size); } + inline ERR setArray(ULONG FieldID, LONG *Value, LONG Size) { return SetArray(this, (FIELD)FieldID|TLONG, Value, Size); } + inline ERR setArray(ULONG FieldID, LARGE *Value, LONG Size) { return SetArray(this, (FIELD)FieldID|TLARGE, Value, Size); } + inline ERR set(ULONG FieldID, int Value) { return SetField(this, (FIELD)FieldID|TLONG, Value); } inline ERR set(ULONG FieldID, unsigned int Value) { return SetField(this, (FIELD)FieldID|TLONG, Value); } inline ERR set(ULONG FieldID, LARGE Value) { return SetField(this, (FIELD)FieldID|TLARGE, Value); } @@ -3359,7 +3365,7 @@ class objMetaClass : public BaseClass { template inline ERR setName(T && Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[9]; + auto field = &this->Class->Dictionary[10]; return field->WriteValue(target, field, 0x08810500, to_cstring(Value), 1); } diff --git a/include/parasol/modules/vector.h b/include/parasol/modules/vector.h index dbaac8700..95f0c77e5 100644 --- a/include/parasol/modules/vector.h +++ b/include/parasol/modules/vector.h @@ -1564,7 +1564,7 @@ class objVectorFilter : public BaseClass { #define MT_VecFreeMatrix -10 struct vecPush { LONG Position; }; -struct vecTracePath { FUNCTION * Callback; }; +struct vecTracePath { FUNCTION * Callback; DOUBLE Scale; LONG Transform; }; struct vecGetBoundary { VBF Flags; DOUBLE X; DOUBLE Y; DOUBLE Width; DOUBLE Height; }; struct vecPointInPath { DOUBLE X; DOUBLE Y; }; struct vecSubscribeInput { JTYPE Mask; FUNCTION * Callback; }; @@ -1578,8 +1578,8 @@ inline ERR vecPush(APTR Ob, LONG Position) noexcept { return(Action(MT_VecPush, (OBJECTPTR)Ob, &args)); } -inline ERR vecTracePath(APTR Ob, FUNCTION * Callback) noexcept { - struct vecTracePath args = { Callback }; +inline ERR vecTracePath(APTR Ob, FUNCTION * Callback, DOUBLE Scale, LONG Transform) noexcept { + struct vecTracePath args = { Callback, Scale, Transform }; return(Action(MT_VecTracePath, (OBJECTPTR)Ob, &args)); } @@ -1652,6 +1652,7 @@ class objVector : public BaseClass { PTC Cursor; // The mouse cursor to display when the pointer is within the vector's boundary. RQ PathQuality; // Defines the quality of a path when it is rendered. VCS ColourSpace; // Defines the colour space to use when blending the vector with a target bitmap's content. + LONG PathTimestamp; // This counter is modified each time the path is regenerated. // Action stubs @@ -1672,37 +1673,37 @@ class objVector : public BaseClass { inline ERR setNext(objVector * Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[27]; + auto field = &this->Class->Dictionary[28]; return field->WriteValue(target, field, 0x08000301, Value, 1); } inline ERR setPrev(objVector * Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[28]; + auto field = &this->Class->Dictionary[29]; return field->WriteValue(target, field, 0x08000301, Value, 1); } inline ERR setStrokeOpacity(const DOUBLE Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[35]; + auto field = &this->Class->Dictionary[36]; return field->WriteValue(target, field, FD_DOUBLE, &Value, 1); } inline ERR setFillOpacity(const DOUBLE Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[41]; + auto field = &this->Class->Dictionary[42]; return field->WriteValue(target, field, FD_DOUBLE, &Value, 1); } inline ERR setOpacity(const DOUBLE Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[20]; + auto field = &this->Class->Dictionary[21]; return field->WriteValue(target, field, FD_DOUBLE, &Value, 1); } inline ERR setMiterLimit(const DOUBLE Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[13]; + auto field = &this->Class->Dictionary[14]; return field->WriteValue(target, field, FD_DOUBLE, &Value, 1); } @@ -1713,7 +1714,7 @@ class objVector : public BaseClass { inline ERR setDashOffset(const DOUBLE Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[19]; + auto field = &this->Class->Dictionary[20]; return field->WriteValue(target, field, FD_DOUBLE, &Value, 1); } @@ -1730,7 +1731,7 @@ class objVector : public BaseClass { inline ERR setCursor(const PTC Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[42]; + auto field = &this->Class->Dictionary[43]; return field->WriteValue(target, field, FD_LONG, &Value, 1); } @@ -1746,19 +1747,19 @@ class objVector : public BaseClass { inline ERR setClipRule(const LONG Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[17]; + auto field = &this->Class->Dictionary[18]; return field->WriteValue(target, field, FD_LONG, &Value, 1); } inline ERR setDashArray(const DOUBLE * Value, LONG Elements) noexcept { auto target = this; - auto field = &this->Class->Dictionary[29]; + auto field = &this->Class->Dictionary[30]; return field->WriteValue(target, field, 0x80001308, Value, Elements); } inline ERR setMask(OBJECTPTR Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[25]; + auto field = &this->Class->Dictionary[26]; return field->WriteValue(target, field, 0x08000309, Value, 1); } @@ -1770,19 +1771,19 @@ class objVector : public BaseClass { inline ERR setAppendPath(OBJECTPTR Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[21]; + auto field = &this->Class->Dictionary[22]; return field->WriteValue(target, field, 0x08000309, Value, 1); } inline ERR setMorphFlags(const LONG Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[16]; + auto field = &this->Class->Dictionary[17]; return field->WriteValue(target, field, FD_LONG, &Value, 1); } inline ERR setNumeric(const LONG Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[34]; + auto field = &this->Class->Dictionary[35]; return field->WriteValue(target, field, FD_LONG, &Value, 1); } @@ -1794,7 +1795,7 @@ class objVector : public BaseClass { inline ERR setResizeEvent(const FUNCTION Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[37]; + auto field = &this->Class->Dictionary[38]; return field->WriteValue(target, field, FD_FUNCTION, &Value, 1); } @@ -1819,61 +1820,61 @@ class objVector : public BaseClass { inline ERR setTransition(OBJECTPTR Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[30]; + auto field = &this->Class->Dictionary[31]; return field->WriteValue(target, field, 0x08000309, Value, 1); } inline ERR setEnableBkgd(const LONG Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[40]; + auto field = &this->Class->Dictionary[41]; return field->WriteValue(target, field, FD_LONG, &Value, 1); } template inline ERR setFill(T && Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[24]; + auto field = &this->Class->Dictionary[25]; return field->WriteValue(target, field, 0x08800308, to_cstring(Value), 1); } inline ERR setFillColour(const FLOAT * Value, LONG Elements) noexcept { auto target = this; - auto field = &this->Class->Dictionary[33]; + auto field = &this->Class->Dictionary[34]; return field->WriteValue(target, field, 0x10001308, Value, Elements); } inline ERR setFillRule(const LONG Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[14]; + auto field = &this->Class->Dictionary[15]; return field->WriteValue(target, field, FD_LONG, &Value, 1); } template inline ERR setFilter(T && Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[43]; + auto field = &this->Class->Dictionary[44]; return field->WriteValue(target, field, 0x08800308, to_cstring(Value), 1); } inline ERR setLineJoin(const LONG Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[36]; + auto field = &this->Class->Dictionary[37]; return field->WriteValue(target, field, FD_LONG, &Value, 1); } inline ERR setLineCap(const LONG Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[23]; + auto field = &this->Class->Dictionary[24]; return field->WriteValue(target, field, FD_LONG, &Value, 1); } inline ERR setInnerJoin(const LONG Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[15]; + auto field = &this->Class->Dictionary[16]; return field->WriteValue(target, field, FD_LONG, &Value, 1); } inline ERR setTabOrder(const LONG Value) noexcept { auto target = this; - auto field = &this->Class->Dictionary[31]; + auto field = &this->Class->Dictionary[32]; return field->WriteValue(target, field, FD_LONG, &Value, 1); } @@ -2194,9 +2195,12 @@ inline void SET_VECTOR_COLOUR(objVectorColour *Colour, DOUBLE Red, DOUBLE Green, #define SVF_A 0x0002b606 #define SVF_ACHROMATOMALY 0xc3f37036 #define SVF_ACHROMATOPSIA 0xc3f56170 +#define SVF_ADDITIVE 0x035604af #define SVF_ALIGN 0x0f174e50 #define SVF_ALT_FILL 0x8c3507fa #define SVF_AMPLITUDE 0x5e60600a +#define SVF_ANIMATE 0x36d195e4 +#define SVF_ANIMATECOLOR 0xcd2d1683 #define SVF_ANIMATEMOTION 0x8a27c6ba #define SVF_ANIMATETRANSFORM 0x6349c940 #define SVF_ARITHMETIC 0x600354ef @@ -2682,6 +2686,12 @@ inline void SET_VECTOR_COLOUR(objVectorColour *Colour, DOUBLE Red, DOUBLE Green, #define SVF_SEMI_EXPANDED 0xa6ff90c9 #define SVF_EXTRA_EXPANDED 0x8c599b5f #define SVF_ULTRA_EXPANDED 0x87e8c363 +#define SVF_CALCMODE 0x0723eabd +#define SVF_KEYPOINTS 0x47b5578b +#define SVF_ORIGIN 0x1315e3ed +#define SVF_KEYTIMES 0xbc9ffbb0 +#define SVF_KEYSPLINES 0x27d7988c +#define SVF_BY 0x00597760 INLINE ERR vecSubscribeInput(APTR Ob, JTYPE Mask, FUNCTION Callback) { diff --git a/include/parasol/strings.hpp b/include/parasol/strings.hpp index e61aadc70..01314cc0e 100644 --- a/include/parasol/strings.hpp +++ b/include/parasol/strings.hpp @@ -2,6 +2,8 @@ #include #include +#include +#include namespace pf { @@ -55,4 +57,14 @@ inline void camelcase(std::string &s) { } } +inline bool iequals(std::string_view lhs, std::string_view rhs) +{ + auto ichar_equals = [](char a, char b) { + return std::tolower(static_cast(a)) == + std::tolower(static_cast(b)); + }; + + return std::ranges::equal(lhs, rhs, ichar_equals); +} + } // namespace diff --git a/include/parasol/system/fields.h b/include/parasol/system/fields.h index 9eecabaf6..1d5bc095c 100644 --- a/include/parasol/system/fields.h +++ b/include/parasol/system/fields.h @@ -7,6 +7,7 @@ #define FID_Category 0x19ee1863LL #define FID_FileName 0x33dfd266LL #define FID_BaseClassID 0xd296daa3LL +#define FID_DisplayScale 0xeb1a36e3LL #define FID_FileDescription 0x675569a9LL #define FID_FileExtension 0x84ea7702LL #define FID_Fields 0xfd727cdcLL @@ -16,23 +17,17 @@ #define FID_Usage 0x1084ecfaLL #define FID_ActionTable 0x1c85e14bLL #define FID_Listener 0xcec9a28bLL -#define FID_ObjectScope 0x97dccef6LL -#define FID_AlarmStyle 0xb5343be3LL #define FID_ClassName 0xe973511cLL -#define FID_DrawCallback 0x14e2b3c0LL -#define FID_ResizeCallback 0xde06a624LL #define FID_ReadOnly 0xf952c623LL #define FID_Flags 0x0f71a6d2LL #define FID_Owner 0x101ac290LL #define FID_Static 0x1c8a8badLL #define FID_Condition 0x733efbacLL #define FID_Activated 0x5198579aLL -#define FID_ValidateInput 0x54a6bd7fLL #define FID_Name 0x7c9b0c46LL #define FID_Test 0x7c9e6865LL #define FID_Statement 0x94a05e9aLL #define FID_ClassID 0xcb8e8f88LL -#define FID_FeedList 0xef8fb495LL #define FID_Compare 0xd3653eecLL #define FID_Exists 0xfc2f1525LL #define FID_Src 0x0b88ab2dLL @@ -112,12 +107,10 @@ #define FID_Bitmap 0xf42617e2LL #define FID_Database 0x05e491daLL #define FID_Target 0x1d90fd6cLL -#define FID_TaskControl 0xc491d7d9LL #define FID_ErrorMsg 0x0b206e76LL #define FID_XML 0x0b88bfd6LL #define FID_Record 0x1926f824LL #define FID_ItemCount 0x2b35f6fdLL -#define FID_PrivateObjects 0xb08c94aaLL #define FID_Output 0x13525d76LL #define FID_RestartLimit 0x3e8d2aa9LL #define FID_ReturnCode 0x45981da0LL @@ -133,12 +126,10 @@ #define FID_CurrentTag 0x7304a3e4LL #define FID_Tags 0x7c9e55d4LL #define FID_TagCount 0xabda762aLL -#define FID_RootIndex 0xb8d300c1LL #define FID_MaxSpeed 0x5ed725dcLL #define FID_Font 0x7c96e4fcLL #define FID_Page 0x7c9c2442LL #define FID_Rows 0x7c9d7ab0LL -#define FID_UpdateRate 0x8f7613d4LL #define FID_MinSpeed 0xaf55759aLL #define FID_Columns 0xd35616e6LL #define FID_CPU 0x0b8866edLL @@ -207,8 +198,6 @@ #define FID_Disable 0x12c4e4b9LL #define FID_Region 0x19290fc9LL #define FID_String 0x1c93affcLL -#define FID_ExitFrame 0x4ec02f4aLL -#define FID_EnterFrame 0x5942272eLL #define FID_ReleaseFrame 0x788d98d1LL #define FID_Text 0x7c9e690aLL #define FID_InputWidth 0x8de1f455LL @@ -230,10 +219,8 @@ #define FID_Disclaimer 0x7a34e702LL #define FID_TotalSamples 0x7c7370deLL #define FID_Description 0x91b0c789LL -#define FID_ResizeFeedback 0xc5d0375cLL #define FID_Active 0xf1644de1LL #define FID_Software 0xf42a7a30LL -#define FID_ItemFeedback 0x7f745cd9LL #define FID_Image 0x0fa87ca8LL #define FID_Strength 0x8ff74c54LL #define FID_RandomSeed 0xc6745cc7LL @@ -947,6 +934,7 @@ #define FID_MatrixRows 0x64419145LL #define FID_MatrixColumns 0x54dc215bLL #define FID_Matrix 0x0d3e291aLL +#define FID_PathTimestamp 0x2efda9a6LL #define FID_PreserveAlpha 0xf9b49d57LL #define FID_Pretext 0xc28b4df1LL #define FID_ResX 0x7c9d4fa7LL diff --git a/include/parasol/vector.hpp b/include/parasol/vector.hpp index 4ca0f0178..c97530f7e 100644 --- a/include/parasol/vector.hpp +++ b/include/parasol/vector.hpp @@ -120,15 +120,17 @@ template class vector { // Erasure - inline void erase(iterator Ref) { + inline T * erase(iterator Ref) { erase(Ref, Ref + 1); + return Ref; } - inline void erase(size_t Index) { + inline T * erase(size_t Index) { erase(from(Index), from(Index + 1)); + return from(Index); } - void erase(iterator Start, iterator Stop) { + T * erase(iterator Start, iterator Stop) { if (Stop IS end()) { for (auto it = Start; it != Stop; it++) { (*it).~T(); @@ -142,6 +144,8 @@ template class vector { auto total_removed = Stop - Start; length -= total_removed; } + + return Start; } iterator insert(const_iterator pTarget, T &pValue) { diff --git a/scripts/dev/idl/common-graphics.fdl b/scripts/dev/idl/common-graphics.fdl index c026a4580..ff6f65be2 100644 --- a/scripts/dev/idl/common-graphics.fdl +++ b/scripts/dev/idl/common-graphics.fdl @@ -114,10 +114,11 @@ flags("LAYOUT", { comment="Universal values for alignment of graphic layouts in { WIDE = "RIGHT|LEFT: The text boundary is extended to the left and right edges of the page." }, { SQUARE = "0: The default. Text will clip around the image's border" }) -struct("HSV", { comment="Colour structure for Hue, Saturation and Value components." }, [[ +struct("HSV", { comment="Colour structure for Hue, Saturation and Value/Light components." }, [[ double Hue # Between 0 and 359.999 double Saturation # Between 0 and 1.0 double Value # Between 0 and 1.0. Corresponds to Value, Lightness or Brightness + double Alpha # Alpha blending value from 0 to 1.0. ]]) struct("FRGB", { comment="64-bit floating point RGB colour value." }, [[ diff --git a/src/core/classes/class_metaclass.cpp b/src/core/classes/class_metaclass.cpp index 977ad7d0e..134405254 100644 --- a/src/core/classes/class_metaclass.cpp +++ b/src/core/classes/class_metaclass.cpp @@ -50,7 +50,7 @@ static ERR GET_Fields(extMetaClass *, const FieldArray **, LONG *); static ERR GET_Location(extMetaClass *, CSTRING *); static ERR GET_Methods(extMetaClass *, const MethodEntry **, LONG *); static ERR GET_Module(extMetaClass *, CSTRING *); -static ERR GET_PrivateObjects(extMetaClass *, OBJECTID **, LONG *); +static ERR GET_Objects(extMetaClass *, OBJECTID **, LONG *); static ERR GET_RootModule(extMetaClass *, class RootModule **); static ERR GET_Dictionary(extMetaClass *, struct Field **, LONG *); static ERR GET_SubFields(extMetaClass *, const FieldArray **, LONG *); @@ -100,12 +100,12 @@ static const std::vector glMetaFieldsPreset = { { (MAXINT)&CategoryTable, NULL, NULL, writeval_default, "Category", FID_Category, sizeof(BaseClass)+28+(sizeof(APTR)*7), 13, FDF_LONG|FDF_LOOKUP|FDF_RI }, // Virtual fields { (MAXINT)"MethodEntry", (ERR (*)(APTR, APTR))GET_Methods, (APTR)SET_Methods, writeval_default, "Methods", FID_Methods, sizeof(BaseClass), 14, FDF_ARRAY|FD_STRUCT|FDF_RI }, - { 0, NULL, (APTR)SET_Actions, writeval_default, "Actions", FID_Actions, sizeof(BaseClass), 15, FDF_POINTER|FDF_I }, + { 0, NULL, (APTR)SET_Actions, writeval_default, "Actions", FID_Actions, sizeof(BaseClass), 15, FDF_POINTER|FDF_I }, { 0, (ERR (*)(APTR, APTR))GET_ActionTable, 0, writeval_default, "ActionTable", FID_ActionTable, sizeof(BaseClass), 16, FDF_ARRAY|FDF_POINTER|FDF_R }, { 0, (ERR (*)(APTR, APTR))GET_Location, 0, writeval_default, "Location", FID_Location, sizeof(BaseClass), 17, FDF_STRING|FDF_R }, { 0, (ERR (*)(APTR, APTR))GET_ClassName, (APTR)SET_ClassName, writeval_default, "Name", FID_Name, sizeof(BaseClass), 18, FDF_STRING|FDF_SYSTEM|FDF_RI }, { 0, (ERR (*)(APTR, APTR))GET_Module, 0, writeval_default, "Module", FID_Module, sizeof(BaseClass), 19, FDF_STRING|FDF_R }, - { 0, (ERR (*)(APTR, APTR))GET_PrivateObjects, 0, writeval_default, "PrivateObjects", FID_PrivateObjects, sizeof(BaseClass), 20, FDF_ARRAY|FDF_LONG|FDF_ALLOC|FDF_R }, + { 0, (ERR (*)(APTR, APTR))GET_Objects, 0, writeval_default, "Objects", FID_Objects, sizeof(BaseClass), 20, FDF_ARRAY|FDF_LONG|FDF_ALLOC|FDF_R }, { (MAXINT)"FieldArray", (ERR (*)(APTR, APTR))GET_SubFields, 0, writeval_default, "SubFields", FID_SubFields, sizeof(BaseClass), 21, FDF_ARRAY|FD_STRUCT|FDF_SYSTEM|FDF_R }, { ID_ROOTMODULE, (ERR (*)(APTR, APTR))GET_RootModule, 0, writeval_default, "RootModule", FID_RootModule, sizeof(BaseClass), 22, FDF_OBJECT|FDF_R }, { 0, 0, 0, NULL, "", 0, 0, 0, 0 } @@ -133,7 +133,7 @@ static const FieldArray glMetaFields[] = { { "Location", FDF_STRING|FDF_R }, { "Name", FDF_STRING|FDF_SYSTEM|FDF_RI, GET_ClassName, SET_ClassName }, { "Module", FDF_STRING|FDF_R, GET_Module }, - { "PrivateObjects", FDF_ARRAY|FDF_LONG|FDF_ALLOC|FDF_R, GET_PrivateObjects }, + { "Objects", FDF_ARRAY|FDF_LONG|FDF_ALLOC|FDF_R, GET_Objects }, { "SubFields", FDF_ARRAY|FD_STRUCT|FDF_SYSTEM|FDF_R, GET_SubFields, NULL, "FieldArray" }, { "RootModule", FDF_OBJECT|FDF_R, GET_RootModule, NULL, ID_ROOTMODULE }, END_FIELD @@ -693,7 +693,7 @@ static ERR GET_Module(extMetaClass *Self, CSTRING *Value) /********************************************************************************************************************* -FIELD- -PrivateObjects: Returns an allocated list of all objects that belong to this class. +Objects: Returns an allocated list of all objects that belong to this class. This field will compile a list of all objects that belong to the class. The list is sorted with the oldest object appearing first. @@ -702,7 +702,7 @@ The resulting array must be terminated with ~FreeResource() after use. *********************************************************************************************************************/ -static ERR GET_PrivateObjects(extMetaClass *Self, OBJECTID **Array, LONG *Elements) +static ERR GET_Objects(extMetaClass *Self, OBJECTID **Array, LONG *Elements) { pf::Log log; std::list objlist; diff --git a/src/core/defs/core.fdl b/src/core/defs/core.fdl index 578cb2722..eaf1f728f 100644 --- a/src/core/defs/core.fdl +++ b/src/core/defs/core.fdl @@ -1637,6 +1637,11 @@ struct BaseClass { // Must be 64-bit aligned return obj ? true : false; } + inline ERR setArray(ULONG FieldID, FLOAT *Value, LONG Size) { return SetArray(this, (FIELD)FieldID|TFLOAT, Value, Size); } + inline ERR setArray(ULONG FieldID, DOUBLE *Value, LONG Size) { return SetArray(this, (FIELD)FieldID|TDOUBLE, Value, Size); } + inline ERR setArray(ULONG FieldID, LONG *Value, LONG Size) { return SetArray(this, (FIELD)FieldID|TLONG, Value, Size); } + inline ERR setArray(ULONG FieldID, LARGE *Value, LONG Size) { return SetArray(this, (FIELD)FieldID|TLARGE, Value, Size); } + inline ERR set(ULONG FieldID, int Value) { return SetField(this, (FIELD)FieldID|TLONG, Value); } inline ERR set(ULONG FieldID, unsigned int Value) { return SetField(this, (FIELD)FieldID|TLONG, Value); } inline ERR set(ULONG FieldID, LARGE Value) { return SetField(this, (FIELD)FieldID|TLARGE, Value); } diff --git a/src/core/defs/fields.fdl b/src/core/defs/fields.fdl index 8ac652b04..3d7b509c4 100644 --- a/src/core/defs/fields.fdl +++ b/src/core/defs/fields.fdl @@ -9,6 +9,7 @@ header({ path="system/fields", copyright="Paul Manias © 1996-2024" }, function( "Category", "FileName", "BaseClassID", + "DisplayScale", "FileDescription", "FileExtension", "Fields", @@ -18,23 +19,17 @@ header({ path="system/fields", copyright="Paul Manias © 1996-2024" }, function( "Usage", "ActionTable", "Listener", - "ObjectScope", - "AlarmStyle", "ClassName", - "DrawCallback", - "ResizeCallback", "ReadOnly", "Flags", "Owner", "Static", "Condition", "Activated", - "ValidateInput", "Name", "Test", "Statement", "ClassID", - "FeedList", "Compare", "Exists", "Src", @@ -114,12 +109,10 @@ header({ path="system/fields", copyright="Paul Manias © 1996-2024" }, function( "Bitmap", "Database", "Target", - "TaskControl", "ErrorMsg", "XML", "Record", "ItemCount", - "PrivateObjects", "Output", "RestartLimit", "ReturnCode", @@ -135,12 +128,10 @@ header({ path="system/fields", copyright="Paul Manias © 1996-2024" }, function( "CurrentTag", "Tags", "TagCount", - "RootIndex", "MaxSpeed", "Font", "Page", "Rows", - "UpdateRate", "MinSpeed", "Columns", "CPU", @@ -209,8 +200,6 @@ header({ path="system/fields", copyright="Paul Manias © 1996-2024" }, function( "Disable", "Region", "String", - "ExitFrame", - "EnterFrame", "ReleaseFrame", "Text", "InputWidth", @@ -232,10 +221,8 @@ header({ path="system/fields", copyright="Paul Manias © 1996-2024" }, function( "Disclaimer", "TotalSamples", "Description", - "ResizeFeedback", "Active", "Software", - "ItemFeedback", "Image", "Strength", "RandomSeed", @@ -954,6 +941,7 @@ header({ path="system/fields", copyright="Paul Manias © 1996-2024" }, function( "MatrixRows", "MatrixColumns", "Matrix", + "PathTimestamp", "PreserveAlpha", "Pretext", "ResX", diff --git a/src/core/idl.h b/src/core/idl.h index 735a98eee..a3695a2ef 100644 --- a/src/core/idl.h +++ b/src/core/idl.h @@ -1 +1 @@ -char glIDL[] = { 115,46,73,110,112,117,116,69,118,101,110,116,58,112,78,101,120,116,58,73,110,112,117,116,69,118,101,110,116,44,100,86,97,108,117,101,44,120,84,105,109,101,115,116,97,109,112,44,108,82,101,99,105,112,105,101,110,116,73,68,44,108,79,118,101,114,73,68,44,100,65,98,115,88,44,100,65,98,115,89,44,100,88,44,100,89,44,108,68,101,118,105,99,101,73,68,44,108,84,121,112,101,44,108,70,108,97,103,115,44,108,77,97,115,107,10,115,46,100,99,82,101,113,117,101,115,116,58,108,73,116,101,109,44,99,80,114,101,102,101,114,101,110,99,101,91,52,93,10,115,46,100,99,65,117,100,105,111,58,108,83,105,122,101,44,108,70,111,114,109,97,116,10,115,46,100,99,75,101,121,69,110,116,114,121,58,108,70,108,97,103,115,44,108,86,97,108,117,101,44,120,84,105,109,101,115,116,97,109,112,44,108,85,110,105,99,111,100,101,10,115,46,100,99,68,101,118,105,99,101,73,110,112,117,116,58,100,86,97,108,117,101,115,91,50,93,44,120,84,105,109,101,115,116,97,109,112,44,108,68,101,118,105,99,101,73,68,44,108,70,108,97,103,115,44,108,84,121,112,101,10,115,46,68,97,116,101,84,105,109,101,58,119,89,101,97,114,44,99,77,111,110,116,104,44,99,68,97,121,44,99,72,111,117,114,44,99,77,105,110,117,116,101,44,99,83,101,99,111,110,100,44,99,84,105,109,101,90,111,110,101,10,115,46,72,83,86,58,100,72,117,101,44,100,83,97,116,117,114,97,116,105,111,110,44,100,86,97,108,117,101,10,115,46,70,82,71,66,58,102,82,101,100,44,102,71,114,101,101,110,44,102,66,108,117,101,44,102,65,108,112,104,97,10,115,46,82,71,66,56,58,117,99,82,101,100,44,117,99,71,114,101,101,110,44,117,99,66,108,117,101,44,117,99,65,108,112,104,97,10,115,46,82,71,66,49,54,58,117,119,82,101,100,44,117,119,71,114,101,101,110,44,117,119,66,108,117,101,44,117,119,65,108,112,104,97,10,115,46,82,71,66,51,50,58,117,108,82,101,100,44,117,108,71,114,101,101,110,44,117,108,66,108,117,101,44,117,108,65,108,112,104,97,10,115,46,82,71,66,80,97,108,101,116,116,101,58,108,65,109,116,67,111,108,111,117,114,115,44,101,67,111,108,58,82,71,66,56,91,50,53,54,93,10,115,46,67,111,108,111,117,114,70,111,114,109,97,116,58,117,99,82,101,100,83,104,105,102,116,44,117,99,71,114,101,101,110,83,104,105,102,116,44,117,99,66,108,117,101,83,104,105,102,116,44,117,99,65,108,112,104,97,83,104,105,102,116,44,117,99,82,101,100,77,97,115,107,44,117,99,71,114,101,101,110,77,97,115,107,44,117,99,66,108,117,101,77,97,115,107,44,117,99,65,108,112,104,97,77,97,115,107,44,117,99,82,101,100,80,111,115,44,117,99,71,114,101,101,110,80,111,115,44,117,99,66,108,117,101,80,111,115,44,117,99,65,108,112,104,97,80,111,115,44,117,99,66,105,116,115,80,101,114,80,105,120,101,108,10,115,46,67,108,105,112,82,101,99,116,97,110,103,108,101,58,108,76,101,102,116,44,108,84,111,112,44,108,82,105,103,104,116,44,108,66,111,116,116,111,109,10,115,46,69,100,103,101,115,58,108,76,101,102,116,44,108,84,111,112,44,108,82,105,103,104,116,44,108,66,111,116,116,111,109,10,115,46,79,98,106,101,99,116,83,105,103,110,97,108,58,111,79,98,106,101,99,116,10,115,46,112,102,66,97,115,101,54,52,68,101,99,111,100,101,58,117,99,83,116,101,112,44,117,99,80,108,97,105,110,67,104,97,114,44,117,99,66,105,116,10,115,46,112,102,66,97,115,101,54,52,69,110,99,111,100,101,58,117,99,83,116,101,112,44,117,99,82,101,115,117,108,116,44,108,83,116,101,112,67,111,117,110,116,10,115,46,70,117,110,99,116,105,111,110,70,105,101,108,100,58,115,78,97,109,101,44,117,108,84,121,112,101,10,115,46,70,117,110,99,116,105,111,110,58,112,65,100,100,114,101,115,115,44,115,78,97,109,101,44,112,65,114,103,115,58,70,117,110,99,116,105,111,110,70,105,101,108,100,10,115,46,70,105,101,108,100,65,114,114,97,121,58,115,78,97,109,101,44,112,71,101,116,70,105,101,108,100,44,112,83,101,116,70,105,101,108,100,44,109,65,114,103,44,117,108,70,108,97,103,115,10,115,46,70,105,101,108,100,68,101,102,58,115,78,97,109,101,44,108,86,97,108,117,101,10,115,46,83,121,115,116,101,109,83,116,97,116,101,58,115,80,108,97,116,102,111,114,109,44,108,67,111,110,115,111,108,101,70,68,44,108,83,116,97,103,101,10,115,46,86,97,114,105,97,98,108,101,58,117,108,84,121,112,101,44,108,85,110,117,115,101,100,44,120,76,97,114,103,101,44,100,68,111,117,98,108,101,44,112,80,111,105,110,116,101,114,10,115,46,65,99,116,105,111,110,65,114,114,97,121,58,112,82,111,117,116,105,110,101,44,108,65,99,116,105,111,110,67,111,100,101,10,115,46,65,99,116,105,111,110,84,97,98,108,101,58,117,108,72,97,115,104,44,108,83,105,122,101,44,115,78,97,109,101,44,112,65,114,103,115,58,70,117,110,99,116,105,111,110,70,105,101,108,100,10,115,46,67,104,105,108,100,69,110,116,114,121,58,108,79,98,106,101,99,116,73,68,44,117,108,67,108,97,115,115,73,68,10,115,46,77,101,115,115,97,103,101,58,120,84,105,109,101,44,108,85,73,68,44,108,84,121,112,101,44,108,83,105,122,101,10,115,46,77,101,109,73,110,102,111,58,112,83,116,97,114,116,44,108,79,98,106,101,99,116,73,68,44,117,108,83,105,122,101,44,108,70,108,97,103,115,44,108,77,101,109,111,114,121,73,68,44,119,65,99,99,101,115,115,67,111,117,110,116,10,115,46,67,111,109,112,114,101,115,115,105,111,110,70,101,101,100,98,97,99,107,58,108,70,101,101,100,98,97,99,107,73,68,44,108,73,110,100,101,120,44,115,80,97,116,104,44,115,68,101,115,116,44,120,80,114,111,103,114,101,115,115,44,120,79,114,105,103,105,110,97,108,83,105,122,101,44,120,67,111,109,112,114,101,115,115,101,100,83,105,122,101,44,119,89,101,97,114,44,119,77,111,110,116,104,44,119,68,97,121,44,119,72,111,117,114,44,119,77,105,110,117,116,101,44,119,83,101,99,111,110,100,10,115,46,67,111,109,112,114,101,115,115,101,100,73,116,101,109,58,120,79,114,105,103,105,110,97,108,83,105,122,101,44,120,67,111,109,112,114,101,115,115,101,100,83,105,122,101,44,112,78,101,120,116,58,67,111,109,112,114,101,115,115,101,100,73,116,101,109,44,115,80,97,116,104,44,108,80,101,114,109,105,115,115,105,111,110,115,44,108,85,115,101,114,73,68,44,108,71,114,111,117,112,73,68,44,108,79,116,104,101,114,115,73,68,44,108,70,108,97,103,115,44,101,67,114,101,97,116,101,100,58,68,97,116,101,84,105,109,101,44,101,77,111,100,105,102,105,101,100,58,68,97,116,101,84,105,109,101,10,115,46,70,105,108,101,73,110,102,111,58,120,83,105,122,101,44,120,84,105,109,101,83,116,97,109,112,44,112,78,101,120,116,58,70,105,108,101,73,110,102,111,44,115,78,97,109,101,44,108,70,108,97,103,115,44,108,80,101,114,109,105,115,115,105,111,110,115,44,108,85,115,101,114,73,68,44,108,71,114,111,117,112,73,68,44,101,67,114,101,97,116,101,100,58,68,97,116,101,84,105,109,101,44,101,77,111,100,105,102,105,101,100,58,68,97,116,101,84,105,109,101,10,115,46,68,105,114,73,110,102,111,58,112,73,110,102,111,58,70,105,108,101,73,110,102,111,10,115,46,70,105,108,101,70,101,101,100,98,97,99,107,58,120,83,105,122,101,44,120,80,111,115,105,116,105,111,110,44,115,80,97,116,104,44,115,68,101,115,116,44,108,70,101,101,100,98,97,99,107,73,68,44,99,82,101,115,101,114,118,101,100,91,51,50,93,10,115,46,70,105,101,108,100,58,109,65,114,103,44,112,71,101,116,86,97,108,117,101,44,112,83,101,116,86,97,108,117,101,44,112,87,114,105,116,101,86,97,108,117,101,44,115,78,97,109,101,44,117,108,70,105,101,108,100,73,68,44,117,119,79,102,102,115,101,116,44,117,119,73,110,100,101,120,44,117,108,70,108,97,103,115,10,99,46,65,67,58,65,99,116,105,118,97,116,101,61,48,120,50,44,67,108,101,97,114,61,48,120,52,44,67,108,105,112,98,111,97,114,100,61,48,120,50,100,44,67,111,112,121,68,97,116,97,61,48,120,55,44,67,117,115,116,111,109,61,48,120,51,52,44,68,97,116,97,70,101,101,100,61,48,120,56,44,68,101,97,99,116,105,118,97,116,101,61,48,120,57,44,68,105,115,97,98,108,101,61,48,120,50,102,44,68,114,97,103,68,114,111,112,61,48,120,49,48,44,68,114,97,119,61,48,120,97,44,69,78,68,61,48,120,51,53,44,69,110,97,98,108,101,61,48,120,51,48,44,70,108,117,115,104,61,48,120,98,44,70,111,99,117,115,61,48,120,99,44,70,114,101,101,61,48,120,100,44,70,114,101,101,87,97,114,110,105,110,103,61,48,120,53,44,71,101,116,86,97,114,61,48,120,102,44,72,105,100,101,61,48,120,49,49,44,73,110,105,116,61,48,120,49,50,44,76,111,99,107,61,48,120,49,51,44,76,111,115,116,70,111,99,117,115,61,48,120,49,52,44,77,111,118,101,61,48,120,49,53,44,77,111,118,101,84,111,66,97,99,107,61,48,120,49,54,44,77,111,118,101,84,111,70,114,111,110,116,61,48,120,49,55,44,77,111,118,101,84,111,80,111,105,110,116,61,48,120,51,50,44,78,101,119,67,104,105,108,100,61,48,120,49,56,44,78,101,119,79,98,106,101,99,116,61,48,120,49,97,44,78,101,119,79,119,110,101,114,61,48,120,49,57,44,78,101,120,116,61,48,120,50,57,44,80,114,101,118,61,48,120,50,97,44,81,117,101,114,121,61,48,120,49,99,44,82,101,97,100,61,48,120,49,100,44,82,101,100,105,109,101,110,115,105,111,110,61,48,120,51,49,44,82,101,100,111,61,48,120,49,98,44,82,101,102,114,101,115,104,61,48,120,50,101,44,82,101,110,97,109,101,61,48,120,49,101,44,82,101,115,101,116,61,48,120,49,102,44,82,101,115,105,122,101,61,48,120,50,48,44,83,97,118,101,73,109,97,103,101,61,48,120,50,49,44,83,97,118,101,83,101,116,116,105,110,103,115,61,48,120,101,44,83,97,118,101,84,111,79,98,106,101,99,116,61,48,120,50,50,44,83,99,114,111,108,108,61,48,120,50,51,44,83,99,114,111,108,108,84,111,80,111,105,110,116,61,48,120,51,51,44,83,101,101,107,61,48,120,50,52,44,83,101,108,101,99,116,65,114,101,97,61,48,120,51,44,83,101,116,70,105,101,108,100,61,48,120,50,99,44,83,101,116,86,97,114,61,48,120,50,53,44,83,104,111,119,61,48,120,50,54,44,83,105,103,110,97,108,61,48,120,49,44,83,111,114,116,61,48,120,54,44,85,110,100,111,61,48,120,50,55,44,85,110,108,111,99,107,61,48,120,50,56,44,87,114,105,116,101,61,48,120,50,98,10,99,46,65,76,73,71,78,58,66,79,84,84,79,77,61,48,120,50,48,44,67,69,78,84,69,82,61,48,120,99,44,72,79,82,73,90,79,78,84,65,76,61,48,120,52,44,76,69,70,84,61,48,120,49,44,77,73,68,68,76,69,61,48,120,99,44,82,73,71,72,84,61,48,120,50,44,84,79,80,61,48,120,49,48,44,86,69,82,84,73,67,65,76,61,48,120,56,10,99,46,67,67,70,58,65,85,68,73,79,61,48,120,50,48,48,44,67,79,77,77,65,78,68,61,48,120,49,44,68,65,84,65,61,48,120,52,48,48,44,68,82,65,87,65,66,76,69,61,48,120,50,44,69,70,70,69,67,84,61,48,120,52,44,70,73,76,69,83,89,83,84,69,77,61,48,120,56,44,71,82,65,80,72,73,67,83,61,48,120,49,48,44,71,85,73,61,48,120,50,48,44,73,79,61,48,120,52,48,44,77,73,83,67,61,48,120,56,48,48,44,77,85,76,84,73,77,69,68,73,65,61,48,120,50,48,48,48,44,78,69,84,87,79,82,75,61,48,120,49,48,48,48,44,83,89,83,84,69,77,61,48,120,56,48,44,84,79,79,76,61,48,120,49,48,48,10,99,46,67,70,58,68,69,70,76,65,84,69,61,48,120,51,44,71,90,73,80,61,48,120,49,44,90,76,73,66,61,48,120,50,10,99,46,67,76,70,58,78,79,95,79,87,78,69,82,83,72,73,80,61,48,120,50,44,80,82,79,77,79,84,69,95,73,78,84,69,71,82,65,76,61,48,120,49,10,99,46,67,76,73,80,77,79,68,69,58,67,79,80,89,61,48,120,50,44,67,85,84,61,48,120,49,44,80,65,83,84,69,61,48,120,52,10,99,46,67,77,70,58,65,80,80,76,89,95,83,69,67,85,82,73,84,89,61,48,120,50,48,44,67,82,69,65,84,69,95,70,73,76,69,61,48,120,52,44,78,69,87,61,48,120,50,44,78,79,95,76,73,78,75,83,61,48,120,49,48,44,80,65,83,83,87,79,82,68,61,48,120,49,44,82,69,65,68,95,79,78,76,89,61,48,120,56,10,99,46,67,78,70,58,65,85,84,79,95,83,65,86,69,61,48,120,50,44,78,69,87,61,48,120,56,44,79,80,84,73,79,78,65,76,95,70,73,76,69,83,61,48,120,52,44,83,84,82,73,80,95,81,85,79,84,69,83,61,48,120,49,10,99,46,68,65,84,65,58,65,85,68,73,79,61,48,120,53,44,67,79,78,84,69,78,84,61,48,120,98,44,68,69,86,73,67,69,95,73,78,80,85,84,61,48,120,51,44,70,73,76,69,61,48,120,97,44,73,77,65,71,69,61,48,120,55,44,73,78,80,85,84,95,82,69,65,68,89,61,48,120,99,44,82,65,87,61,48,120,50,44,82,69,67,69,73,80,84,61,48,120,57,44,82,69,67,79,82,68,61,48,120,54,44,82,69,81,85,69,83,84,61,48,120,56,44,84,69,88,84,61,48,120,49,44,88,77,76,61,48,120,52,10,99,46,68,69,86,73,67,69,58,67,79,77,80,65,67,84,95,68,73,83,67,61,48,120,49,44,70,76,79,80,80,89,95,68,73,83,75,61,48,120,52,44,72,65,82,68,95,68,73,83,75,61,48,120,50,44,77,69,77,79,82,89,61,48,120,49,48,48,48,44,77,79,68,69,77,61,48,120,50,48,48,48,44,78,69,84,87,79,82,75,61,48,120,56,48,44,80,82,73,78,84,69,82,61,48,120,50,48,48,44,80,82,73,78,84,69,82,95,51,68,61,48,120,56,48,48,48,44,82,69,65,68,61,48,120,56,44,82,69,77,79,86,65,66,76,69,61,48,120,50,48,44,82,69,77,79,86,69,65,66,76,69,61,48,120,50,48,44,83,67,65,78,78,69,82,61,48,120,52,48,48,44,83,67,65,78,78,69,82,95,51,68,61,48,120,49,48,48,48,48,44,83,79,70,84,87,65,82,69,61,48,120,52,48,44,84,65,80,69,61,48,120,49,48,48,44,84,69,77,80,79,82,65,82,89,61,48,120,56,48,48,44,85,83,66,61,48,120,52,48,48,48,44,87,82,73,84,69,61,48,120,49,48,10,99,46,68,77,70,58,70,73,88,69,68,95,67,69,78,84,69,82,95,88,61,48,120,49,48,48,48,48,48,44,70,73,88,69,68,95,67,69,78,84,69,82,95,89,61,48,120,50,48,48,48,48,48,44,70,73,88,69,68,95,68,69,80,84,72,61,48,120,49,48,48,48,44,70,73,88,69,68,95,72,69,73,71,72,84,61,48,120,49,48,48,44,70,73,88,69,68,95,82,65,68,73,85,83,61,48,120,50,48,50,48,48,48,48,44,70,73,88,69,68,95,82,65,68,73,85,83,95,88,61,48,120,50,48,48,48,48,44,70,73,88,69,68,95,82,65,68,73,85,83,95,89,61,48,120,50,48,48,48,48,48,48,44,70,73,88,69,68,95,87,73,68,84,72,61,48,120,50,48,48,44,70,73,88,69,68,95,88,61,48,120,52,44,70,73,88,69,68,95,88,95,79,70,70,83,69,84,61,48,120,52,48,44,70,73,88,69,68,95,89,61,48,120,56,44,70,73,88,69,68,95,89,95,79,70,70,83,69,84,61,48,120,56,48,44,70,73,88,69,68,95,90,61,48,120,52,48,48,48,44,72,69,73,71,72,84,61,48,120,53,48,48,44,72,69,73,71,72,84,95,70,76,65,71,83,61,48,120,53,97,48,44,72,79,82,73,90,79,78,84,65,76,95,70,76,65,71,83,61,48,120,97,53,53,44,83,67,65,76,69,68,95,67,69,78,84,69,82,95,88,61,48,120,52,48,48,48,48,44,83,67,65,76,69,68,95,67,69,78,84,69,82,95,89,61,48,120,56,48,48,48,48,44,83,67,65,76,69,68,95,68,69,80,84,72,61,48,120,50,48,48,48,44,83,67,65,76,69,68,95,72,69,73,71,72,84,61,48,120,52,48,48,44,83,67,65,76,69,68,95,82,65,68,73,85,83,61,48,120,49,48,49,48,48,48,48,44,83,67,65,76,69,68,95,82,65,68,73,85,83,95,88,61,48,120,49,48,48,48,48,44,83,67,65,76,69,68,95,82,65,68,73,85,83,95,89,61,48,120,49,48,48,48,48,48,48,44,83,67,65,76,69,68,95,87,73,68,84,72,61,48,120,56,48,48,44,83,67,65,76,69,68,95,88,61,48,120,49,44,83,67,65,76,69,68,95,88,95,79,70,70,83,69,84,61,48,120,49,48,44,83,67,65,76,69,68,95,89,61,48,120,50,44,83,67,65,76,69,68,95,89,95,79,70,70,83,69,84,61,48,120,50,48,44,83,67,65,76,69,68,95,90,61,48,120,56,48,48,48,44,83,84,65,84,85,83,95,67,72,65,78,71,69,61,48,120,99,48,48,48,48,48,44,83,84,65,84,85,83,95,67,72,65,78,71,69,95,72,61,48,120,52,48,48,48,48,48,44,83,84,65,84,85,83,95,67,72,65,78,71,69,95,86,61,48,120,56,48,48,48,48,48,44,86,69,82,84,73,67,65,76,95,70,76,65,71,83,61,48,120,53,97,97,44,87,73,68,84,72,61,48,120,97,48,48,44,87,73,68,84,72,95,70,76,65,71,83,61,48,120,97,53,48,44,88,61,48,120,53,44,88,95,79,70,70,83,69,84,61,48,120,53,48,44,89,61,48,120,97,44,89,95,79,70,70,83,69,84,61,48,120,97,48,10,99,46,68,82,76,58,68,79,87,78,61,48,120,49,44,69,65,83,84,61,48,120,50,44,76,69,70,84,61,48,120,51,44,78,79,82,84,72,61,48,120,48,44,78,79,82,84,72,95,69,65,83,84,61,48,120,52,44,78,79,82,84,72,95,87,69,83,84,61,48,120,53,44,82,73,71,72,84,61,48,120,50,44,83,79,85,84,72,61,48,120,49,44,83,79,85,84,72,95,69,65,83,84,61,48,120,54,44,83,79,85,84,72,95,87,69,83,84,61,48,120,55,44,85,80,61,48,120,48,44,87,69,83,84,61,48,120,51,10,99,46,69,68,71,69,58,65,76,76,61,48,120,102,102,44,66,79,84,84,79,77,61,48,120,56,44,66,79,84,84,79,77,95,76,69,70,84,61,48,120,52,48,44,66,79,84,84,79,77,95,82,73,71,72,84,61,48,120,56,48,44,76,69,70,84,61,48,120,50,44,82,73,71,72,84,61,48,120,52,44,84,79,80,61,48,120,49,44,84,79,80,95,76,69,70,84,61,48,120,49,48,44,84,79,80,95,82,73,71,72,84,61,48,120,50,48,10,99,46,69,82,70,58,78,111,116,105,102,105,101,100,61,48,120,52,48,48,48,48,48,48,48,10,99,46,69,82,82,58,65,99,99,101,115,115,77,101,109,111,114,121,61,48,120,52,98,44,65,99,99,101,115,115,79,98,106,101,99,116,61,48,120,53,51,44,65,99,99,101,115,115,83,101,109,97,112,104,111,114,101,61,48,120,55,51,44,65,99,116,105,118,97,116,101,61,48,120,52,50,44,65,100,100,67,108,97,115,115,61,48,120,52,49,44,65,108,108,111,99,77,101,109,111,114,121,61,48,120,53,52,44,65,108,108,111,99,83,101,109,97,112,104,111,114,101,61,48,120,55,50,44,65,108,114,101,97,100,121,68,101,102,105,110,101,100,61,48,120,98,54,44,65,108,114,101,97,100,121,76,111,99,107,101,100,61,48,120,57,99,44,65,114,103,115,61,48,120,49,52,44,65,114,114,97,121,70,117,108,108,61,48,120,50,100,44,66,117,102,102,101,114,79,118,101,114,102,108,111,119,61,48,120,53,99,44,66,117,115,121,61,48,120,56,98,44,67,97,110,99,101,108,108,101,100,61,48,120,51,44,67,97,114,100,82,101,97,100,101,114,85,110,97,118,97,105,108,97,98,108,101,61,48,120,57,102,44,67,97,114,100,82,101,97,100,101,114,85,110,107,110,111,119,110,61,48,120,57,100,44,67,111,109,112,114,101,115,115,105,111,110,61,48,120,97,100,44,67,111,110,110,101,99,116,105,111,110,65,98,111,114,116,101,100,61,48,120,56,99,44,67,111,110,110,101,99,116,105,111,110,82,101,102,117,115,101,100,61,48,120,56,51,44,67,111,110,115,116,114,97,105,110,116,86,105,111,108,97,116,105,111,110,61,48,120,56,56,44,67,111,110,116,105,110,117,101,61,48,120,53,44,67,111,114,101,86,101,114,115,105,111,110,61,48,120,50,54,44,67,114,101,97,116,101,70,105,108,101,61,48,120,55,52,44,67,114,101,97,116,101,79,98,106,101,99,116,61,48,120,54,53,44,67,114,101,97,116,101,82,101,115,111,117,114,99,101,61,48,120,98,50,44,68,97,116,97,83,105,122,101,61,48,120,56,97,44,68,101,97,99,116,105,118,97,116,101,100,61,48,120,57,97,44,68,101,97,100,76,111,99,107,61,48,120,51,101,44,68,101,99,111,109,112,114,101,115,115,105,111,110,61,48,120,97,99,44,68,101,108,101,116,101,70,105,108,101,61,48,120,55,53,44,68,105,114,69,109,112,116,121,61,48,120,56,44,68,105,115,99,111,110,110,101,99,116,101,100,61,48,120,56,54,44,68,111,78,111,116,69,120,112,117,110,103,101,61,48,120,51,48,44,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,55,56,44,68,111,117,98,108,101,73,110,105,116,61,48,120,52,51,44,68,114,97,119,61,48,120,52,56,44,69,78,68,61,48,120,98,101,44,69,109,112,116,121,83,116,114,105,110,103,61,48,120,54,100,44,69,110,100,79,102,70,105,108,101,61,48,120,55,101,44,69,110,116,114,121,77,105,115,115,105,110,103,72,101,97,100,101,114,61,48,120,51,97,44,69,120,97,109,105,110,101,70,97,105,108,101,100,61,48,120,49,57,44,69,120,99,101,112,116,105,111,110,61,48,120,97,51,44,69,120,99,101,112,116,105,111,110,84,104,114,101,115,104,111,108,100,61,48,120,57,44,69,120,99,108,117,115,105,118,101,68,101,110,105,101,100,61,48,120,53,51,44,69,120,101,99,86,105,111,108,97,116,105,111,110,61,48,120,56,102,44,69,120,105,115,116,115,61,48,120,55,97,44,69,120,112,101,99,116,101,100,70,105,108,101,61,48,120,54,102,44,69,120,112,101,99,116,101,100,70,111,108,100,101,114,61,48,120,97,101,44,70,97,105,108,101,100,61,48,120,100,44,70,97,108,115,101,61,48,120,49,44,70,105,101,108,100,78,111,116,83,101,116,61,48,120,52,52,44,70,105,101,108,100,83,101,97,114,99,104,61,48,120,51,50,44,70,105,101,108,100,84,121,112,101,77,105,115,109,97,116,99,104,61,48,120,53,97,44,70,105,108,101,61,48,120,101,44,70,105,108,101,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,49,50,44,70,105,108,101,69,120,105,115,116,115,61,48,120,54,51,44,70,105,108,101,78,111,116,70,111,117,110,100,61,48,120,49,50,44,70,105,108,101,82,101,97,100,70,108,97,103,61,48,120,52,54,44,70,105,108,101,87,114,105,116,101,70,108,97,103,61,48,120,52,55,44,70,105,110,105,115,104,101,100,61,48,120,55,101,44,70,117,110,99,116,105,111,110,61,48,120,98,53,44,71,101,116,70,105,101,108,100,61,48,120,53,54,44,71,101,116,83,117,114,102,97,99,101,73,110,102,111,61,48,120,55,100,44,72,111,115,116,78,111,116,70,111,117,110,100,61,48,120,56,49,44,72,111,115,116,85,110,114,101,97,99,104,97,98,108,101,61,48,120,56,53,44,73,100,101,110,116,105,99,97,108,80,97,116,104,115,61,48,120,55,57,44,73,108,108,101,103,97,108,65,99,116,105,111,110,65,116,116,101,109,112,116,61,48,120,51,57,44,73,108,108,101,103,97,108,65,99,116,105,111,110,73,68,61,48,120,51,55,44,73,108,108,101,103,97,108,65,100,100,114,101,115,115,61,48,120,57,49,44,73,108,108,101,103,97,108,77,101,116,104,111,100,73,68,61,48,120,51,54,44,73,109,109,117,116,97,98,108,101,61,48,120,97,102,44,73,110,85,115,101,61,48,120,99,44,73,110,105,116,61,48,120,50,49,44,73,110,105,116,77,111,100,117,108,101,61,48,120,49,49,44,73,110,112,117,116,79,117,116,112,117,116,61,48,120,57,52,44,73,110,116,101,103,114,105,116,121,86,105,111,108,97,116,105,111,110,61,48,120,56,56,44,73,110,118,97,108,105,100,68,97,116,97,61,48,120,102,44,73,110,118,97,108,105,100,68,105,109,101,110,115,105,111,110,61,48,120,53,57,44,73,110,118,97,108,105,100,72,84,84,80,82,101,115,112,111,110,115,101,61,48,120,97,49,44,73,110,118,97,108,105,100,72,97,110,100,108,101,61,48,120,57,54,44,73,110,118,97,108,105,100,79,98,106,101,99,116,61,48,120,56,101,44,73,110,118,97,108,105,100,80,97,116,104,61,48,120,51,51,44,73,110,118,97,108,105,100,82,101,102,101,114,101,110,99,101,61,48,120,97,50,44,73,110,118,97,108,105,100,83,116,97,116,101,61,48,120,56,48,44,73,110,118,97,108,105,100,85,82,73,61,48,120,56,50,44,73,110,118,97,108,105,100,86,97,108,117,101,61,48,120,57,56,44,76,105,109,105,116,101,100,83,117,99,99,101,115,115,61,48,120,50,44,76,105,115,116,67,104,105,108,100,114,101,110,61,48,120,54,97,44,76,111,97,100,77,111,100,117,108,101,61,48,120,57,53,44,76,111,99,107,61,48,120,49,56,44,76,111,99,107,70,97,105,108,101,100,61,48,120,49,56,44,76,111,99,107,77,117,116,101,120,61,48,120,97,97,44,76,111,99,107,82,101,113,117,105,114,101,100,61,48,120,57,98,44,76,111,99,107,101,100,61,48,120,57,99,44,76,111,111,112,61,48,120,54,50,44,76,111,115,116,67,108,97,115,115,61,48,120,49,97,44,76,111,115,116,79,119,110,101,114,61,48,120,50,102,44,76,111,119,67,97,112,97,99,105,116,121,61,48,120,50,48,44,77,97,114,107,101,100,70,111,114,68,101,108,101,116,105,111,110,61,48,120,51,53,44,77,101,109,111,114,121,61,48,120,49,100,44,77,101,109,111,114,121,67,111,114,114,117,112,116,61,48,120,51,49,44,77,101,109,111,114,121,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,51,100,44,77,101,109,111,114,121,73,110,102,111,61,48,120,54,54,44,77,105,115,109,97,116,99,104,61,48,120,53,101,44,77,105,115,115,105,110,103,67,108,97,115,115,61,48,120,52,53,44,77,105,115,115,105,110,103,67,108,97,115,115,78,97,109,101,61,48,120,50,97,44,77,105,115,115,105,110,103,80,97,116,104,61,48,120,52,99,44,77,111,100,117,108,101,73,110,105,116,70,97,105,108,101,100,61,48,120,51,99,44,77,111,100,117,108,101,77,105,115,115,105,110,103,73,110,105,116,61,48,120,51,98,44,77,111,100,117,108,101,77,105,115,115,105,110,103,78,97,109,101,61,48,120,52,48,44,77,111,100,117,108,101,79,112,101,110,70,97,105,108,101,100,61,48,120,51,56,44,78,101,101,100,79,119,110,101,114,61,48,120,50,52,44,78,101,101,100,87,105,100,116,104,72,101,105,103,104,116,61,48,120,50,55,44,78,101,103,97,116,105,118,101,67,108,97,115,115,73,68,61,48,120,50,57,44,78,101,103,97,116,105,118,101,83,117,98,67,108,97,115,115,73,68,61,48,120,50,56,44,78,101,116,119,111,114,107,85,110,114,101,97,99,104,97,98,108,101,61,48,120,56,52,44,78,101,119,79,98,106,101,99,116,61,48,120,53,53,44,78,111,65,99,116,105,111,110,61,48,120,49,98,44,78,111,68,97,116,97,61,48,120,49,53,44,78,111,70,105,101,108,100,65,99,99,101,115,115,61,48,120,53,55,44,78,111,77,97,116,99,104,105,110,103,79,98,106,101,99,116,61,48,120,52,97,44,78,111,77,101,100,105,97,73,110,115,101,114,116,101,100,61,48,120,57,101,44,78,111,77,101,109,111,114,121,61,48,120,97,44,78,111,77,101,116,104,111,100,115,61,48,120,52,57,44,78,111,80,101,114,109,105,115,115,105,111,110,61,48,120,50,50,44,78,111,80,111,105,110,116,101,114,61,48,120,98,44,78,111,83,101,97,114,99,104,82,101,115,117,108,116,61,48,120,52,101,44,78,111,83,116,97,116,115,61,48,120,49,102,44,78,111,83,117,112,112,111,114,116,61,48,120,49,99,44,78,111,116,70,111,117,110,100,61,48,120,49,48,44,78,111,116,73,110,105,116,105,97,108,105,115,101,100,61,48,120,54,55,44,78,111,116,76,111,99,107,101,100,61,48,120,52,100,44,78,111,116,80,111,115,115,105,98,108,101,61,48,120,98,51,44,78,111,116,104,105,110,103,68,111,110,101,61,48,120,52,44,78,111,116,105,102,105,101,100,61,48,120,52,48,48,48,48,48,48,48,44,78,117,108,108,65,114,103,115,61,48,120,56,100,44,79,98,106,101,99,116,67,111,114,114,117,112,116,61,48,120,53,48,44,79,98,106,101,99,116,69,120,105,115,116,115,61,48,120,54,101,44,79,98,115,111,108,101,116,101,61,48,120,98,49,44,79,98,116,97,105,110,77,101,116,104,111,100,61,48,120,50,99,44,79,107,97,121,61,48,120,48,44,79,112,101,110,70,105,108,101,61,48,120,55,54,44,79,112,101,110,71,76,61,48,120,97,53,44,79,117,116,79,102,66,111,117,110,100,115,61,48,120,53,102,44,79,117,116,79,102,68,97,116,97,61,48,120,55,101,44,79,117,116,79,102,82,97,110,103,101,61,48,120,50,98,44,79,117,116,79,102,83,112,97,99,101,61,48,120,55,99,44,79,117,116,115,105,100,101,77,97,105,110,84,104,114,101,97,100,61,48,120,97,54,44,79,119,110,101,114,78,101,101,100,115,66,105,116,109,97,112,61,48,120,50,53,44,79,119,110,101,114,80,97,115,115,84,104,114,111,117,103,104,61,48,120,53,49,44,80,101,114,109,105,115,115,105,111,110,68,101,110,105,101,100,61,48,120,50,50,44,80,101,114,109,105,115,115,105,111,110,115,61,48,120,50,50,44,80,114,111,120,121,83,83,76,84,117,110,110,101,108,61,48,120,97,48,44,81,117,101,114,121,61,48,120,50,101,44,82,101,97,100,61,48,120,49,54,44,82,101,97,100,70,105,108,101,84,111,66,117,102,102,101,114,61,48,120,98,48,44,82,101,97,100,79,110,108,121,61,48,120,55,55,44,82,101,97,108,108,111,99,77,101,109,111,114,121,61,48,120,54,49,44,82,101,99,117,114,115,105,111,110,61,48,120,57,48,44,82,101,100,105,109,101,110,115,105,111,110,61,48,120,55,49,44,82,101,102,114,101,115,104,61,48,120,54,57,44,82,101,115,105,122,101,61,48,120,55,48,44,82,101,115,111,108,118,101,80,97,116,104,61,48,120,54,52,44,82,101,115,111,108,118,101,83,121,109,98,111,108,61,48,120,98,52,44,82,101,115,111,117,114,99,101,69,120,105,115,116,115,61,48,120,54,56,44,82,101,116,114,121,61,48,120,55,44,83,97,110,105,116,121,70,97,105,108,117,114,101,61,48,120,55,98,44,83,99,104,101,109,97,86,105,111,108,97,116,105,111,110,61,48,120,56,57,44,83,101,97,114,99,104,61,48,120,49,48,44,83,101,99,117,114,105,116,121,61,48,120,57,55,44,83,101,101,107,61,48,120,54,48,44,83,101,114,118,105,99,101,85,110,97,118,97,105,108,97,98,108,101,61,48,120,57,57,44,83,101,116,70,105,101,108,100,61,48,120,51,52,44,83,101,116,86,97,108,117,101,78,111,116,65,114,114,97,121,61,48,120,98,99,44,83,101,116,86,97,108,117,101,78,111,116,70,117,110,99,116,105,111,110,61,48,120,98,97,44,83,101,116,86,97,108,117,101,78,111,116,76,111,111,107,117,112,61,48,120,98,100,44,83,101,116,86,97,108,117,101,78,111,116,78,117,109,101,114,105,99,61,48,120,98,55,44,83,101,116,86,97,108,117,101,78,111,116,79,98,106,101,99,116,61,48,120,98,57,44,83,101,116,86,97,108,117,101,78,111,116,80,111,105,110,116,101,114,61,48,120,98,98,44,83,101,116,86,97,108,117,101,78,111,116,83,116,114,105,110,103,61,48,120,98,56,44,83,101,116,86,111,108,117,109,101,61,48,120,97,98,44,83,107,105,112,61,48,120,54,44,83,109,97,108,108,77,97,115,107,61,48,120,54,99,44,83,116,97,116,101,109,101,110,116,85,110,115,97,116,105,115,102,105,101,100,61,48,120,52,102,44,83,116,114,105,110,103,70,111,114,109,97,116,61,48,120,55,102,44,83,121,110,116,97,120,61,48,120,55,102,44,83,121,115,116,101,109,67,97,108,108,61,48,120,54,98,44,83,121,115,116,101,109,67,111,114,114,117,112,116,61,48,120,50,51,44,83,121,115,116,101,109,76,111,99,107,101,100,61,48,120,51,102,44,84,97,115,107,83,116,105,108,108,69,120,105,115,116,115,61,48,120,56,55,44,84,101,114,109,105,110,97,116,101,61,48,120,57,44,84,104,114,101,97,100,65,108,114,101,97,100,121,65,99,116,105,118,101,61,48,120,97,52,44,84,104,114,101,97,100,78,111,116,76,111,99,107,101,100,61,48,120,97,57,44,84,105,109,101,79,117,116,61,48,120,49,101,44,84,114,117,101,61,48,120,48,44,85,110,98,97,108,97,110,99,101,100,88,77,76,61,48,120,57,50,44,85,110,100,101,102,105,110,101,100,70,105,101,108,100,61,48,120,52,52,44,85,110,114,101,99,111,103,110,105,115,101,100,70,105,101,108,100,84,121,112,101,61,48,120,53,98,44,85,110,115,117,112,112,111,114,116,101,100,70,105,101,108,100,61,48,120,53,100,44,85,110,115,117,112,112,111,114,116,101,100,79,119,110,101,114,61,48,120,53,50,44,85,115,101,83,117,98,67,108,97,115,115,61,48,120,97,55,44,86,105,114,116,117,97,108,86,111,108,117,109,101,61,48,120,53,56,44,87,111,117,108,100,66,108,111,99,107,61,48,120,57,51,44,87,114,105,116,101,61,48,120,49,55,44,87,114,111,110,103,67,108,97,115,115,61,48,120,56,101,44,87,114,111,110,103,79,98,106,101,99,116,84,121,112,101,61,48,120,56,101,44,87,114,111,110,103,84,121,112,101,61,48,120,97,56,44,87,114,111,110,103,86,101,114,115,105,111,110,61,48,120,49,51,10,99,46,69,86,71,58,65,78,68,82,79,73,68,61,48,120,100,44,65,80,80,61,48,120,99,44,65,85,68,73,79,61,48,120,56,44,67,76,65,83,83,61,48,120,98,44,68,73,83,80,76,65,89,61,48,120,53,44,69,78,68,61,48,120,101,44,70,73,76,69,83,89,83,84,69,77,61,48,120,49,44,71,85,73,61,48,120,52,44,72,65,82,68,87,65,82,69,61,48,120,55,44,73,79,61,48,120,54,44,78,69,84,87,79,82,75,61,48,120,50,44,80,79,87,69,82,61,48,120,97,44,83,89,83,84,69,77,61,48,120,51,44,85,83,69,82,61,48,120,57,10,99,46,70,66,75,58,67,79,80,89,95,70,73,76,69,61,48,120,50,44,68,69,76,69,84,69,95,70,73,76,69,61,48,120,51,44,77,79,86,69,95,70,73,76,69,61,48,120,49,10,99,46,70,68,58,65,76,76,79,67,61,48,120,50,48,44,65,82,82,65,89,61,48,120,49,48,48,48,44,65,82,82,65,89,83,73,90,69,61,48,120,56,48,44,66,85,70,70,69,82,61,48,120,50,48,48,44,66,85,70,83,73,90,69,61,48,120,56,48,44,66,89,84,69,61,48,120,49,48,48,48,48,48,48,44,67,80,80,61,48,120,52,48,48,48,44,67,85,83,84,79,77,61,48,120,56,48,48,48,44,68,79,85,66,76,69,61,48,120,56,48,48,48,48,48,48,48,44,68,79,85,66,76,69,82,69,83,85,76,84,61,48,120,56,48,48,48,48,49,48,48,44,69,82,82,79,82,61,48,120,56,48,48,44,70,76,65,71,83,61,48,120,52,48,44,70,76,79,65,84,61,48,120,49,48,48,48,48,48,48,48,44,70,85,78,67,84,73,79,78,61,48,120,50,48,48,48,48,48,48,44,70,85,78,67,84,73,79,78,80,84,82,61,48,120,97,48,48,48,48,48,48,44,73,61,48,120,52,48,48,44,73,78,73,84,61,48,120,52,48,48,44,73,78,84,69,71,82,65,76,61,48,120,50,44,76,65,82,71,69,61,48,120,52,48,48,48,48,48,48,44,76,65,82,71,69,82,69,83,85,76,84,61,48,120,52,48,48,48,49,48,48,44,76,79,78,71,61,48,120,52,48,48,48,48,48,48,48,44,76,79,78,71,82,69,83,85,76,84,61,48,120,52,48,48,48,48,49,48,48,44,76,79,79,75,85,80,61,48,120,56,48,44,79,66,74,69,67,84,61,48,120,49,44,79,66,74,69,67,84,73,68,61,48,120,52,48,48,48,48,48,48,49,44,79,66,74,69,67,84,80,84,82,61,48,120,56,48,48,48,48,48,49,44,80,79,73,78,84,69,82,61,48,120,56,48,48,48,48,48,48,44,80,82,73,86,65,84,69,61,48,120,49,48,48,48,48,44,80,84,82,61,48,120,56,48,48,48,48,48,48,44,80,84,82,66,85,70,70,69,82,61,48,120,56,48,48,48,50,48,48,44,80,84,82,82,69,83,85,76,84,61,48,120,56,48,48,48,49,48,48,44,80,84,82,83,73,90,69,61,48,120,56,48,44,80,84,82,95,68,79,85,66,76,69,82,69,83,85,76,84,61,48,120,56,56,48,48,48,49,48,48,44,80,84,82,95,76,65,82,71,69,82,69,83,85,76,84,61,48,120,99,48,48,48,49,48,48,44,80,84,82,95,76,79,78,71,82,69,83,85,76,84,61,48,120,52,56,48,48,48,49,48,48,44,82,61,48,120,49,48,48,44,82,69,65,68,61,48,120,49,48,48,44,82,69,81,85,73,82,69,68,61,48,120,52,44,82,69,83,79,85,82,67,69,61,48,120,50,48,48,48,44,82,69,83,85,76,84,61,48,120,49,48,48,44,82,71,66,61,48,120,56,48,48,48,48,44,82,73,61,48,120,53,48,48,44,82,87,61,48,120,51,48,48,44,83,67,65,76,69,68,61,48,120,50,48,48,48,48,48,44,83,84,82,61,48,120,56,48,48,48,48,48,44,83,84,82,73,78,71,61,48,120,56,48,48,48,48,48,44,83,84,82,82,69,83,85,76,84,61,48,120,56,48,48,49,48,48,44,83,84,82,85,67,84,61,48,120,49,48,44,83,89,78,79,78,89,77,61,48,120,50,48,48,48,48,44,83,89,83,84,69,77,61,48,120,49,48,48,48,48,44,84,65,71,83,61,48,120,52,48,48,44,85,78,83,73,71,78,69,68,61,48,120,52,48,48,48,48,44,86,65,82,73,65,66,76,69,61,48,120,50,48,48,48,48,48,48,48,44,86,65,82,84,65,71,83,61,48,120,52,48,44,86,73,82,84,85,65,76,61,48,120,56,44,86,79,73,68,61,48,120,48,44,86,79,76,65,84,73,76,69,61,48,120,48,44,87,61,48,120,50,48,48,44,87,79,82,68,61,48,120,52,48,48,48,48,48,44,87,82,73,84,69,61,48,120,50,48,48,10,99,46,70,68,66,58,67,79,77,80,82,69,83,83,95,70,73,76,69,61,48,120,50,44,68,69,67,79,77,80,82,69,83,83,95,70,73,76,69,61,48,120,49,44,68,69,67,79,77,80,82,69,83,83,95,79,66,74,69,67,84,61,48,120,52,44,82,69,77,79,86,69,95,70,73,76,69,61,48,120,51,10,99,46,70,68,76,58,70,69,69,68,66,65,67,75,61,48,120,49,10,99,46,70,68,84,58,65,67,67,69,83,83,69,68,61,48,120,50,44,65,82,67,72,73,86,69,68,61,48,120,51,44,67,82,69,65,84,69,68,61,48,120,49,44,77,79,68,73,70,73,69,68,61,48,120,48,10,99,46,70,70,82,58,65,66,79,82,84,61,48,120,50,44,67,79,78,84,73,78,85,69,61,48,120,48,44,79,75,65,89,61,48,120,48,44,83,75,73,80,61,48,120,49,10,99,46,70,76,58,65,80,80,82,79,88,73,77,65,84,69,61,48,120,49,48,44,66,85,70,70,69,82,61,48,120,52,48,44,68,69,86,73,67,69,61,48,120,52,48,48,44,68,73,82,69,67,84,79,82,89,61,48,120,56,44,69,88,67,76,85,68,69,95,70,73,76,69,83,61,48,120,49,48,48,48,44,69,88,67,76,85,68,69,95,70,79,76,68,69,82,83,61,48,120,50,48,48,48,44,70,73,76,69,61,48,120,49,48,48,44,70,79,76,68,69,82,61,48,120,56,44,76,73,78,75,61,48,120,50,48,44,76,79,79,80,61,48,120,56,48,44,78,69,87,61,48,120,50,44,82,69,65,68,61,48,120,52,44,82,69,83,69,84,95,68,65,84,69,61,48,120,50,48,48,44,83,84,82,69,65,77,61,48,120,56,48,48,44,87,82,73,84,69,61,48,120,49,10,99,46,70,79,70,58,83,77,65,82,84,95,78,65,77,69,83,61,48,120,49,10,99,46,73,68,84,89,80,69,58,70,85,78,67,84,73,79,78,61,48,120,51,44,71,76,79,66,65,76,61,48,120,50,44,77,69,83,83,65,71,69,61,48,120,49,10,99,46,74,69,84,58,65,66,83,95,88,89,61,48,120,49,98,44,65,78,65,76,79,71,50,95,88,89,61,48,120,49,54,44,65,78,65,76,79,71,50,95,90,61,48,120,49,55,44,65,78,65,76,79,71,95,88,89,61,48,120,49,52,44,65,78,65,76,79,71,95,90,61,48,120,49,53,44,66,85,84,84,79,78,95,49,61,48,120,50,44,66,85,84,84,79,78,95,49,48,61,48,120,98,44,66,85,84,84,79,78,95,50,61,48,120,51,44,66,85,84,84,79,78,95,51,61,48,120,52,44,66,85,84,84,79,78,95,52,61,48,120,53,44,66,85,84,84,79,78,95,53,61,48,120,54,44,66,85,84,84,79,78,95,54,61,48,120,55,44,66,85,84,84,79,78,95,55,61,48,120,56,44,66,85,84,84,79,78,95,56,61,48,120,57,44,66,85,84,84,79,78,95,57,61,48,120,97,44,66,85,84,84,79,78,95,83,69,76,69,67,84,61,48,120,102,44,66,85,84,84,79,78,95,83,84,65,82,84,61,48,120,101,44,67,82,79,83,83,69,68,95,73,78,61,48,120,49,99,44,67,82,79,83,83,69,68,95,79,85,84,61,48,120,49,100,44,68,69,86,73,67,69,95,84,73,76,84,95,88,89,61,48,120,49,102,44,68,69,86,73,67,69,95,84,73,76,84,95,90,61,48,120,50,48,44,68,73,71,73,84,65,76,95,88,89,61,48,120,49,44,68,73,83,80,76,65,89,95,69,68,71,69,61,48,120,50,49,44,69,78,68,61,48,120,50,50,44,76,69,70,84,95,66,85,77,80,69,82,95,49,61,48,120,49,48,44,76,69,70,84,95,66,85,77,80,69,82,95,50,61,48,120,49,49,44,76,77,66,61,48,120,50,44,77,77,66,61,48,120,52,44,80,69,78,95,84,73,76,84,95,88,89,61,48,120,49,97,44,80,82,69,83,83,85,82,69,61,48,120,49,101,44,82,73,71,72,84,95,66,85,77,80,69,82,95,49,61,48,120,49,50,44,82,73,71,72,84,95,66,85,77,80,69,82,95,50,61,48,120,49,51,44,82,77,66,61,48,120,51,44,84,82,73,71,71,69,82,95,76,69,70,84,61,48,120,99,44,84,82,73,71,71,69,82,95,82,73,71,72,84,61,48,120,100,44,87,72,69,69,76,61,48,120,49,56,44,87,72,69,69,76,95,84,73,76,84,61,48,120,49,57,10,99,46,74,84,89,80,69,58,65,78,65,76,79,71,61,48,120,50,48,44,65,78,67,72,79,82,69,68,61,48,120,50,44,66,85,84,84,79,78,61,48,120,56,48,44,67,82,79,83,83,73,78,71,61,48,120,56,44,68,66,76,95,67,76,73,67,75,61,48,120,50,48,48,44,68,73,71,73,84,65,76,61,48,120,49,48,44,68,82,65,71,71,69,68,61,48,120,52,44,68,82,65,71,95,73,84,69,77,61,48,120,56,48,48,44,69,88,84,95,77,79,86,69,77,69,78,84,61,48,120,52,48,44,77,79,86,69,77,69,78,84,61,48,120,49,48,48,44,82,69,80,69,65,84,69,68,61,48,120,52,48,48,44,83,69,67,79,78,68,65,82,89,61,48,120,49,10,99,46,75,69,89,58,65,61,48,120,49,44,65,80,79,83,84,82,79,80,72,69,61,48,120,50,98,44,65,84,61,48,120,56,97,44,66,61,48,120,50,44,66,65,67,75,61,48,120,56,54,44,66,65,67,75,83,80,65,67,69,61,48,120,54,57,44,66,65,67,75,95,83,76,65,83,72,61,48,120,50,102,44,66,82,69,65,75,61,48,120,55,98,44,67,61,48,120,51,44,67,65,76,76,61,48,120,56,55,44,67,65,77,69,82,65,61,48,120,56,57,44,67,65,78,67,69,76,61,48,120,55,97,44,67,65,80,83,95,76,79,67,75,61,48,120,52,54,44,67,76,69,65,82,61,48,120,54,101,44,67,79,77,77,65,61,48,120,50,99,44,68,61,48,120,52,44,68,69,76,69,84,69,61,48,120,54,100,44,68,79,84,61,48,120,50,100,44,68,79,87,78,61,48,120,54,49,44,69,61,48,120,53,44,69,73,71,72,84,61,48,120,50,50,44,69,78,68,61,48,120,55,50,44,69,78,68,95,67,65,76,76,61,48,120,56,56,44,69,78,84,69,82,61,48,120,54,98,44,69,81,85,65,76,83,61,48,120,50,55,44,69,83,67,65,80,69,61,48,120,54,99,44,69,88,69,67,85,84,69,61,48,120,55,52,44,70,61,48,120,54,44,70,49,61,48,120,52,99,44,70,49,48,61,48,120,53,53,44,70,49,49,61,48,120,53,54,44,70,49,50,61,48,120,53,55,44,70,49,51,61,48,120,53,56,44,70,49,52,61,48,120,53,57,44,70,49,53,61,48,120,53,97,44,70,49,54,61,48,120,53,98,44,70,49,55,61,48,120,53,99,44,70,49,56,61,48,120,56,48,44,70,49,57,61,48,120,56,49,44,70,50,61,48,120,52,100,44,70,50,48,61,48,120,56,50,44,70,51,61,48,120,52,101,44,70,52,61,48,120,52,102,44,70,53,61,48,120,53,48,44,70,54,61,48,120,53,49,44,70,55,61,48,120,53,50,44,70,56,61,48,120,53,51,44,70,57,61,48,120,53,52,44,70,73,78,68,61,48,120,55,57,44,70,73,86,69,61,48,120,49,102,44,70,79,82,87,65,82,68,61,48,120,57,48,44,70,79,85,82,61,48,120,49,101,44,71,61,48,120,55,44,72,61,48,120,56,44,72,69,76,80,61,48,120,52,51,44,72,79,77,69,61,48,120,54,102,44,73,61,48,120,57,44,73,78,83,69,82,84,61,48,120,55,53,44,74,61,48,120,97,44,75,61,48,120,98,44,76,61,48,120,99,44,76,69,70,84,61,48,120,54,51,44,76,69,78,83,95,70,79,67,85,83,61,48,120,56,99,44,76,69,83,83,95,71,82,69,65,84,69,82,61,48,120,53,102,44,76,73,83,84,95,69,78,68,61,48,120,57,54,44,76,95,65,76,84,61,48,120,52,56,44,76,95,67,79,77,77,65,78,68,61,48,120,52,97,44,76,95,67,79,78,84,82,79,76,61,48,120,52,49,44,76,95,83,72,73,70,84,61,48,120,52,52,44,76,95,83,81,85,65,82,69,61,48,120,50,56,44,77,61,48,120,100,44,77,65,67,82,79,61,48,120,53,100,44,77,69,78,85,61,48,120,55,56,44,77,73,78,85,83,61,48,120,50,54,44,77,85,84,69,61,48,120,57,50,44,78,61,48,120,101,44,78,69,88,84,61,48,120,56,101,44,78,73,78,69,61,48,120,50,51,44,78,80,95,48,61,48,120,51,49,44,78,80,95,49,61,48,120,51,50,44,78,80,95,50,61,48,120,51,51,44,78,80,95,51,61,48,120,51,52,44,78,80,95,52,61,48,120,51,53,44,78,80,95,53,61,48,120,51,54,44,78,80,95,54,61,48,120,51,55,44,78,80,95,55,61,48,120,51,56,44,78,80,95,56,61,48,120,51,57,44,78,80,95,57,61,48,120,51,97,44,78,80,95,66,65,82,61,48,120,51,100,44,78,80,95,68,69,67,73,77,65,76,61,48,120,51,102,44,78,80,95,68,73,86,73,68,69,61,48,120,52,48,44,78,80,95,68,79,84,61,48,120,51,102,44,78,80,95,69,78,84,69,82,61,48,120,55,101,44,78,80,95,77,73,78,85,83,61,48,120,51,101,44,78,80,95,77,85,76,84,73,80,76,89,61,48,120,51,98,44,78,80,95,80,76,85,83,61,48,120,51,99,44,78,80,95,80,76,85,83,95,77,73,78,85,83,61,48,120,53,101,44,78,80,95,83,69,80,65,82,65,84,79,82,61,48,120,51,100,44,78,85,77,95,76,79,67,75,61,48,120,55,99,44,79,61,48,120,102,44,79,78,69,61,48,120,49,98,44,80,61,48,120,49,48,44,80,65,71,69,95,68,79,87,78,61,48,120,55,49,44,80,65,71,69,95,85,80,61,48,120,55,48,44,80,65,85,83,69,61,48,120,54,53,44,80,69,82,73,79,68,61,48,120,50,100,44,80,76,65,89,61,48,120,57,53,44,80,76,85,83,61,48,120,56,98,44,80,79,85,78,68,61,48,120,57,52,44,80,79,87,69,82,61,48,120,54,56,44,80,82,69,86,73,79,85,83,61,48,120,56,102,44,80,82,73,78,84,61,48,120,52,55,44,80,82,84,95,83,67,82,61,48,120,55,100,44,81,61,48,120,49,49,44,82,61,48,120,49,50,44,82,69,68,79,61,48,120,55,55,44,82,69,86,69,82,83,69,95,81,85,79,84,69,61,48,120,50,53,44,82,69,87,73,78,68,61,48,120,57,49,44,82,73,71,72,84,61,48,120,54,50,44,82,95,65,76,84,61,48,120,52,57,44,82,95,67,79,77,77,65,78,68,61,48,120,52,98,44,82,95,67,79,78,84,82,79,76,61,48,120,52,50,44,82,95,83,72,73,70,84,61,48,120,52,53,44,82,95,83,81,85,65,82,69,61,48,120,50,57,44,83,61,48,120,49,51,44,83,67,82,95,76,79,67,75,61,48,120,54,52,44,83,69,76,69,67,84,61,48,120,55,51,44,83,69,77,73,95,67,79,76,79,78,61,48,120,50,97,44,83,69,86,69,78,61,48,120,50,49,44,83,73,88,61,48,120,50,48,44,83,76,65,83,72,61,48,120,50,101,44,83,76,69,69,80,61,48,120,54,55,44,83,80,65,67,69,61,48,120,51,48,44,83,84,65,82,61,48,120,57,51,44,83,84,79,80,61,48,120,56,100,44,83,89,83,82,81,61,48,120,55,102,44,84,61,48,120,49,52,44,84,65,66,61,48,120,54,97,44,84,72,82,69,69,61,48,120,49,100,44,84,87,79,61,48,120,49,99,44,85,61,48,120,49,53,44,85,78,68,79,61,48,120,55,54,44,85,80,61,48,120,54,48,44,86,61,48,120,49,54,44,86,79,76,85,77,69,95,68,79,87,78,61,48,120,56,53,44,86,79,76,85,77,69,95,85,80,61,48,120,56,52,44,87,61,48,120,49,55,44,87,65,75,69,61,48,120,54,54,44,87,73,78,95,67,79,78,84,82,79,76,61,48,120,56,51,44,88,61,48,120,49,56,44,89,61,48,120,49,57,44,90,61,48,120,49,97,44,90,69,82,79,61,48,120,50,52,10,99,46,75,81,58,65,76,84,61,48,120,54,48,44,65,76,84,71,82,61,48,120,52,48,44,67,65,80,83,95,76,79,67,75,61,48,120,52,44,67,79,77,77,65,78,68,61,48,120,49,56,48,44,67,79,78,84,82,79,76,61,48,120,49,56,44,67,84,82,76,61,48,120,49,56,44,68,69,65,68,95,75,69,89,61,48,120,49,48,48,48,48,44,73,78,70,79,61,48,120,51,99,48,52,44,73,78,83,84,82,85,67,84,73,79,78,95,75,69,89,83,61,48,120,55,56,44,76,95,65,76,84,61,48,120,50,48,44,76,95,67,79,77,77,65,78,68,61,48,120,56,48,44,76,95,67,79,78,84,82,79,76,61,48,120,56,44,76,95,67,84,82,76,61,48,120,56,44,76,95,83,72,73,70,84,61,48,120,49,44,78,79,84,95,80,82,73,78,84,65,66,76,69,61,48,120,50,48,48,48,44,78,85,77,95,76,79,67,75,61,48,120,56,48,48,48,44,78,85,77,95,80,65,68,61,48,120,50,48,48,44,80,82,69,83,83,69,68,61,48,120,49,48,48,48,44,81,85,65,76,73,70,73,69,82,83,61,48,120,49,102,98,44,82,69,76,69,65,83,69,68,61,48,120,56,48,48,44,82,69,80,69,65,84,61,48,120,52,48,48,44,82,95,65,76,84,61,48,120,52,48,44,82,95,67,79,77,77,65,78,68,61,48,120,49,48,48,44,82,95,67,79,78,84,82,79,76,61,48,120,49,48,44,82,95,67,84,82,76,61,48,120,49,48,44,82,95,83,72,73,70,84,61,48,120,50,44,83,67,82,95,76,79,67,75,61,48,120,52,48,48,48,44,83,72,73,70,84,61,48,120,51,44,87,73,78,95,67,79,78,84,82,79,76,61,48,120,50,48,48,48,48,10,99,46,76,65,89,79,85,84,58,66,65,67,75,71,82,79,85,78,68,61,48,120,56,44,69,77,66,69,68,68,69,68,61,48,120,50,48,44,70,79,82,69,71,82,79,85,78,68,61,48,120,49,48,44,73,71,78,79,82,69,95,67,85,82,83,79,82,61,48,120,56,48,44,76,69,70,84,61,48,120,50,44,76,79,67,75,61,48,120,52,48,44,82,73,71,72,84,61,48,120,52,44,83,81,85,65,82,69,61,48,120,48,44,84,73,71,72,84,61,48,120,49,44,84,73,76,69,61,48,120,49,48,48,44,87,73,68,69,61,48,120,54,10,99,46,76,68,70,58,67,72,69,67,75,95,69,88,73,83,84,83,61,48,120,49,10,99,46,76,79,67,58,68,73,82,69,67,84,79,82,89,61,48,120,49,44,70,73,76,69,61,48,120,51,44,70,79,76,68,69,82,61,48,120,49,44,86,79,76,85,77,69,61,48,120,50,10,99,46,77,65,88,58,78,65,77,69,95,76,69,78,61,48,120,50,48,10,99,46,77,69,77,58,65,85,68,73,79,61,48,120,56,44,67,65,76,76,69,82,61,48,120,56,48,48,48,48,48,44,67,79,68,69,61,48,120,49,48,44,68,65,84,65,61,48,120,48,44,68,69,76,69,84,69,61,48,120,49,48,48,48,44,69,88,67,76,85,83,73,86,69,61,48,120,56,48,48,44,72,73,68,68,69,78,61,48,120,49,48,48,48,48,48,44,77,65,78,65,71,69,68,61,48,120,49,44,78,79,95,66,76,79,67,75,61,48,120,50,48,48,48,44,78,79,95,66,76,79,67,75,73,78,71,61,48,120,50,48,48,48,44,78,79,95,67,76,69,65,82,61,48,120,52,48,48,48,48,44,78,79,95,76,79,67,75,61,48,120,52,48,48,44,78,79,95,80,79,79,76,61,48,120,50,48,44,79,66,74,69,67,84,61,48,120,50,48,48,44,82,69,65,68,61,48,120,49,48,48,48,48,44,82,69,65,68,95,87,82,73,84,69,61,48,120,51,48,48,48,48,44,83,84,82,73,78,71,61,48,120,49,48,48,44,84,69,88,84,85,82,69,61,48,120,52,44,84,77,80,95,76,79,67,75,61,48,120,52,48,44,85,78,84,82,65,67,75,69,68,61,48,120,56,48,44,86,73,68,69,79,61,48,120,50,44,87,82,73,84,69,61,48,120,50,48,48,48,48,10,99,46,77,70,70,58,65,84,84,82,73,66,61,48,120,50,48,44,67,76,79,83,69,68,61,48,120,56,48,44,67,82,69,65,84,69,61,48,120,52,44,68,69,69,80,61,48,120,49,48,48,48,44,68,69,76,69,84,69,61,48,120,56,44,70,73,76,69,61,48,120,52,48,48,44,70,79,76,68,69,82,61,48,120,50,48,48,44,77,79,68,73,70,89,61,48,120,50,44,77,79,86,69,68,61,48,120,49,48,44,79,80,69,78,69,68,61,48,120,52,48,44,82,69,65,68,61,48,120,49,44,82,69,78,65,77,69,61,48,120,49,48,44,83,69,76,70,61,48,120,56,48,48,44,85,78,77,79,85,78,84,61,48,120,49,48,48,44,87,82,73,84,69,61,48,120,50,10,99,46,77,72,70,58,68,69,70,65,85,76,84,61,48,120,50,44,83,84,65,84,73,67,61,48,120,49,44,83,84,82,85,67,84,85,82,69,61,48,120,50,10,99,46,77,79,70,58,76,73,78,75,95,76,73,66,82,65,82,89,61,48,120,49,44,83,84,65,84,73,67,61,48,120,50,44,83,89,83,84,69,77,95,80,82,79,66,69,61,48,120,52,10,99,46,77,79,86,69,58,65,76,76,61,48,120,102,44,68,79,87,78,61,48,120,49,44,76,69,70,84,61,48,120,52,44,82,73,71,72,84,61,48,120,56,44,85,80,61,48,120,50,10,99,46,77,83,70,58,65,68,68,61,48,120,56,44,65,68,68,82,69,83,83,61,48,120,49,48,44,77,69,83,83,65,71,69,95,73,68,61,48,120,50,48,44,78,79,95,68,85,80,76,73,67,65,84,69,61,48,120,52,44,85,80,68,65,84,69,61,48,120,50,44,87,65,73,84,61,48,120,49,10,99,46,77,83,71,73,68,58,65,67,84,73,79,78,61,48,120,54,51,44,66,82,69,65,75,61,48,120,54,52,44,67,79,77,77,65,78,68,61,48,120,54,53,44,67,79,82,69,95,69,78,68,61,48,120,54,52,44,68,69,66,85,71,61,48,120,53,102,44,69,86,69,78,84,61,48,120,53,101,44,70,82,69,69,61,48,120,54,50,44,81,85,73,84,61,48,120,51,101,56,44,84,72,82,69,65,68,95,65,67,84,73,79,78,61,48,120,53,98,44,84,72,82,69,65,68,95,67,65,76,76,66,65,67,75,61,48,120,53,99,44,86,65,76,73,68,65,84,69,95,80,82,79,67,69,83,83,61,48,120,53,100,44,87,65,73,84,95,70,79,82,95,79,66,74,69,67,84,83,61,48,120,53,97,10,99,46,77,84,70,58,65,78,73,77,61,48,120,56,44,82,69,76,65,84,73,86,69,61,48,120,49,48,44,88,61,48,120,49,44,89,61,48,120,50,44,90,61,48,120,52,10,99,46,78,69,84,77,83,71,58,69,78,68,61,48,120,49,44,83,84,65,82,84,61,48,120,48,10,99,46,78,70,58,67,79,76,76,69,67,84,61,48,120,56,48,44,70,82,69,69,61,48,120,49,48,44,70,82,69,69,95,79,78,95,85,78,76,79,67,75,61,48,120,56,44,73,78,73,84,73,65,76,73,83,69,68,61,48,120,50,44,73,78,84,69,71,82,65,76,61,48,120,52,44,77,69,83,83,65,71,69,61,48,120,50,48,48,44,78,65,77,69,61,48,120,56,48,48,48,48,48,48,48,44,80,82,73,86,65,84,69,61,48,120,48,44,82,69,67,76,65,83,83,69,68,61,48,120,49,48,48,44,83,73,71,78,65,76,76,69,68,61,48,120,52,48,48,44,83,85,80,80,82,69,83,83,95,76,79,71,61,48,120,52,48,44,84,73,77,69,82,95,83,85,66,61,48,120,50,48,44,85,78,73,81,85,69,61,48,120,52,48,48,48,48,48,48,48,44,85,78,84,82,65,67,75,69,68,61,48,120,49,10,99,46,79,80,70,58,65,82,71,83,61,48,120,52,48,44,68,69,84,65,73,76,61,48,120,52,44,69,82,82,79,82,61,48,120,56,48,44,77,65,88,95,68,69,80,84,72,61,48,120,50,44,77,79,68,85,76,69,95,80,65,84,72,61,48,120,52,48,48,44,79,80,84,73,79,78,83,61,48,120,49,44,80,82,73,86,73,76,69,71,69,68,61,48,120,49,48,48,44,82,79,79,84,95,80,65,84,72,61,48,120,56,48,48,44,83,67,65,78,95,77,79,68,85,76,69,83,61,48,120,49,48,48,48,44,83,72,79,87,95,69,82,82,79,82,83,61,48,120,50,48,44,83,72,79,87,95,73,79,61,48,120,49,48,44,83,72,79,87,95,77,69,77,79,82,89,61,48,120,56,44,83,89,83,84,69,77,95,80,65,84,72,61,48,120,50,48,48,10,99,46,80,69,82,77,73,84,58,65,76,76,95,68,69,76,69,84,69,61,48,120,56,56,56,44,65,76,76,95,69,88,69,67,61,48,120,52,52,52,44,65,76,76,95,82,69,65,68,61,48,120,49,49,49,44,65,76,76,95,87,82,73,84,69,61,48,120,50,50,50,44,65,82,67,72,73,86,69,61,48,120,50,48,48,48,44,68,69,76,69,84,69,61,48,120,56,44,69,86,69,82,89,79,78,69,95,65,67,67,69,83,83,61,48,120,102,102,102,44,69,86,69,82,89,79,78,69,95,68,69,76,69,84,69,61,48,120,56,56,56,44,69,86,69,82,89,79,78,69,95,69,88,69,67,61,48,120,52,52,52,44,69,86,69,82,89,79,78,69,95,82,69,65,68,61,48,120,49,49,49,44,69,86,69,82,89,79,78,69,95,82,69,65,68,87,82,73,84,69,61,48,120,51,51,51,44,69,86,69,82,89,79,78,69,95,87,82,73,84,69,61,48,120,50,50,50,44,69,88,69,67,61,48,120,52,44,71,82,79,85,80,61,48,120,102,48,44,71,82,79,85,80,73,68,61,48,120,49,48,48,48,48,44,71,82,79,85,80,95,68,69,76,69,84,69,61,48,120,56,48,44,71,82,79,85,80,95,69,88,69,67,61,48,120,52,48,44,71,82,79,85,80,95,82,69,65,68,61,48,120,49,48,44,71,82,79,85,80,95,87,82,73,84,69,61,48,120,50,48,44,72,73,68,68,69,78,61,48,120,49,48,48,48,44,73,78,72,69,82,73,84,61,48,120,50,48,48,48,48,44,78,69,84,87,79,82,75,61,48,120,56,48,48,48,48,44,79,70,70,76,73,78,69,61,48,120,52,48,48,48,48,44,79,84,72,69,82,83,61,48,120,102,48,48,44,79,84,72,69,82,83,95,68,69,76,69,84,69,61,48,120,56,48,48,44,79,84,72,69,82,83,95,69,88,69,67,61,48,120,52,48,48,44,79,84,72,69,82,83,95,82,69,65,68,61,48,120,49,48,48,44,79,84,72,69,82,83,95,87,82,73,84,69,61,48,120,50,48,48,44,80,65,83,83,87,79,82,68,61,48,120,52,48,48,48,44,82,69,65,68,61,48,120,49,44,85,83,69,82,61,48,120,102,44,85,83,69,82,73,68,61,48,120,56,48,48,48,44,85,83,69,82,95,69,88,69,67,61,48,120,52,44,85,83,69,82,95,82,69,65,68,61,48,120,49,44,85,83,69,82,95,87,82,73,84,69,61,48,120,50,44,87,82,73,84,69,61,48,120,50,10,99,46,80,77,70,58,83,89,83,84,69,77,95,78,79,95,66,82,69,65,75,61,48,120,49,10,99,46,80,84,67,58,67,82,79,83,83,72,65,73,82,61,48,120,97,44,67,85,83,84,79,77,61,48,120,49,55,44,68,69,70,65,85,76,84,61,48,120,49,44,68,82,65,71,71,65,66,76,69,61,48,120,49,56,44,69,78,68,61,48,120,49,57,44,72,65,78,68,61,48,120,49,48,44,72,65,78,68,95,76,69,70,84,61,48,120,49,49,44,72,65,78,68,95,82,73,71,72,84,61,48,120,49,50,44,73,78,86,73,83,73,66,76,69,61,48,120,49,54,44,77,65,71,78,73,70,73,69,82,61,48,120,102,44,78,79,95,67,72,65,78,71,69,61,48,120,48,44,80,65,73,78,84,66,82,85,83,72,61,48,120,49,52,44,83,73,90,69,95,66,79,84,84,79,77,61,48,120,57,44,83,73,90,69,95,66,79,84,84,79,77,95,76,69,70,84,61,48,120,50,44,83,73,90,69,95,66,79,84,84,79,77,95,82,73,71,72,84,61,48,120,51,44,83,73,90,69,95,76,69,70,84,61,48,120,54,44,83,73,90,69,95,82,73,71,72,84,61,48,120,55,44,83,73,90,69,95,84,79,80,61,48,120,56,44,83,73,90,69,95,84,79,80,95,76,69,70,84,61,48,120,52,44,83,73,90,69,95,84,79,80,95,82,73,71,72,84,61,48,120,53,44,83,73,90,73,78,71,61,48,120,99,44,83,76,69,69,80,61,48,120,98,44,83,80,76,73,84,95,72,79,82,73,90,79,78,84,65,76,61,48,120,101,44,83,80,76,73,84,95,86,69,82,84,73,67,65,76,61,48,120,100,44,83,84,79,80,61,48,120,49,53,44,84,69,88,84,61,48,120,49,51,10,99,46,82,68,70,58,65,82,67,72,73,86,69,61,48,120,50,48,48,48,44,68,65,84,69,61,48,120,50,44,70,73,76,69,61,48,120,56,44,70,73,76,69,83,61,48,120,56,44,70,79,76,68,69,82,61,48,120,49,48,44,70,79,76,68,69,82,83,61,48,120,49,48,44,72,73,68,68,69,78,61,48,120,49,48,48,44,76,73,78,75,61,48,120,52,48,44,79,80,69,78,68,73,82,61,48,120,52,48,48,48,44,80,69,82,77,73,83,83,73,79,78,83,61,48,120,52,44,81,85,65,76,73,70,73,69,68,61,48,120,50,48,48,44,81,85,65,76,73,70,89,61,48,120,50,48,48,44,82,69,65,68,95,65,76,76,61,48,120,49,102,44,82,69,65,68,95,79,78,76,89,61,48,120,49,48,48,48,44,83,73,90,69,61,48,120,49,44,83,84,82,69,65,77,61,48,120,56,48,48,44,84,65,71,83,61,48,120,56,48,44,84,73,77,69,61,48,120,50,44,86,73,82,84,85,65,76,61,48,120,52,48,48,44,86,79,76,85,77,69,61,48,120,50,48,10,99,46,82,69,83,58,67,79,78,83,79,76,69,95,70,68,61,48,120,50,44,67,79,82,69,95,73,68,76,61,48,120,56,44,67,80,85,95,83,80,69,69,68,61,48,120,49,54,44,68,73,83,80,76,65,89,95,68,82,73,86,69,82,61,48,120,53,44,69,88,67,69,80,84,73,79,78,95,72,65,78,68,76,69,82,61,48,120,49,49,44,70,82,69,69,95,77,69,77,79,82,89,61,48,120,49,55,44,70,82,69,69,95,83,87,65,80,61,48,120,49,44,74,78,73,95,69,78,86,61,48,120,101,44,75,69,89,95,83,84,65,84,69,61,48,120,51,44,76,79,71,95,68,69,80,84,72,61,48,120,100,44,76,79,71,95,76,69,86,69,76,61,48,120,97,44,77,65,88,95,80,82,79,67,69,83,83,69,83,61,48,120,99,44,78,69,84,95,80,82,79,67,69,83,83,73,78,71,61,48,120,49,50,44,79,80,69,78,95,73,78,70,79,61,48,120,49,48,44,80,65,82,69,78,84,95,67,79,78,84,69,88,84,61,48,120,57,44,80,82,73,86,73,76,69,71,69,68,61,48,120,55,44,80,82,73,86,73,76,69,71,69,68,95,85,83,69,82,61,48,120,54,44,80,82,79,67,69,83,83,95,83,84,65,84,69,61,48,120,49,51,44,83,84,65,84,73,67,95,66,85,73,76,68,61,48,120,49,56,44,84,72,82,69,65,68,95,73,68,61,48,120,102,44,84,79,84,65,76,95,77,69,77,79,82,89,61,48,120,49,52,44,84,79,84,65,76,95,83,72,65,82,69,68,95,77,69,77,79,82,89,61,48,120,98,44,84,79,84,65,76,95,83,87,65,80,61,48,120,49,53,44,85,83,69,82,95,73,68,61,48,120,52,10,99,46,82,70,68,58,65,76,76,79,87,95,82,69,67,85,82,83,73,79,78,61,48,120,50,48,44,65,76,87,65,89,83,95,67,65,76,76,61,48,120,49,48,48,44,69,88,67,69,80,84,61,48,120,50,44,82,69,65,68,61,48,120,52,44,82,69,67,65,76,76,61,48,120,56,48,44,82,69,77,79,86,69,61,48,120,56,44,83,79,67,75,69,84,61,48,120,52,48,44,83,84,79,80,95,82,69,67,85,82,83,69,61,48,120,49,48,44,87,82,73,84,69,61,48,120,49,10,99,46,82,80,58,77,79,68,85,76,69,95,80,65,84,72,61,48,120,49,44,82,79,79,84,95,80,65,84,72,61,48,120,51,44,83,89,83,84,69,77,95,80,65,84,72,61,48,120,50,10,99,46,82,83,70,58,65,80,80,82,79,88,73,77,65,84,69,61,48,120,52,44,67,65,83,69,95,83,69,78,83,73,84,73,86,69,61,48,120,50,48,44,67,72,69,67,75,95,86,73,82,84,85,65,76,61,48,120,50,44,78,79,95,68,69,69,80,95,83,67,65,78,61,48,120,56,44,78,79,95,70,73,76,69,95,67,72,69,67,75,61,48,120,49,44,80,65,84,72,61,48,120,49,48,10,99,46,83,67,70,58,69,88,73,84,95,79,78,95,69,82,82,79,82,61,48,120,49,44,76,79,71,95,65,76,76,61,48,120,50,10,99,46,83,69,69,75,58,67,85,82,82,69,78,84,61,48,120,49,44,69,78,68,61,48,120,50,44,82,69,76,65,84,73,86,69,61,48,120,51,44,83,84,65,82,84,61,48,120,48,10,99,46,83,84,80,58,65,78,73,77,61,48,120,56,44,88,61,48,120,49,44,89,61,48,120,50,44,90,61,48,120,52,10,99,46,83,84,82,58,67,65,83,69,61,48,120,49,44,77,65,84,67,72,95,67,65,83,69,61,48,120,49,44,77,65,84,67,72,95,76,69,78,61,48,120,50,44,87,73,76,68,67,65,82,68,61,48,120,52,10,99,46,83,84,84,58,70,76,79,65,84,61,48,120,50,44,72,69,88,61,48,120,51,44,78,85,77,66,69,82,61,48,120,49,44,83,84,82,73,78,71,61,48,120,52,10,99,46,84,72,70,58,65,85,84,79,95,70,82,69,69,61,48,120,49,10,99,46,84,79,73,58,65,78,68,82,79,73,68,95,65,83,83,69,84,77,71,82,61,48,120,52,44,65,78,68,82,79,73,68,95,67,76,65,83,83,61,48,120,51,44,65,78,68,82,79,73,68,95,69,78,86,61,48,120,50,44,76,79,67,65,76,95,67,65,67,72,69,61,48,120,48,44,76,79,67,65,76,95,83,84,79,82,65,71,69,61,48,120,49,10,99,46,84,83,70,58,65,84,84,65,67,72,69,68,61,48,120,49,48,48,44,68,69,84,65,67,72,69,68,61,48,120,56,48,44,70,79,82,69,73,71,78,61,48,120,49,44,76,79,71,95,65,76,76,61,48,120,50,48,44,80,73,80,69,61,48,120,50,48,48,44,80,82,73,86,73,76,69,71,69,68,61,48,120,56,44,81,85,73,69,84,61,48,120,52,48,44,82,69,83,69,84,95,80,65,84,72,61,48,120,52,44,83,72,69,76,76,61,48,120,49,48,44,87,65,73,84,61,48,120,50,10,99,46,84,83,84,65,84,69,58,80,65,85,83,69,68,61,48,120,49,44,82,85,78,78,73,78,71,61,48,120,48,44,83,84,79,80,80,73,78,71,61,48,120,50,44,84,69,82,77,73,78,65,84,69,68,61,48,120,51,10,99,46,86,65,83,58,67,65,83,69,95,83,69,78,83,73,84,73,86,69,61,48,120,102,44,67,76,79,83,69,95,68,73,82,61,48,120,54,44,67,82,69,65,84,69,95,76,73,78,75,61,48,120,49,49,44,68,69,76,69,84,69,61,48,120,51,44,68,69,82,69,71,73,83,84,69,82,61,48,120,49,44,68,82,73,86,69,82,95,83,73,90,69,61,48,120,49,50,44,71,69,84,95,68,69,86,73,67,69,95,73,78,70,79,61,48,120,98,44,71,69,84,95,73,78,70,79,61,48,120,97,44,73,68,69,78,84,73,70,89,95,70,73,76,69,61,48,120,99,44,73,71,78,79,82,69,95,70,73,76,69,61,48,120,57,44,77,65,75,69,95,68,73,82,61,48,120,100,44,79,80,69,78,95,68,73,82,61,48,120,53,44,82,69,65,68,95,76,73,78,75,61,48,120,49,48,44,82,69,78,65,77,69,61,48,120,52,44,83,65,77,69,95,70,73,76,69,61,48,120,101,44,83,67,65,78,95,68,73,82,61,48,120,50,44,84,69,83,84,95,80,65,84,72,61,48,120,55,44,87,65,84,67,72,95,80,65,84,72,61,48,120,56,10,99,46,86,76,70,58,65,80,73,61,48,120,50,48,44,66,82,65,78,67,72,61,48,120,49,44,67,82,73,84,73,67,65,76,61,48,120,56,44,68,69,84,65,73,76,61,48,120,52,48,44,69,82,82,79,82,61,48,120,50,44,70,85,78,67,84,73,79,78,61,48,120,49,48,48,44,73,78,70,79,61,48,120,49,48,44,84,82,65,67,69,61,48,120,56,48,44,87,65,82,78,73,78,71,61,48,120,52,10,99,46,86,79,76,85,77,69,58,72,73,68,68,69,78,61,48,120,52,44,80,82,73,79,82,73,84,89,61,48,120,50,44,82,69,80,76,65,67,69,61,48,120,49,44,83,89,83,84,69,77,61,48,120,56,10,0 }; +char glIDL[] = { 115,46,73,110,112,117,116,69,118,101,110,116,58,112,78,101,120,116,58,73,110,112,117,116,69,118,101,110,116,44,100,86,97,108,117,101,44,120,84,105,109,101,115,116,97,109,112,44,108,82,101,99,105,112,105,101,110,116,73,68,44,108,79,118,101,114,73,68,44,100,65,98,115,88,44,100,65,98,115,89,44,100,88,44,100,89,44,108,68,101,118,105,99,101,73,68,44,108,84,121,112,101,44,108,70,108,97,103,115,44,108,77,97,115,107,10,115,46,100,99,82,101,113,117,101,115,116,58,108,73,116,101,109,44,99,80,114,101,102,101,114,101,110,99,101,91,52,93,10,115,46,100,99,65,117,100,105,111,58,108,83,105,122,101,44,108,70,111,114,109,97,116,10,115,46,100,99,75,101,121,69,110,116,114,121,58,108,70,108,97,103,115,44,108,86,97,108,117,101,44,120,84,105,109,101,115,116,97,109,112,44,108,85,110,105,99,111,100,101,10,115,46,100,99,68,101,118,105,99,101,73,110,112,117,116,58,100,86,97,108,117,101,115,91,50,93,44,120,84,105,109,101,115,116,97,109,112,44,108,68,101,118,105,99,101,73,68,44,108,70,108,97,103,115,44,108,84,121,112,101,10,115,46,68,97,116,101,84,105,109,101,58,119,89,101,97,114,44,99,77,111,110,116,104,44,99,68,97,121,44,99,72,111,117,114,44,99,77,105,110,117,116,101,44,99,83,101,99,111,110,100,44,99,84,105,109,101,90,111,110,101,10,115,46,72,83,86,58,100,72,117,101,44,100,83,97,116,117,114,97,116,105,111,110,44,100,86,97,108,117,101,44,100,65,108,112,104,97,10,115,46,70,82,71,66,58,102,82,101,100,44,102,71,114,101,101,110,44,102,66,108,117,101,44,102,65,108,112,104,97,10,115,46,82,71,66,56,58,117,99,82,101,100,44,117,99,71,114,101,101,110,44,117,99,66,108,117,101,44,117,99,65,108,112,104,97,10,115,46,82,71,66,49,54,58,117,119,82,101,100,44,117,119,71,114,101,101,110,44,117,119,66,108,117,101,44,117,119,65,108,112,104,97,10,115,46,82,71,66,51,50,58,117,108,82,101,100,44,117,108,71,114,101,101,110,44,117,108,66,108,117,101,44,117,108,65,108,112,104,97,10,115,46,82,71,66,80,97,108,101,116,116,101,58,108,65,109,116,67,111,108,111,117,114,115,44,101,67,111,108,58,82,71,66,56,91,50,53,54,93,10,115,46,67,111,108,111,117,114,70,111,114,109,97,116,58,117,99,82,101,100,83,104,105,102,116,44,117,99,71,114,101,101,110,83,104,105,102,116,44,117,99,66,108,117,101,83,104,105,102,116,44,117,99,65,108,112,104,97,83,104,105,102,116,44,117,99,82,101,100,77,97,115,107,44,117,99,71,114,101,101,110,77,97,115,107,44,117,99,66,108,117,101,77,97,115,107,44,117,99,65,108,112,104,97,77,97,115,107,44,117,99,82,101,100,80,111,115,44,117,99,71,114,101,101,110,80,111,115,44,117,99,66,108,117,101,80,111,115,44,117,99,65,108,112,104,97,80,111,115,44,117,99,66,105,116,115,80,101,114,80,105,120,101,108,10,115,46,67,108,105,112,82,101,99,116,97,110,103,108,101,58,108,76,101,102,116,44,108,84,111,112,44,108,82,105,103,104,116,44,108,66,111,116,116,111,109,10,115,46,69,100,103,101,115,58,108,76,101,102,116,44,108,84,111,112,44,108,82,105,103,104,116,44,108,66,111,116,116,111,109,10,115,46,79,98,106,101,99,116,83,105,103,110,97,108,58,111,79,98,106,101,99,116,10,115,46,112,102,66,97,115,101,54,52,68,101,99,111,100,101,58,117,99,83,116,101,112,44,117,99,80,108,97,105,110,67,104,97,114,44,117,99,66,105,116,10,115,46,112,102,66,97,115,101,54,52,69,110,99,111,100,101,58,117,99,83,116,101,112,44,117,99,82,101,115,117,108,116,44,108,83,116,101,112,67,111,117,110,116,10,115,46,70,117,110,99,116,105,111,110,70,105,101,108,100,58,115,78,97,109,101,44,117,108,84,121,112,101,10,115,46,70,117,110,99,116,105,111,110,58,112,65,100,100,114,101,115,115,44,115,78,97,109,101,44,112,65,114,103,115,58,70,117,110,99,116,105,111,110,70,105,101,108,100,10,115,46,70,105,101,108,100,65,114,114,97,121,58,115,78,97,109,101,44,112,71,101,116,70,105,101,108,100,44,112,83,101,116,70,105,101,108,100,44,109,65,114,103,44,117,108,70,108,97,103,115,10,115,46,70,105,101,108,100,68,101,102,58,115,78,97,109,101,44,108,86,97,108,117,101,10,115,46,83,121,115,116,101,109,83,116,97,116,101,58,115,80,108,97,116,102,111,114,109,44,108,67,111,110,115,111,108,101,70,68,44,108,83,116,97,103,101,10,115,46,86,97,114,105,97,98,108,101,58,117,108,84,121,112,101,44,108,85,110,117,115,101,100,44,120,76,97,114,103,101,44,100,68,111,117,98,108,101,44,112,80,111,105,110,116,101,114,10,115,46,65,99,116,105,111,110,65,114,114,97,121,58,112,82,111,117,116,105,110,101,44,108,65,99,116,105,111,110,67,111,100,101,10,115,46,65,99,116,105,111,110,84,97,98,108,101,58,117,108,72,97,115,104,44,108,83,105,122,101,44,115,78,97,109,101,44,112,65,114,103,115,58,70,117,110,99,116,105,111,110,70,105,101,108,100,10,115,46,67,104,105,108,100,69,110,116,114,121,58,108,79,98,106,101,99,116,73,68,44,117,108,67,108,97,115,115,73,68,10,115,46,77,101,115,115,97,103,101,58,120,84,105,109,101,44,108,85,73,68,44,108,84,121,112,101,44,108,83,105,122,101,10,115,46,77,101,109,73,110,102,111,58,112,83,116,97,114,116,44,108,79,98,106,101,99,116,73,68,44,117,108,83,105,122,101,44,108,70,108,97,103,115,44,108,77,101,109,111,114,121,73,68,44,119,65,99,99,101,115,115,67,111,117,110,116,10,115,46,67,111,109,112,114,101,115,115,105,111,110,70,101,101,100,98,97,99,107,58,108,70,101,101,100,98,97,99,107,73,68,44,108,73,110,100,101,120,44,115,80,97,116,104,44,115,68,101,115,116,44,120,80,114,111,103,114,101,115,115,44,120,79,114,105,103,105,110,97,108,83,105,122,101,44,120,67,111,109,112,114,101,115,115,101,100,83,105,122,101,44,119,89,101,97,114,44,119,77,111,110,116,104,44,119,68,97,121,44,119,72,111,117,114,44,119,77,105,110,117,116,101,44,119,83,101,99,111,110,100,10,115,46,67,111,109,112,114,101,115,115,101,100,73,116,101,109,58,120,79,114,105,103,105,110,97,108,83,105,122,101,44,120,67,111,109,112,114,101,115,115,101,100,83,105,122,101,44,112,78,101,120,116,58,67,111,109,112,114,101,115,115,101,100,73,116,101,109,44,115,80,97,116,104,44,108,80,101,114,109,105,115,115,105,111,110,115,44,108,85,115,101,114,73,68,44,108,71,114,111,117,112,73,68,44,108,79,116,104,101,114,115,73,68,44,108,70,108,97,103,115,44,101,67,114,101,97,116,101,100,58,68,97,116,101,84,105,109,101,44,101,77,111,100,105,102,105,101,100,58,68,97,116,101,84,105,109,101,10,115,46,70,105,108,101,73,110,102,111,58,120,83,105,122,101,44,120,84,105,109,101,83,116,97,109,112,44,112,78,101,120,116,58,70,105,108,101,73,110,102,111,44,115,78,97,109,101,44,108,70,108,97,103,115,44,108,80,101,114,109,105,115,115,105,111,110,115,44,108,85,115,101,114,73,68,44,108,71,114,111,117,112,73,68,44,101,67,114,101,97,116,101,100,58,68,97,116,101,84,105,109,101,44,101,77,111,100,105,102,105,101,100,58,68,97,116,101,84,105,109,101,10,115,46,68,105,114,73,110,102,111,58,112,73,110,102,111,58,70,105,108,101,73,110,102,111,10,115,46,70,105,108,101,70,101,101,100,98,97,99,107,58,120,83,105,122,101,44,120,80,111,115,105,116,105,111,110,44,115,80,97,116,104,44,115,68,101,115,116,44,108,70,101,101,100,98,97,99,107,73,68,44,99,82,101,115,101,114,118,101,100,91,51,50,93,10,115,46,70,105,101,108,100,58,109,65,114,103,44,112,71,101,116,86,97,108,117,101,44,112,83,101,116,86,97,108,117,101,44,112,87,114,105,116,101,86,97,108,117,101,44,115,78,97,109,101,44,117,108,70,105,101,108,100,73,68,44,117,119,79,102,102,115,101,116,44,117,119,73,110,100,101,120,44,117,108,70,108,97,103,115,10,99,46,65,67,58,65,99,116,105,118,97,116,101,61,48,120,50,44,67,108,101,97,114,61,48,120,52,44,67,108,105,112,98,111,97,114,100,61,48,120,50,100,44,67,111,112,121,68,97,116,97,61,48,120,55,44,67,117,115,116,111,109,61,48,120,51,52,44,68,97,116,97,70,101,101,100,61,48,120,56,44,68,101,97,99,116,105,118,97,116,101,61,48,120,57,44,68,105,115,97,98,108,101,61,48,120,50,102,44,68,114,97,103,68,114,111,112,61,48,120,49,48,44,68,114,97,119,61,48,120,97,44,69,78,68,61,48,120,51,53,44,69,110,97,98,108,101,61,48,120,51,48,44,70,108,117,115,104,61,48,120,98,44,70,111,99,117,115,61,48,120,99,44,70,114,101,101,61,48,120,100,44,70,114,101,101,87,97,114,110,105,110,103,61,48,120,53,44,71,101,116,86,97,114,61,48,120,102,44,72,105,100,101,61,48,120,49,49,44,73,110,105,116,61,48,120,49,50,44,76,111,99,107,61,48,120,49,51,44,76,111,115,116,70,111,99,117,115,61,48,120,49,52,44,77,111,118,101,61,48,120,49,53,44,77,111,118,101,84,111,66,97,99,107,61,48,120,49,54,44,77,111,118,101,84,111,70,114,111,110,116,61,48,120,49,55,44,77,111,118,101,84,111,80,111,105,110,116,61,48,120,51,50,44,78,101,119,67,104,105,108,100,61,48,120,49,56,44,78,101,119,79,98,106,101,99,116,61,48,120,49,97,44,78,101,119,79,119,110,101,114,61,48,120,49,57,44,78,101,120,116,61,48,120,50,57,44,80,114,101,118,61,48,120,50,97,44,81,117,101,114,121,61,48,120,49,99,44,82,101,97,100,61,48,120,49,100,44,82,101,100,105,109,101,110,115,105,111,110,61,48,120,51,49,44,82,101,100,111,61,48,120,49,98,44,82,101,102,114,101,115,104,61,48,120,50,101,44,82,101,110,97,109,101,61,48,120,49,101,44,82,101,115,101,116,61,48,120,49,102,44,82,101,115,105,122,101,61,48,120,50,48,44,83,97,118,101,73,109,97,103,101,61,48,120,50,49,44,83,97,118,101,83,101,116,116,105,110,103,115,61,48,120,101,44,83,97,118,101,84,111,79,98,106,101,99,116,61,48,120,50,50,44,83,99,114,111,108,108,61,48,120,50,51,44,83,99,114,111,108,108,84,111,80,111,105,110,116,61,48,120,51,51,44,83,101,101,107,61,48,120,50,52,44,83,101,108,101,99,116,65,114,101,97,61,48,120,51,44,83,101,116,70,105,101,108,100,61,48,120,50,99,44,83,101,116,86,97,114,61,48,120,50,53,44,83,104,111,119,61,48,120,50,54,44,83,105,103,110,97,108,61,48,120,49,44,83,111,114,116,61,48,120,54,44,85,110,100,111,61,48,120,50,55,44,85,110,108,111,99,107,61,48,120,50,56,44,87,114,105,116,101,61,48,120,50,98,10,99,46,65,76,73,71,78,58,66,79,84,84,79,77,61,48,120,50,48,44,67,69,78,84,69,82,61,48,120,99,44,72,79,82,73,90,79,78,84,65,76,61,48,120,52,44,76,69,70,84,61,48,120,49,44,77,73,68,68,76,69,61,48,120,99,44,82,73,71,72,84,61,48,120,50,44,84,79,80,61,48,120,49,48,44,86,69,82,84,73,67,65,76,61,48,120,56,10,99,46,67,67,70,58,65,85,68,73,79,61,48,120,50,48,48,44,67,79,77,77,65,78,68,61,48,120,49,44,68,65,84,65,61,48,120,52,48,48,44,68,82,65,87,65,66,76,69,61,48,120,50,44,69,70,70,69,67,84,61,48,120,52,44,70,73,76,69,83,89,83,84,69,77,61,48,120,56,44,71,82,65,80,72,73,67,83,61,48,120,49,48,44,71,85,73,61,48,120,50,48,44,73,79,61,48,120,52,48,44,77,73,83,67,61,48,120,56,48,48,44,77,85,76,84,73,77,69,68,73,65,61,48,120,50,48,48,48,44,78,69,84,87,79,82,75,61,48,120,49,48,48,48,44,83,89,83,84,69,77,61,48,120,56,48,44,84,79,79,76,61,48,120,49,48,48,10,99,46,67,70,58,68,69,70,76,65,84,69,61,48,120,51,44,71,90,73,80,61,48,120,49,44,90,76,73,66,61,48,120,50,10,99,46,67,76,70,58,78,79,95,79,87,78,69,82,83,72,73,80,61,48,120,50,44,80,82,79,77,79,84,69,95,73,78,84,69,71,82,65,76,61,48,120,49,10,99,46,67,76,73,80,77,79,68,69,58,67,79,80,89,61,48,120,50,44,67,85,84,61,48,120,49,44,80,65,83,84,69,61,48,120,52,10,99,46,67,77,70,58,65,80,80,76,89,95,83,69,67,85,82,73,84,89,61,48,120,50,48,44,67,82,69,65,84,69,95,70,73,76,69,61,48,120,52,44,78,69,87,61,48,120,50,44,78,79,95,76,73,78,75,83,61,48,120,49,48,44,80,65,83,83,87,79,82,68,61,48,120,49,44,82,69,65,68,95,79,78,76,89,61,48,120,56,10,99,46,67,78,70,58,65,85,84,79,95,83,65,86,69,61,48,120,50,44,78,69,87,61,48,120,56,44,79,80,84,73,79,78,65,76,95,70,73,76,69,83,61,48,120,52,44,83,84,82,73,80,95,81,85,79,84,69,83,61,48,120,49,10,99,46,68,65,84,65,58,65,85,68,73,79,61,48,120,53,44,67,79,78,84,69,78,84,61,48,120,98,44,68,69,86,73,67,69,95,73,78,80,85,84,61,48,120,51,44,70,73,76,69,61,48,120,97,44,73,77,65,71,69,61,48,120,55,44,73,78,80,85,84,95,82,69,65,68,89,61,48,120,99,44,82,65,87,61,48,120,50,44,82,69,67,69,73,80,84,61,48,120,57,44,82,69,67,79,82,68,61,48,120,54,44,82,69,81,85,69,83,84,61,48,120,56,44,84,69,88,84,61,48,120,49,44,88,77,76,61,48,120,52,10,99,46,68,69,86,73,67,69,58,67,79,77,80,65,67,84,95,68,73,83,67,61,48,120,49,44,70,76,79,80,80,89,95,68,73,83,75,61,48,120,52,44,72,65,82,68,95,68,73,83,75,61,48,120,50,44,77,69,77,79,82,89,61,48,120,49,48,48,48,44,77,79,68,69,77,61,48,120,50,48,48,48,44,78,69,84,87,79,82,75,61,48,120,56,48,44,80,82,73,78,84,69,82,61,48,120,50,48,48,44,80,82,73,78,84,69,82,95,51,68,61,48,120,56,48,48,48,44,82,69,65,68,61,48,120,56,44,82,69,77,79,86,65,66,76,69,61,48,120,50,48,44,82,69,77,79,86,69,65,66,76,69,61,48,120,50,48,44,83,67,65,78,78,69,82,61,48,120,52,48,48,44,83,67,65,78,78,69,82,95,51,68,61,48,120,49,48,48,48,48,44,83,79,70,84,87,65,82,69,61,48,120,52,48,44,84,65,80,69,61,48,120,49,48,48,44,84,69,77,80,79,82,65,82,89,61,48,120,56,48,48,44,85,83,66,61,48,120,52,48,48,48,44,87,82,73,84,69,61,48,120,49,48,10,99,46,68,77,70,58,70,73,88,69,68,95,67,69,78,84,69,82,95,88,61,48,120,49,48,48,48,48,48,44,70,73,88,69,68,95,67,69,78,84,69,82,95,89,61,48,120,50,48,48,48,48,48,44,70,73,88,69,68,95,68,69,80,84,72,61,48,120,49,48,48,48,44,70,73,88,69,68,95,72,69,73,71,72,84,61,48,120,49,48,48,44,70,73,88,69,68,95,82,65,68,73,85,83,61,48,120,50,48,50,48,48,48,48,44,70,73,88,69,68,95,82,65,68,73,85,83,95,88,61,48,120,50,48,48,48,48,44,70,73,88,69,68,95,82,65,68,73,85,83,95,89,61,48,120,50,48,48,48,48,48,48,44,70,73,88,69,68,95,87,73,68,84,72,61,48,120,50,48,48,44,70,73,88,69,68,95,88,61,48,120,52,44,70,73,88,69,68,95,88,95,79,70,70,83,69,84,61,48,120,52,48,44,70,73,88,69,68,95,89,61,48,120,56,44,70,73,88,69,68,95,89,95,79,70,70,83,69,84,61,48,120,56,48,44,70,73,88,69,68,95,90,61,48,120,52,48,48,48,44,72,69,73,71,72,84,61,48,120,53,48,48,44,72,69,73,71,72,84,95,70,76,65,71,83,61,48,120,53,97,48,44,72,79,82,73,90,79,78,84,65,76,95,70,76,65,71,83,61,48,120,97,53,53,44,83,67,65,76,69,68,95,67,69,78,84,69,82,95,88,61,48,120,52,48,48,48,48,44,83,67,65,76,69,68,95,67,69,78,84,69,82,95,89,61,48,120,56,48,48,48,48,44,83,67,65,76,69,68,95,68,69,80,84,72,61,48,120,50,48,48,48,44,83,67,65,76,69,68,95,72,69,73,71,72,84,61,48,120,52,48,48,44,83,67,65,76,69,68,95,82,65,68,73,85,83,61,48,120,49,48,49,48,48,48,48,44,83,67,65,76,69,68,95,82,65,68,73,85,83,95,88,61,48,120,49,48,48,48,48,44,83,67,65,76,69,68,95,82,65,68,73,85,83,95,89,61,48,120,49,48,48,48,48,48,48,44,83,67,65,76,69,68,95,87,73,68,84,72,61,48,120,56,48,48,44,83,67,65,76,69,68,95,88,61,48,120,49,44,83,67,65,76,69,68,95,88,95,79,70,70,83,69,84,61,48,120,49,48,44,83,67,65,76,69,68,95,89,61,48,120,50,44,83,67,65,76,69,68,95,89,95,79,70,70,83,69,84,61,48,120,50,48,44,83,67,65,76,69,68,95,90,61,48,120,56,48,48,48,44,83,84,65,84,85,83,95,67,72,65,78,71,69,61,48,120,99,48,48,48,48,48,44,83,84,65,84,85,83,95,67,72,65,78,71,69,95,72,61,48,120,52,48,48,48,48,48,44,83,84,65,84,85,83,95,67,72,65,78,71,69,95,86,61,48,120,56,48,48,48,48,48,44,86,69,82,84,73,67,65,76,95,70,76,65,71,83,61,48,120,53,97,97,44,87,73,68,84,72,61,48,120,97,48,48,44,87,73,68,84,72,95,70,76,65,71,83,61,48,120,97,53,48,44,88,61,48,120,53,44,88,95,79,70,70,83,69,84,61,48,120,53,48,44,89,61,48,120,97,44,89,95,79,70,70,83,69,84,61,48,120,97,48,10,99,46,68,82,76,58,68,79,87,78,61,48,120,49,44,69,65,83,84,61,48,120,50,44,76,69,70,84,61,48,120,51,44,78,79,82,84,72,61,48,120,48,44,78,79,82,84,72,95,69,65,83,84,61,48,120,52,44,78,79,82,84,72,95,87,69,83,84,61,48,120,53,44,82,73,71,72,84,61,48,120,50,44,83,79,85,84,72,61,48,120,49,44,83,79,85,84,72,95,69,65,83,84,61,48,120,54,44,83,79,85,84,72,95,87,69,83,84,61,48,120,55,44,85,80,61,48,120,48,44,87,69,83,84,61,48,120,51,10,99,46,69,68,71,69,58,65,76,76,61,48,120,102,102,44,66,79,84,84,79,77,61,48,120,56,44,66,79,84,84,79,77,95,76,69,70,84,61,48,120,52,48,44,66,79,84,84,79,77,95,82,73,71,72,84,61,48,120,56,48,44,76,69,70,84,61,48,120,50,44,82,73,71,72,84,61,48,120,52,44,84,79,80,61,48,120,49,44,84,79,80,95,76,69,70,84,61,48,120,49,48,44,84,79,80,95,82,73,71,72,84,61,48,120,50,48,10,99,46,69,82,70,58,78,111,116,105,102,105,101,100,61,48,120,52,48,48,48,48,48,48,48,10,99,46,69,82,82,58,65,99,99,101,115,115,77,101,109,111,114,121,61,48,120,52,98,44,65,99,99,101,115,115,79,98,106,101,99,116,61,48,120,53,51,44,65,99,99,101,115,115,83,101,109,97,112,104,111,114,101,61,48,120,55,51,44,65,99,116,105,118,97,116,101,61,48,120,52,50,44,65,100,100,67,108,97,115,115,61,48,120,52,49,44,65,108,108,111,99,77,101,109,111,114,121,61,48,120,53,52,44,65,108,108,111,99,83,101,109,97,112,104,111,114,101,61,48,120,55,50,44,65,108,114,101,97,100,121,68,101,102,105,110,101,100,61,48,120,98,54,44,65,108,114,101,97,100,121,76,111,99,107,101,100,61,48,120,57,99,44,65,114,103,115,61,48,120,49,52,44,65,114,114,97,121,70,117,108,108,61,48,120,50,100,44,66,117,102,102,101,114,79,118,101,114,102,108,111,119,61,48,120,53,99,44,66,117,115,121,61,48,120,56,98,44,67,97,110,99,101,108,108,101,100,61,48,120,51,44,67,97,114,100,82,101,97,100,101,114,85,110,97,118,97,105,108,97,98,108,101,61,48,120,57,102,44,67,97,114,100,82,101,97,100,101,114,85,110,107,110,111,119,110,61,48,120,57,100,44,67,111,109,112,114,101,115,115,105,111,110,61,48,120,97,100,44,67,111,110,110,101,99,116,105,111,110,65,98,111,114,116,101,100,61,48,120,56,99,44,67,111,110,110,101,99,116,105,111,110,82,101,102,117,115,101,100,61,48,120,56,51,44,67,111,110,115,116,114,97,105,110,116,86,105,111,108,97,116,105,111,110,61,48,120,56,56,44,67,111,110,116,105,110,117,101,61,48,120,53,44,67,111,114,101,86,101,114,115,105,111,110,61,48,120,50,54,44,67,114,101,97,116,101,70,105,108,101,61,48,120,55,52,44,67,114,101,97,116,101,79,98,106,101,99,116,61,48,120,54,53,44,67,114,101,97,116,101,82,101,115,111,117,114,99,101,61,48,120,98,50,44,68,97,116,97,83,105,122,101,61,48,120,56,97,44,68,101,97,99,116,105,118,97,116,101,100,61,48,120,57,97,44,68,101,97,100,76,111,99,107,61,48,120,51,101,44,68,101,99,111,109,112,114,101,115,115,105,111,110,61,48,120,97,99,44,68,101,108,101,116,101,70,105,108,101,61,48,120,55,53,44,68,105,114,69,109,112,116,121,61,48,120,56,44,68,105,115,99,111,110,110,101,99,116,101,100,61,48,120,56,54,44,68,111,78,111,116,69,120,112,117,110,103,101,61,48,120,51,48,44,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,55,56,44,68,111,117,98,108,101,73,110,105,116,61,48,120,52,51,44,68,114,97,119,61,48,120,52,56,44,69,78,68,61,48,120,98,101,44,69,109,112,116,121,83,116,114,105,110,103,61,48,120,54,100,44,69,110,100,79,102,70,105,108,101,61,48,120,55,101,44,69,110,116,114,121,77,105,115,115,105,110,103,72,101,97,100,101,114,61,48,120,51,97,44,69,120,97,109,105,110,101,70,97,105,108,101,100,61,48,120,49,57,44,69,120,99,101,112,116,105,111,110,61,48,120,97,51,44,69,120,99,101,112,116,105,111,110,84,104,114,101,115,104,111,108,100,61,48,120,57,44,69,120,99,108,117,115,105,118,101,68,101,110,105,101,100,61,48,120,53,51,44,69,120,101,99,86,105,111,108,97,116,105,111,110,61,48,120,56,102,44,69,120,105,115,116,115,61,48,120,55,97,44,69,120,112,101,99,116,101,100,70,105,108,101,61,48,120,54,102,44,69,120,112,101,99,116,101,100,70,111,108,100,101,114,61,48,120,97,101,44,70,97,105,108,101,100,61,48,120,100,44,70,97,108,115,101,61,48,120,49,44,70,105,101,108,100,78,111,116,83,101,116,61,48,120,52,52,44,70,105,101,108,100,83,101,97,114,99,104,61,48,120,51,50,44,70,105,101,108,100,84,121,112,101,77,105,115,109,97,116,99,104,61,48,120,53,97,44,70,105,108,101,61,48,120,101,44,70,105,108,101,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,49,50,44,70,105,108,101,69,120,105,115,116,115,61,48,120,54,51,44,70,105,108,101,78,111,116,70,111,117,110,100,61,48,120,49,50,44,70,105,108,101,82,101,97,100,70,108,97,103,61,48,120,52,54,44,70,105,108,101,87,114,105,116,101,70,108,97,103,61,48,120,52,55,44,70,105,110,105,115,104,101,100,61,48,120,55,101,44,70,117,110,99,116,105,111,110,61,48,120,98,53,44,71,101,116,70,105,101,108,100,61,48,120,53,54,44,71,101,116,83,117,114,102,97,99,101,73,110,102,111,61,48,120,55,100,44,72,111,115,116,78,111,116,70,111,117,110,100,61,48,120,56,49,44,72,111,115,116,85,110,114,101,97,99,104,97,98,108,101,61,48,120,56,53,44,73,100,101,110,116,105,99,97,108,80,97,116,104,115,61,48,120,55,57,44,73,108,108,101,103,97,108,65,99,116,105,111,110,65,116,116,101,109,112,116,61,48,120,51,57,44,73,108,108,101,103,97,108,65,99,116,105,111,110,73,68,61,48,120,51,55,44,73,108,108,101,103,97,108,65,100,100,114,101,115,115,61,48,120,57,49,44,73,108,108,101,103,97,108,77,101,116,104,111,100,73,68,61,48,120,51,54,44,73,109,109,117,116,97,98,108,101,61,48,120,97,102,44,73,110,85,115,101,61,48,120,99,44,73,110,105,116,61,48,120,50,49,44,73,110,105,116,77,111,100,117,108,101,61,48,120,49,49,44,73,110,112,117,116,79,117,116,112,117,116,61,48,120,57,52,44,73,110,116,101,103,114,105,116,121,86,105,111,108,97,116,105,111,110,61,48,120,56,56,44,73,110,118,97,108,105,100,68,97,116,97,61,48,120,102,44,73,110,118,97,108,105,100,68,105,109,101,110,115,105,111,110,61,48,120,53,57,44,73,110,118,97,108,105,100,72,84,84,80,82,101,115,112,111,110,115,101,61,48,120,97,49,44,73,110,118,97,108,105,100,72,97,110,100,108,101,61,48,120,57,54,44,73,110,118,97,108,105,100,79,98,106,101,99,116,61,48,120,56,101,44,73,110,118,97,108,105,100,80,97,116,104,61,48,120,51,51,44,73,110,118,97,108,105,100,82,101,102,101,114,101,110,99,101,61,48,120,97,50,44,73,110,118,97,108,105,100,83,116,97,116,101,61,48,120,56,48,44,73,110,118,97,108,105,100,85,82,73,61,48,120,56,50,44,73,110,118,97,108,105,100,86,97,108,117,101,61,48,120,57,56,44,76,105,109,105,116,101,100,83,117,99,99,101,115,115,61,48,120,50,44,76,105,115,116,67,104,105,108,100,114,101,110,61,48,120,54,97,44,76,111,97,100,77,111,100,117,108,101,61,48,120,57,53,44,76,111,99,107,61,48,120,49,56,44,76,111,99,107,70,97,105,108,101,100,61,48,120,49,56,44,76,111,99,107,77,117,116,101,120,61,48,120,97,97,44,76,111,99,107,82,101,113,117,105,114,101,100,61,48,120,57,98,44,76,111,99,107,101,100,61,48,120,57,99,44,76,111,111,112,61,48,120,54,50,44,76,111,115,116,67,108,97,115,115,61,48,120,49,97,44,76,111,115,116,79,119,110,101,114,61,48,120,50,102,44,76,111,119,67,97,112,97,99,105,116,121,61,48,120,50,48,44,77,97,114,107,101,100,70,111,114,68,101,108,101,116,105,111,110,61,48,120,51,53,44,77,101,109,111,114,121,61,48,120,49,100,44,77,101,109,111,114,121,67,111,114,114,117,112,116,61,48,120,51,49,44,77,101,109,111,114,121,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,51,100,44,77,101,109,111,114,121,73,110,102,111,61,48,120,54,54,44,77,105,115,109,97,116,99,104,61,48,120,53,101,44,77,105,115,115,105,110,103,67,108,97,115,115,61,48,120,52,53,44,77,105,115,115,105,110,103,67,108,97,115,115,78,97,109,101,61,48,120,50,97,44,77,105,115,115,105,110,103,80,97,116,104,61,48,120,52,99,44,77,111,100,117,108,101,73,110,105,116,70,97,105,108,101,100,61,48,120,51,99,44,77,111,100,117,108,101,77,105,115,115,105,110,103,73,110,105,116,61,48,120,51,98,44,77,111,100,117,108,101,77,105,115,115,105,110,103,78,97,109,101,61,48,120,52,48,44,77,111,100,117,108,101,79,112,101,110,70,97,105,108,101,100,61,48,120,51,56,44,78,101,101,100,79,119,110,101,114,61,48,120,50,52,44,78,101,101,100,87,105,100,116,104,72,101,105,103,104,116,61,48,120,50,55,44,78,101,103,97,116,105,118,101,67,108,97,115,115,73,68,61,48,120,50,57,44,78,101,103,97,116,105,118,101,83,117,98,67,108,97,115,115,73,68,61,48,120,50,56,44,78,101,116,119,111,114,107,85,110,114,101,97,99,104,97,98,108,101,61,48,120,56,52,44,78,101,119,79,98,106,101,99,116,61,48,120,53,53,44,78,111,65,99,116,105,111,110,61,48,120,49,98,44,78,111,68,97,116,97,61,48,120,49,53,44,78,111,70,105,101,108,100,65,99,99,101,115,115,61,48,120,53,55,44,78,111,77,97,116,99,104,105,110,103,79,98,106,101,99,116,61,48,120,52,97,44,78,111,77,101,100,105,97,73,110,115,101,114,116,101,100,61,48,120,57,101,44,78,111,77,101,109,111,114,121,61,48,120,97,44,78,111,77,101,116,104,111,100,115,61,48,120,52,57,44,78,111,80,101,114,109,105,115,115,105,111,110,61,48,120,50,50,44,78,111,80,111,105,110,116,101,114,61,48,120,98,44,78,111,83,101,97,114,99,104,82,101,115,117,108,116,61,48,120,52,101,44,78,111,83,116,97,116,115,61,48,120,49,102,44,78,111,83,117,112,112,111,114,116,61,48,120,49,99,44,78,111,116,70,111,117,110,100,61,48,120,49,48,44,78,111,116,73,110,105,116,105,97,108,105,115,101,100,61,48,120,54,55,44,78,111,116,76,111,99,107,101,100,61,48,120,52,100,44,78,111,116,80,111,115,115,105,98,108,101,61,48,120,98,51,44,78,111,116,104,105,110,103,68,111,110,101,61,48,120,52,44,78,111,116,105,102,105,101,100,61,48,120,52,48,48,48,48,48,48,48,44,78,117,108,108,65,114,103,115,61,48,120,56,100,44,79,98,106,101,99,116,67,111,114,114,117,112,116,61,48,120,53,48,44,79,98,106,101,99,116,69,120,105,115,116,115,61,48,120,54,101,44,79,98,115,111,108,101,116,101,61,48,120,98,49,44,79,98,116,97,105,110,77,101,116,104,111,100,61,48,120,50,99,44,79,107,97,121,61,48,120,48,44,79,112,101,110,70,105,108,101,61,48,120,55,54,44,79,112,101,110,71,76,61,48,120,97,53,44,79,117,116,79,102,66,111,117,110,100,115,61,48,120,53,102,44,79,117,116,79,102,68,97,116,97,61,48,120,55,101,44,79,117,116,79,102,82,97,110,103,101,61,48,120,50,98,44,79,117,116,79,102,83,112,97,99,101,61,48,120,55,99,44,79,117,116,115,105,100,101,77,97,105,110,84,104,114,101,97,100,61,48,120,97,54,44,79,119,110,101,114,78,101,101,100,115,66,105,116,109,97,112,61,48,120,50,53,44,79,119,110,101,114,80,97,115,115,84,104,114,111,117,103,104,61,48,120,53,49,44,80,101,114,109,105,115,115,105,111,110,68,101,110,105,101,100,61,48,120,50,50,44,80,101,114,109,105,115,115,105,111,110,115,61,48,120,50,50,44,80,114,111,120,121,83,83,76,84,117,110,110,101,108,61,48,120,97,48,44,81,117,101,114,121,61,48,120,50,101,44,82,101,97,100,61,48,120,49,54,44,82,101,97,100,70,105,108,101,84,111,66,117,102,102,101,114,61,48,120,98,48,44,82,101,97,100,79,110,108,121,61,48,120,55,55,44,82,101,97,108,108,111,99,77,101,109,111,114,121,61,48,120,54,49,44,82,101,99,117,114,115,105,111,110,61,48,120,57,48,44,82,101,100,105,109,101,110,115,105,111,110,61,48,120,55,49,44,82,101,102,114,101,115,104,61,48,120,54,57,44,82,101,115,105,122,101,61,48,120,55,48,44,82,101,115,111,108,118,101,80,97,116,104,61,48,120,54,52,44,82,101,115,111,108,118,101,83,121,109,98,111,108,61,48,120,98,52,44,82,101,115,111,117,114,99,101,69,120,105,115,116,115,61,48,120,54,56,44,82,101,116,114,121,61,48,120,55,44,83,97,110,105,116,121,70,97,105,108,117,114,101,61,48,120,55,98,44,83,99,104,101,109,97,86,105,111,108,97,116,105,111,110,61,48,120,56,57,44,83,101,97,114,99,104,61,48,120,49,48,44,83,101,99,117,114,105,116,121,61,48,120,57,55,44,83,101,101,107,61,48,120,54,48,44,83,101,114,118,105,99,101,85,110,97,118,97,105,108,97,98,108,101,61,48,120,57,57,44,83,101,116,70,105,101,108,100,61,48,120,51,52,44,83,101,116,86,97,108,117,101,78,111,116,65,114,114,97,121,61,48,120,98,99,44,83,101,116,86,97,108,117,101,78,111,116,70,117,110,99,116,105,111,110,61,48,120,98,97,44,83,101,116,86,97,108,117,101,78,111,116,76,111,111,107,117,112,61,48,120,98,100,44,83,101,116,86,97,108,117,101,78,111,116,78,117,109,101,114,105,99,61,48,120,98,55,44,83,101,116,86,97,108,117,101,78,111,116,79,98,106,101,99,116,61,48,120,98,57,44,83,101,116,86,97,108,117,101,78,111,116,80,111,105,110,116,101,114,61,48,120,98,98,44,83,101,116,86,97,108,117,101,78,111,116,83,116,114,105,110,103,61,48,120,98,56,44,83,101,116,86,111,108,117,109,101,61,48,120,97,98,44,83,107,105,112,61,48,120,54,44,83,109,97,108,108,77,97,115,107,61,48,120,54,99,44,83,116,97,116,101,109,101,110,116,85,110,115,97,116,105,115,102,105,101,100,61,48,120,52,102,44,83,116,114,105,110,103,70,111,114,109,97,116,61,48,120,55,102,44,83,121,110,116,97,120,61,48,120,55,102,44,83,121,115,116,101,109,67,97,108,108,61,48,120,54,98,44,83,121,115,116,101,109,67,111,114,114,117,112,116,61,48,120,50,51,44,83,121,115,116,101,109,76,111,99,107,101,100,61,48,120,51,102,44,84,97,115,107,83,116,105,108,108,69,120,105,115,116,115,61,48,120,56,55,44,84,101,114,109,105,110,97,116,101,61,48,120,57,44,84,104,114,101,97,100,65,108,114,101,97,100,121,65,99,116,105,118,101,61,48,120,97,52,44,84,104,114,101,97,100,78,111,116,76,111,99,107,101,100,61,48,120,97,57,44,84,105,109,101,79,117,116,61,48,120,49,101,44,84,114,117,101,61,48,120,48,44,85,110,98,97,108,97,110,99,101,100,88,77,76,61,48,120,57,50,44,85,110,100,101,102,105,110,101,100,70,105,101,108,100,61,48,120,52,52,44,85,110,114,101,99,111,103,110,105,115,101,100,70,105,101,108,100,84,121,112,101,61,48,120,53,98,44,85,110,115,117,112,112,111,114,116,101,100,70,105,101,108,100,61,48,120,53,100,44,85,110,115,117,112,112,111,114,116,101,100,79,119,110,101,114,61,48,120,53,50,44,85,115,101,83,117,98,67,108,97,115,115,61,48,120,97,55,44,86,105,114,116,117,97,108,86,111,108,117,109,101,61,48,120,53,56,44,87,111,117,108,100,66,108,111,99,107,61,48,120,57,51,44,87,114,105,116,101,61,48,120,49,55,44,87,114,111,110,103,67,108,97,115,115,61,48,120,56,101,44,87,114,111,110,103,79,98,106,101,99,116,84,121,112,101,61,48,120,56,101,44,87,114,111,110,103,84,121,112,101,61,48,120,97,56,44,87,114,111,110,103,86,101,114,115,105,111,110,61,48,120,49,51,10,99,46,69,86,71,58,65,78,68,82,79,73,68,61,48,120,100,44,65,80,80,61,48,120,99,44,65,85,68,73,79,61,48,120,56,44,67,76,65,83,83,61,48,120,98,44,68,73,83,80,76,65,89,61,48,120,53,44,69,78,68,61,48,120,101,44,70,73,76,69,83,89,83,84,69,77,61,48,120,49,44,71,85,73,61,48,120,52,44,72,65,82,68,87,65,82,69,61,48,120,55,44,73,79,61,48,120,54,44,78,69,84,87,79,82,75,61,48,120,50,44,80,79,87,69,82,61,48,120,97,44,83,89,83,84,69,77,61,48,120,51,44,85,83,69,82,61,48,120,57,10,99,46,70,66,75,58,67,79,80,89,95,70,73,76,69,61,48,120,50,44,68,69,76,69,84,69,95,70,73,76,69,61,48,120,51,44,77,79,86,69,95,70,73,76,69,61,48,120,49,10,99,46,70,68,58,65,76,76,79,67,61,48,120,50,48,44,65,82,82,65,89,61,48,120,49,48,48,48,44,65,82,82,65,89,83,73,90,69,61,48,120,56,48,44,66,85,70,70,69,82,61,48,120,50,48,48,44,66,85,70,83,73,90,69,61,48,120,56,48,44,66,89,84,69,61,48,120,49,48,48,48,48,48,48,44,67,80,80,61,48,120,52,48,48,48,44,67,85,83,84,79,77,61,48,120,56,48,48,48,44,68,79,85,66,76,69,61,48,120,56,48,48,48,48,48,48,48,44,68,79,85,66,76,69,82,69,83,85,76,84,61,48,120,56,48,48,48,48,49,48,48,44,69,82,82,79,82,61,48,120,56,48,48,44,70,76,65,71,83,61,48,120,52,48,44,70,76,79,65,84,61,48,120,49,48,48,48,48,48,48,48,44,70,85,78,67,84,73,79,78,61,48,120,50,48,48,48,48,48,48,44,70,85,78,67,84,73,79,78,80,84,82,61,48,120,97,48,48,48,48,48,48,44,73,61,48,120,52,48,48,44,73,78,73,84,61,48,120,52,48,48,44,73,78,84,69,71,82,65,76,61,48,120,50,44,76,65,82,71,69,61,48,120,52,48,48,48,48,48,48,44,76,65,82,71,69,82,69,83,85,76,84,61,48,120,52,48,48,48,49,48,48,44,76,79,78,71,61,48,120,52,48,48,48,48,48,48,48,44,76,79,78,71,82,69,83,85,76,84,61,48,120,52,48,48,48,48,49,48,48,44,76,79,79,75,85,80,61,48,120,56,48,44,79,66,74,69,67,84,61,48,120,49,44,79,66,74,69,67,84,73,68,61,48,120,52,48,48,48,48,48,48,49,44,79,66,74,69,67,84,80,84,82,61,48,120,56,48,48,48,48,48,49,44,80,79,73,78,84,69,82,61,48,120,56,48,48,48,48,48,48,44,80,82,73,86,65,84,69,61,48,120,49,48,48,48,48,44,80,84,82,61,48,120,56,48,48,48,48,48,48,44,80,84,82,66,85,70,70,69,82,61,48,120,56,48,48,48,50,48,48,44,80,84,82,82,69,83,85,76,84,61,48,120,56,48,48,48,49,48,48,44,80,84,82,83,73,90,69,61,48,120,56,48,44,80,84,82,95,68,79,85,66,76,69,82,69,83,85,76,84,61,48,120,56,56,48,48,48,49,48,48,44,80,84,82,95,76,65,82,71,69,82,69,83,85,76,84,61,48,120,99,48,48,48,49,48,48,44,80,84,82,95,76,79,78,71,82,69,83,85,76,84,61,48,120,52,56,48,48,48,49,48,48,44,82,61,48,120,49,48,48,44,82,69,65,68,61,48,120,49,48,48,44,82,69,81,85,73,82,69,68,61,48,120,52,44,82,69,83,79,85,82,67,69,61,48,120,50,48,48,48,44,82,69,83,85,76,84,61,48,120,49,48,48,44,82,71,66,61,48,120,56,48,48,48,48,44,82,73,61,48,120,53,48,48,44,82,87,61,48,120,51,48,48,44,83,67,65,76,69,68,61,48,120,50,48,48,48,48,48,44,83,84,82,61,48,120,56,48,48,48,48,48,44,83,84,82,73,78,71,61,48,120,56,48,48,48,48,48,44,83,84,82,82,69,83,85,76,84,61,48,120,56,48,48,49,48,48,44,83,84,82,85,67,84,61,48,120,49,48,44,83,89,78,79,78,89,77,61,48,120,50,48,48,48,48,44,83,89,83,84,69,77,61,48,120,49,48,48,48,48,44,84,65,71,83,61,48,120,52,48,48,44,85,78,83,73,71,78,69,68,61,48,120,52,48,48,48,48,44,86,65,82,73,65,66,76,69,61,48,120,50,48,48,48,48,48,48,48,44,86,65,82,84,65,71,83,61,48,120,52,48,44,86,73,82,84,85,65,76,61,48,120,56,44,86,79,73,68,61,48,120,48,44,86,79,76,65,84,73,76,69,61,48,120,48,44,87,61,48,120,50,48,48,44,87,79,82,68,61,48,120,52,48,48,48,48,48,44,87,82,73,84,69,61,48,120,50,48,48,10,99,46,70,68,66,58,67,79,77,80,82,69,83,83,95,70,73,76,69,61,48,120,50,44,68,69,67,79,77,80,82,69,83,83,95,70,73,76,69,61,48,120,49,44,68,69,67,79,77,80,82,69,83,83,95,79,66,74,69,67,84,61,48,120,52,44,82,69,77,79,86,69,95,70,73,76,69,61,48,120,51,10,99,46,70,68,76,58,70,69,69,68,66,65,67,75,61,48,120,49,10,99,46,70,68,84,58,65,67,67,69,83,83,69,68,61,48,120,50,44,65,82,67,72,73,86,69,68,61,48,120,51,44,67,82,69,65,84,69,68,61,48,120,49,44,77,79,68,73,70,73,69,68,61,48,120,48,10,99,46,70,70,82,58,65,66,79,82,84,61,48,120,50,44,67,79,78,84,73,78,85,69,61,48,120,48,44,79,75,65,89,61,48,120,48,44,83,75,73,80,61,48,120,49,10,99,46,70,76,58,65,80,80,82,79,88,73,77,65,84,69,61,48,120,49,48,44,66,85,70,70,69,82,61,48,120,52,48,44,68,69,86,73,67,69,61,48,120,52,48,48,44,68,73,82,69,67,84,79,82,89,61,48,120,56,44,69,88,67,76,85,68,69,95,70,73,76,69,83,61,48,120,49,48,48,48,44,69,88,67,76,85,68,69,95,70,79,76,68,69,82,83,61,48,120,50,48,48,48,44,70,73,76,69,61,48,120,49,48,48,44,70,79,76,68,69,82,61,48,120,56,44,76,73,78,75,61,48,120,50,48,44,76,79,79,80,61,48,120,56,48,44,78,69,87,61,48,120,50,44,82,69,65,68,61,48,120,52,44,82,69,83,69,84,95,68,65,84,69,61,48,120,50,48,48,44,83,84,82,69,65,77,61,48,120,56,48,48,44,87,82,73,84,69,61,48,120,49,10,99,46,70,79,70,58,83,77,65,82,84,95,78,65,77,69,83,61,48,120,49,10,99,46,73,68,84,89,80,69,58,70,85,78,67,84,73,79,78,61,48,120,51,44,71,76,79,66,65,76,61,48,120,50,44,77,69,83,83,65,71,69,61,48,120,49,10,99,46,74,69,84,58,65,66,83,95,88,89,61,48,120,49,98,44,65,78,65,76,79,71,50,95,88,89,61,48,120,49,54,44,65,78,65,76,79,71,50,95,90,61,48,120,49,55,44,65,78,65,76,79,71,95,88,89,61,48,120,49,52,44,65,78,65,76,79,71,95,90,61,48,120,49,53,44,66,85,84,84,79,78,95,49,61,48,120,50,44,66,85,84,84,79,78,95,49,48,61,48,120,98,44,66,85,84,84,79,78,95,50,61,48,120,51,44,66,85,84,84,79,78,95,51,61,48,120,52,44,66,85,84,84,79,78,95,52,61,48,120,53,44,66,85,84,84,79,78,95,53,61,48,120,54,44,66,85,84,84,79,78,95,54,61,48,120,55,44,66,85,84,84,79,78,95,55,61,48,120,56,44,66,85,84,84,79,78,95,56,61,48,120,57,44,66,85,84,84,79,78,95,57,61,48,120,97,44,66,85,84,84,79,78,95,83,69,76,69,67,84,61,48,120,102,44,66,85,84,84,79,78,95,83,84,65,82,84,61,48,120,101,44,67,82,79,83,83,69,68,95,73,78,61,48,120,49,99,44,67,82,79,83,83,69,68,95,79,85,84,61,48,120,49,100,44,68,69,86,73,67,69,95,84,73,76,84,95,88,89,61,48,120,49,102,44,68,69,86,73,67,69,95,84,73,76,84,95,90,61,48,120,50,48,44,68,73,71,73,84,65,76,95,88,89,61,48,120,49,44,68,73,83,80,76,65,89,95,69,68,71,69,61,48,120,50,49,44,69,78,68,61,48,120,50,50,44,76,69,70,84,95,66,85,77,80,69,82,95,49,61,48,120,49,48,44,76,69,70,84,95,66,85,77,80,69,82,95,50,61,48,120,49,49,44,76,77,66,61,48,120,50,44,77,77,66,61,48,120,52,44,80,69,78,95,84,73,76,84,95,88,89,61,48,120,49,97,44,80,82,69,83,83,85,82,69,61,48,120,49,101,44,82,73,71,72,84,95,66,85,77,80,69,82,95,49,61,48,120,49,50,44,82,73,71,72,84,95,66,85,77,80,69,82,95,50,61,48,120,49,51,44,82,77,66,61,48,120,51,44,84,82,73,71,71,69,82,95,76,69,70,84,61,48,120,99,44,84,82,73,71,71,69,82,95,82,73,71,72,84,61,48,120,100,44,87,72,69,69,76,61,48,120,49,56,44,87,72,69,69,76,95,84,73,76,84,61,48,120,49,57,10,99,46,74,84,89,80,69,58,65,78,65,76,79,71,61,48,120,50,48,44,65,78,67,72,79,82,69,68,61,48,120,50,44,66,85,84,84,79,78,61,48,120,56,48,44,67,82,79,83,83,73,78,71,61,48,120,56,44,68,66,76,95,67,76,73,67,75,61,48,120,50,48,48,44,68,73,71,73,84,65,76,61,48,120,49,48,44,68,82,65,71,71,69,68,61,48,120,52,44,68,82,65,71,95,73,84,69,77,61,48,120,56,48,48,44,69,88,84,95,77,79,86,69,77,69,78,84,61,48,120,52,48,44,77,79,86,69,77,69,78,84,61,48,120,49,48,48,44,82,69,80,69,65,84,69,68,61,48,120,52,48,48,44,83,69,67,79,78,68,65,82,89,61,48,120,49,10,99,46,75,69,89,58,65,61,48,120,49,44,65,80,79,83,84,82,79,80,72,69,61,48,120,50,98,44,65,84,61,48,120,56,97,44,66,61,48,120,50,44,66,65,67,75,61,48,120,56,54,44,66,65,67,75,83,80,65,67,69,61,48,120,54,57,44,66,65,67,75,95,83,76,65,83,72,61,48,120,50,102,44,66,82,69,65,75,61,48,120,55,98,44,67,61,48,120,51,44,67,65,76,76,61,48,120,56,55,44,67,65,77,69,82,65,61,48,120,56,57,44,67,65,78,67,69,76,61,48,120,55,97,44,67,65,80,83,95,76,79,67,75,61,48,120,52,54,44,67,76,69,65,82,61,48,120,54,101,44,67,79,77,77,65,61,48,120,50,99,44,68,61,48,120,52,44,68,69,76,69,84,69,61,48,120,54,100,44,68,79,84,61,48,120,50,100,44,68,79,87,78,61,48,120,54,49,44,69,61,48,120,53,44,69,73,71,72,84,61,48,120,50,50,44,69,78,68,61,48,120,55,50,44,69,78,68,95,67,65,76,76,61,48,120,56,56,44,69,78,84,69,82,61,48,120,54,98,44,69,81,85,65,76,83,61,48,120,50,55,44,69,83,67,65,80,69,61,48,120,54,99,44,69,88,69,67,85,84,69,61,48,120,55,52,44,70,61,48,120,54,44,70,49,61,48,120,52,99,44,70,49,48,61,48,120,53,53,44,70,49,49,61,48,120,53,54,44,70,49,50,61,48,120,53,55,44,70,49,51,61,48,120,53,56,44,70,49,52,61,48,120,53,57,44,70,49,53,61,48,120,53,97,44,70,49,54,61,48,120,53,98,44,70,49,55,61,48,120,53,99,44,70,49,56,61,48,120,56,48,44,70,49,57,61,48,120,56,49,44,70,50,61,48,120,52,100,44,70,50,48,61,48,120,56,50,44,70,51,61,48,120,52,101,44,70,52,61,48,120,52,102,44,70,53,61,48,120,53,48,44,70,54,61,48,120,53,49,44,70,55,61,48,120,53,50,44,70,56,61,48,120,53,51,44,70,57,61,48,120,53,52,44,70,73,78,68,61,48,120,55,57,44,70,73,86,69,61,48,120,49,102,44,70,79,82,87,65,82,68,61,48,120,57,48,44,70,79,85,82,61,48,120,49,101,44,71,61,48,120,55,44,72,61,48,120,56,44,72,69,76,80,61,48,120,52,51,44,72,79,77,69,61,48,120,54,102,44,73,61,48,120,57,44,73,78,83,69,82,84,61,48,120,55,53,44,74,61,48,120,97,44,75,61,48,120,98,44,76,61,48,120,99,44,76,69,70,84,61,48,120,54,51,44,76,69,78,83,95,70,79,67,85,83,61,48,120,56,99,44,76,69,83,83,95,71,82,69,65,84,69,82,61,48,120,53,102,44,76,73,83,84,95,69,78,68,61,48,120,57,54,44,76,95,65,76,84,61,48,120,52,56,44,76,95,67,79,77,77,65,78,68,61,48,120,52,97,44,76,95,67,79,78,84,82,79,76,61,48,120,52,49,44,76,95,83,72,73,70,84,61,48,120,52,52,44,76,95,83,81,85,65,82,69,61,48,120,50,56,44,77,61,48,120,100,44,77,65,67,82,79,61,48,120,53,100,44,77,69,78,85,61,48,120,55,56,44,77,73,78,85,83,61,48,120,50,54,44,77,85,84,69,61,48,120,57,50,44,78,61,48,120,101,44,78,69,88,84,61,48,120,56,101,44,78,73,78,69,61,48,120,50,51,44,78,80,95,48,61,48,120,51,49,44,78,80,95,49,61,48,120,51,50,44,78,80,95,50,61,48,120,51,51,44,78,80,95,51,61,48,120,51,52,44,78,80,95,52,61,48,120,51,53,44,78,80,95,53,61,48,120,51,54,44,78,80,95,54,61,48,120,51,55,44,78,80,95,55,61,48,120,51,56,44,78,80,95,56,61,48,120,51,57,44,78,80,95,57,61,48,120,51,97,44,78,80,95,66,65,82,61,48,120,51,100,44,78,80,95,68,69,67,73,77,65,76,61,48,120,51,102,44,78,80,95,68,73,86,73,68,69,61,48,120,52,48,44,78,80,95,68,79,84,61,48,120,51,102,44,78,80,95,69,78,84,69,82,61,48,120,55,101,44,78,80,95,77,73,78,85,83,61,48,120,51,101,44,78,80,95,77,85,76,84,73,80,76,89,61,48,120,51,98,44,78,80,95,80,76,85,83,61,48,120,51,99,44,78,80,95,80,76,85,83,95,77,73,78,85,83,61,48,120,53,101,44,78,80,95,83,69,80,65,82,65,84,79,82,61,48,120,51,100,44,78,85,77,95,76,79,67,75,61,48,120,55,99,44,79,61,48,120,102,44,79,78,69,61,48,120,49,98,44,80,61,48,120,49,48,44,80,65,71,69,95,68,79,87,78,61,48,120,55,49,44,80,65,71,69,95,85,80,61,48,120,55,48,44,80,65,85,83,69,61,48,120,54,53,44,80,69,82,73,79,68,61,48,120,50,100,44,80,76,65,89,61,48,120,57,53,44,80,76,85,83,61,48,120,56,98,44,80,79,85,78,68,61,48,120,57,52,44,80,79,87,69,82,61,48,120,54,56,44,80,82,69,86,73,79,85,83,61,48,120,56,102,44,80,82,73,78,84,61,48,120,52,55,44,80,82,84,95,83,67,82,61,48,120,55,100,44,81,61,48,120,49,49,44,82,61,48,120,49,50,44,82,69,68,79,61,48,120,55,55,44,82,69,86,69,82,83,69,95,81,85,79,84,69,61,48,120,50,53,44,82,69,87,73,78,68,61,48,120,57,49,44,82,73,71,72,84,61,48,120,54,50,44,82,95,65,76,84,61,48,120,52,57,44,82,95,67,79,77,77,65,78,68,61,48,120,52,98,44,82,95,67,79,78,84,82,79,76,61,48,120,52,50,44,82,95,83,72,73,70,84,61,48,120,52,53,44,82,95,83,81,85,65,82,69,61,48,120,50,57,44,83,61,48,120,49,51,44,83,67,82,95,76,79,67,75,61,48,120,54,52,44,83,69,76,69,67,84,61,48,120,55,51,44,83,69,77,73,95,67,79,76,79,78,61,48,120,50,97,44,83,69,86,69,78,61,48,120,50,49,44,83,73,88,61,48,120,50,48,44,83,76,65,83,72,61,48,120,50,101,44,83,76,69,69,80,61,48,120,54,55,44,83,80,65,67,69,61,48,120,51,48,44,83,84,65,82,61,48,120,57,51,44,83,84,79,80,61,48,120,56,100,44,83,89,83,82,81,61,48,120,55,102,44,84,61,48,120,49,52,44,84,65,66,61,48,120,54,97,44,84,72,82,69,69,61,48,120,49,100,44,84,87,79,61,48,120,49,99,44,85,61,48,120,49,53,44,85,78,68,79,61,48,120,55,54,44,85,80,61,48,120,54,48,44,86,61,48,120,49,54,44,86,79,76,85,77,69,95,68,79,87,78,61,48,120,56,53,44,86,79,76,85,77,69,95,85,80,61,48,120,56,52,44,87,61,48,120,49,55,44,87,65,75,69,61,48,120,54,54,44,87,73,78,95,67,79,78,84,82,79,76,61,48,120,56,51,44,88,61,48,120,49,56,44,89,61,48,120,49,57,44,90,61,48,120,49,97,44,90,69,82,79,61,48,120,50,52,10,99,46,75,81,58,65,76,84,61,48,120,54,48,44,65,76,84,71,82,61,48,120,52,48,44,67,65,80,83,95,76,79,67,75,61,48,120,52,44,67,79,77,77,65,78,68,61,48,120,49,56,48,44,67,79,78,84,82,79,76,61,48,120,49,56,44,67,84,82,76,61,48,120,49,56,44,68,69,65,68,95,75,69,89,61,48,120,49,48,48,48,48,44,73,78,70,79,61,48,120,51,99,48,52,44,73,78,83,84,82,85,67,84,73,79,78,95,75,69,89,83,61,48,120,55,56,44,76,95,65,76,84,61,48,120,50,48,44,76,95,67,79,77,77,65,78,68,61,48,120,56,48,44,76,95,67,79,78,84,82,79,76,61,48,120,56,44,76,95,67,84,82,76,61,48,120,56,44,76,95,83,72,73,70,84,61,48,120,49,44,78,79,84,95,80,82,73,78,84,65,66,76,69,61,48,120,50,48,48,48,44,78,85,77,95,76,79,67,75,61,48,120,56,48,48,48,44,78,85,77,95,80,65,68,61,48,120,50,48,48,44,80,82,69,83,83,69,68,61,48,120,49,48,48,48,44,81,85,65,76,73,70,73,69,82,83,61,48,120,49,102,98,44,82,69,76,69,65,83,69,68,61,48,120,56,48,48,44,82,69,80,69,65,84,61,48,120,52,48,48,44,82,95,65,76,84,61,48,120,52,48,44,82,95,67,79,77,77,65,78,68,61,48,120,49,48,48,44,82,95,67,79,78,84,82,79,76,61,48,120,49,48,44,82,95,67,84,82,76,61,48,120,49,48,44,82,95,83,72,73,70,84,61,48,120,50,44,83,67,82,95,76,79,67,75,61,48,120,52,48,48,48,44,83,72,73,70,84,61,48,120,51,44,87,73,78,95,67,79,78,84,82,79,76,61,48,120,50,48,48,48,48,10,99,46,76,65,89,79,85,84,58,66,65,67,75,71,82,79,85,78,68,61,48,120,56,44,69,77,66,69,68,68,69,68,61,48,120,50,48,44,70,79,82,69,71,82,79,85,78,68,61,48,120,49,48,44,73,71,78,79,82,69,95,67,85,82,83,79,82,61,48,120,56,48,44,76,69,70,84,61,48,120,50,44,76,79,67,75,61,48,120,52,48,44,82,73,71,72,84,61,48,120,52,44,83,81,85,65,82,69,61,48,120,48,44,84,73,71,72,84,61,48,120,49,44,84,73,76,69,61,48,120,49,48,48,44,87,73,68,69,61,48,120,54,10,99,46,76,68,70,58,67,72,69,67,75,95,69,88,73,83,84,83,61,48,120,49,10,99,46,76,79,67,58,68,73,82,69,67,84,79,82,89,61,48,120,49,44,70,73,76,69,61,48,120,51,44,70,79,76,68,69,82,61,48,120,49,44,86,79,76,85,77,69,61,48,120,50,10,99,46,77,65,88,58,78,65,77,69,95,76,69,78,61,48,120,50,48,10,99,46,77,69,77,58,65,85,68,73,79,61,48,120,56,44,67,65,76,76,69,82,61,48,120,56,48,48,48,48,48,44,67,79,68,69,61,48,120,49,48,44,68,65,84,65,61,48,120,48,44,68,69,76,69,84,69,61,48,120,49,48,48,48,44,69,88,67,76,85,83,73,86,69,61,48,120,56,48,48,44,72,73,68,68,69,78,61,48,120,49,48,48,48,48,48,44,77,65,78,65,71,69,68,61,48,120,49,44,78,79,95,66,76,79,67,75,61,48,120,50,48,48,48,44,78,79,95,66,76,79,67,75,73,78,71,61,48,120,50,48,48,48,44,78,79,95,67,76,69,65,82,61,48,120,52,48,48,48,48,44,78,79,95,76,79,67,75,61,48,120,52,48,48,44,78,79,95,80,79,79,76,61,48,120,50,48,44,79,66,74,69,67,84,61,48,120,50,48,48,44,82,69,65,68,61,48,120,49,48,48,48,48,44,82,69,65,68,95,87,82,73,84,69,61,48,120,51,48,48,48,48,44,83,84,82,73,78,71,61,48,120,49,48,48,44,84,69,88,84,85,82,69,61,48,120,52,44,84,77,80,95,76,79,67,75,61,48,120,52,48,44,85,78,84,82,65,67,75,69,68,61,48,120,56,48,44,86,73,68,69,79,61,48,120,50,44,87,82,73,84,69,61,48,120,50,48,48,48,48,10,99,46,77,70,70,58,65,84,84,82,73,66,61,48,120,50,48,44,67,76,79,83,69,68,61,48,120,56,48,44,67,82,69,65,84,69,61,48,120,52,44,68,69,69,80,61,48,120,49,48,48,48,44,68,69,76,69,84,69,61,48,120,56,44,70,73,76,69,61,48,120,52,48,48,44,70,79,76,68,69,82,61,48,120,50,48,48,44,77,79,68,73,70,89,61,48,120,50,44,77,79,86,69,68,61,48,120,49,48,44,79,80,69,78,69,68,61,48,120,52,48,44,82,69,65,68,61,48,120,49,44,82,69,78,65,77,69,61,48,120,49,48,44,83,69,76,70,61,48,120,56,48,48,44,85,78,77,79,85,78,84,61,48,120,49,48,48,44,87,82,73,84,69,61,48,120,50,10,99,46,77,72,70,58,68,69,70,65,85,76,84,61,48,120,50,44,83,84,65,84,73,67,61,48,120,49,44,83,84,82,85,67,84,85,82,69,61,48,120,50,10,99,46,77,79,70,58,76,73,78,75,95,76,73,66,82,65,82,89,61,48,120,49,44,83,84,65,84,73,67,61,48,120,50,44,83,89,83,84,69,77,95,80,82,79,66,69,61,48,120,52,10,99,46,77,79,86,69,58,65,76,76,61,48,120,102,44,68,79,87,78,61,48,120,49,44,76,69,70,84,61,48,120,52,44,82,73,71,72,84,61,48,120,56,44,85,80,61,48,120,50,10,99,46,77,83,70,58,65,68,68,61,48,120,56,44,65,68,68,82,69,83,83,61,48,120,49,48,44,77,69,83,83,65,71,69,95,73,68,61,48,120,50,48,44,78,79,95,68,85,80,76,73,67,65,84,69,61,48,120,52,44,85,80,68,65,84,69,61,48,120,50,44,87,65,73,84,61,48,120,49,10,99,46,77,83,71,73,68,58,65,67,84,73,79,78,61,48,120,54,51,44,66,82,69,65,75,61,48,120,54,52,44,67,79,77,77,65,78,68,61,48,120,54,53,44,67,79,82,69,95,69,78,68,61,48,120,54,52,44,68,69,66,85,71,61,48,120,53,102,44,69,86,69,78,84,61,48,120,53,101,44,70,82,69,69,61,48,120,54,50,44,81,85,73,84,61,48,120,51,101,56,44,84,72,82,69,65,68,95,65,67,84,73,79,78,61,48,120,53,98,44,84,72,82,69,65,68,95,67,65,76,76,66,65,67,75,61,48,120,53,99,44,86,65,76,73,68,65,84,69,95,80,82,79,67,69,83,83,61,48,120,53,100,44,87,65,73,84,95,70,79,82,95,79,66,74,69,67,84,83,61,48,120,53,97,10,99,46,77,84,70,58,65,78,73,77,61,48,120,56,44,82,69,76,65,84,73,86,69,61,48,120,49,48,44,88,61,48,120,49,44,89,61,48,120,50,44,90,61,48,120,52,10,99,46,78,69,84,77,83,71,58,69,78,68,61,48,120,49,44,83,84,65,82,84,61,48,120,48,10,99,46,78,70,58,67,79,76,76,69,67,84,61,48,120,56,48,44,70,82,69,69,61,48,120,49,48,44,70,82,69,69,95,79,78,95,85,78,76,79,67,75,61,48,120,56,44,73,78,73,84,73,65,76,73,83,69,68,61,48,120,50,44,73,78,84,69,71,82,65,76,61,48,120,52,44,77,69,83,83,65,71,69,61,48,120,50,48,48,44,78,65,77,69,61,48,120,56,48,48,48,48,48,48,48,44,80,82,73,86,65,84,69,61,48,120,48,44,82,69,67,76,65,83,83,69,68,61,48,120,49,48,48,44,83,73,71,78,65,76,76,69,68,61,48,120,52,48,48,44,83,85,80,80,82,69,83,83,95,76,79,71,61,48,120,52,48,44,84,73,77,69,82,95,83,85,66,61,48,120,50,48,44,85,78,73,81,85,69,61,48,120,52,48,48,48,48,48,48,48,44,85,78,84,82,65,67,75,69,68,61,48,120,49,10,99,46,79,80,70,58,65,82,71,83,61,48,120,52,48,44,68,69,84,65,73,76,61,48,120,52,44,69,82,82,79,82,61,48,120,56,48,44,77,65,88,95,68,69,80,84,72,61,48,120,50,44,77,79,68,85,76,69,95,80,65,84,72,61,48,120,52,48,48,44,79,80,84,73,79,78,83,61,48,120,49,44,80,82,73,86,73,76,69,71,69,68,61,48,120,49,48,48,44,82,79,79,84,95,80,65,84,72,61,48,120,56,48,48,44,83,67,65,78,95,77,79,68,85,76,69,83,61,48,120,49,48,48,48,44,83,72,79,87,95,69,82,82,79,82,83,61,48,120,50,48,44,83,72,79,87,95,73,79,61,48,120,49,48,44,83,72,79,87,95,77,69,77,79,82,89,61,48,120,56,44,83,89,83,84,69,77,95,80,65,84,72,61,48,120,50,48,48,10,99,46,80,69,82,77,73,84,58,65,76,76,95,68,69,76,69,84,69,61,48,120,56,56,56,44,65,76,76,95,69,88,69,67,61,48,120,52,52,52,44,65,76,76,95,82,69,65,68,61,48,120,49,49,49,44,65,76,76,95,87,82,73,84,69,61,48,120,50,50,50,44,65,82,67,72,73,86,69,61,48,120,50,48,48,48,44,68,69,76,69,84,69,61,48,120,56,44,69,86,69,82,89,79,78,69,95,65,67,67,69,83,83,61,48,120,102,102,102,44,69,86,69,82,89,79,78,69,95,68,69,76,69,84,69,61,48,120,56,56,56,44,69,86,69,82,89,79,78,69,95,69,88,69,67,61,48,120,52,52,52,44,69,86,69,82,89,79,78,69,95,82,69,65,68,61,48,120,49,49,49,44,69,86,69,82,89,79,78,69,95,82,69,65,68,87,82,73,84,69,61,48,120,51,51,51,44,69,86,69,82,89,79,78,69,95,87,82,73,84,69,61,48,120,50,50,50,44,69,88,69,67,61,48,120,52,44,71,82,79,85,80,61,48,120,102,48,44,71,82,79,85,80,73,68,61,48,120,49,48,48,48,48,44,71,82,79,85,80,95,68,69,76,69,84,69,61,48,120,56,48,44,71,82,79,85,80,95,69,88,69,67,61,48,120,52,48,44,71,82,79,85,80,95,82,69,65,68,61,48,120,49,48,44,71,82,79,85,80,95,87,82,73,84,69,61,48,120,50,48,44,72,73,68,68,69,78,61,48,120,49,48,48,48,44,73,78,72,69,82,73,84,61,48,120,50,48,48,48,48,44,78,69,84,87,79,82,75,61,48,120,56,48,48,48,48,44,79,70,70,76,73,78,69,61,48,120,52,48,48,48,48,44,79,84,72,69,82,83,61,48,120,102,48,48,44,79,84,72,69,82,83,95,68,69,76,69,84,69,61,48,120,56,48,48,44,79,84,72,69,82,83,95,69,88,69,67,61,48,120,52,48,48,44,79,84,72,69,82,83,95,82,69,65,68,61,48,120,49,48,48,44,79,84,72,69,82,83,95,87,82,73,84,69,61,48,120,50,48,48,44,80,65,83,83,87,79,82,68,61,48,120,52,48,48,48,44,82,69,65,68,61,48,120,49,44,85,83,69,82,61,48,120,102,44,85,83,69,82,73,68,61,48,120,56,48,48,48,44,85,83,69,82,95,69,88,69,67,61,48,120,52,44,85,83,69,82,95,82,69,65,68,61,48,120,49,44,85,83,69,82,95,87,82,73,84,69,61,48,120,50,44,87,82,73,84,69,61,48,120,50,10,99,46,80,77,70,58,83,89,83,84,69,77,95,78,79,95,66,82,69,65,75,61,48,120,49,10,99,46,80,84,67,58,67,82,79,83,83,72,65,73,82,61,48,120,97,44,67,85,83,84,79,77,61,48,120,49,55,44,68,69,70,65,85,76,84,61,48,120,49,44,68,82,65,71,71,65,66,76,69,61,48,120,49,56,44,69,78,68,61,48,120,49,57,44,72,65,78,68,61,48,120,49,48,44,72,65,78,68,95,76,69,70,84,61,48,120,49,49,44,72,65,78,68,95,82,73,71,72,84,61,48,120,49,50,44,73,78,86,73,83,73,66,76,69,61,48,120,49,54,44,77,65,71,78,73,70,73,69,82,61,48,120,102,44,78,79,95,67,72,65,78,71,69,61,48,120,48,44,80,65,73,78,84,66,82,85,83,72,61,48,120,49,52,44,83,73,90,69,95,66,79,84,84,79,77,61,48,120,57,44,83,73,90,69,95,66,79,84,84,79,77,95,76,69,70,84,61,48,120,50,44,83,73,90,69,95,66,79,84,84,79,77,95,82,73,71,72,84,61,48,120,51,44,83,73,90,69,95,76,69,70,84,61,48,120,54,44,83,73,90,69,95,82,73,71,72,84,61,48,120,55,44,83,73,90,69,95,84,79,80,61,48,120,56,44,83,73,90,69,95,84,79,80,95,76,69,70,84,61,48,120,52,44,83,73,90,69,95,84,79,80,95,82,73,71,72,84,61,48,120,53,44,83,73,90,73,78,71,61,48,120,99,44,83,76,69,69,80,61,48,120,98,44,83,80,76,73,84,95,72,79,82,73,90,79,78,84,65,76,61,48,120,101,44,83,80,76,73,84,95,86,69,82,84,73,67,65,76,61,48,120,100,44,83,84,79,80,61,48,120,49,53,44,84,69,88,84,61,48,120,49,51,10,99,46,82,68,70,58,65,82,67,72,73,86,69,61,48,120,50,48,48,48,44,68,65,84,69,61,48,120,50,44,70,73,76,69,61,48,120,56,44,70,73,76,69,83,61,48,120,56,44,70,79,76,68,69,82,61,48,120,49,48,44,70,79,76,68,69,82,83,61,48,120,49,48,44,72,73,68,68,69,78,61,48,120,49,48,48,44,76,73,78,75,61,48,120,52,48,44,79,80,69,78,68,73,82,61,48,120,52,48,48,48,44,80,69,82,77,73,83,83,73,79,78,83,61,48,120,52,44,81,85,65,76,73,70,73,69,68,61,48,120,50,48,48,44,81,85,65,76,73,70,89,61,48,120,50,48,48,44,82,69,65,68,95,65,76,76,61,48,120,49,102,44,82,69,65,68,95,79,78,76,89,61,48,120,49,48,48,48,44,83,73,90,69,61,48,120,49,44,83,84,82,69,65,77,61,48,120,56,48,48,44,84,65,71,83,61,48,120,56,48,44,84,73,77,69,61,48,120,50,44,86,73,82,84,85,65,76,61,48,120,52,48,48,44,86,79,76,85,77,69,61,48,120,50,48,10,99,46,82,69,83,58,67,79,78,83,79,76,69,95,70,68,61,48,120,50,44,67,79,82,69,95,73,68,76,61,48,120,56,44,67,80,85,95,83,80,69,69,68,61,48,120,49,54,44,68,73,83,80,76,65,89,95,68,82,73,86,69,82,61,48,120,53,44,69,88,67,69,80,84,73,79,78,95,72,65,78,68,76,69,82,61,48,120,49,49,44,70,82,69,69,95,77,69,77,79,82,89,61,48,120,49,55,44,70,82,69,69,95,83,87,65,80,61,48,120,49,44,74,78,73,95,69,78,86,61,48,120,101,44,75,69,89,95,83,84,65,84,69,61,48,120,51,44,76,79,71,95,68,69,80,84,72,61,48,120,100,44,76,79,71,95,76,69,86,69,76,61,48,120,97,44,77,65,88,95,80,82,79,67,69,83,83,69,83,61,48,120,99,44,78,69,84,95,80,82,79,67,69,83,83,73,78,71,61,48,120,49,50,44,79,80,69,78,95,73,78,70,79,61,48,120,49,48,44,80,65,82,69,78,84,95,67,79,78,84,69,88,84,61,48,120,57,44,80,82,73,86,73,76,69,71,69,68,61,48,120,55,44,80,82,73,86,73,76,69,71,69,68,95,85,83,69,82,61,48,120,54,44,80,82,79,67,69,83,83,95,83,84,65,84,69,61,48,120,49,51,44,83,84,65,84,73,67,95,66,85,73,76,68,61,48,120,49,56,44,84,72,82,69,65,68,95,73,68,61,48,120,102,44,84,79,84,65,76,95,77,69,77,79,82,89,61,48,120,49,52,44,84,79,84,65,76,95,83,72,65,82,69,68,95,77,69,77,79,82,89,61,48,120,98,44,84,79,84,65,76,95,83,87,65,80,61,48,120,49,53,44,85,83,69,82,95,73,68,61,48,120,52,10,99,46,82,70,68,58,65,76,76,79,87,95,82,69,67,85,82,83,73,79,78,61,48,120,50,48,44,65,76,87,65,89,83,95,67,65,76,76,61,48,120,49,48,48,44,69,88,67,69,80,84,61,48,120,50,44,82,69,65,68,61,48,120,52,44,82,69,67,65,76,76,61,48,120,56,48,44,82,69,77,79,86,69,61,48,120,56,44,83,79,67,75,69,84,61,48,120,52,48,44,83,84,79,80,95,82,69,67,85,82,83,69,61,48,120,49,48,44,87,82,73,84,69,61,48,120,49,10,99,46,82,80,58,77,79,68,85,76,69,95,80,65,84,72,61,48,120,49,44,82,79,79,84,95,80,65,84,72,61,48,120,51,44,83,89,83,84,69,77,95,80,65,84,72,61,48,120,50,10,99,46,82,83,70,58,65,80,80,82,79,88,73,77,65,84,69,61,48,120,52,44,67,65,83,69,95,83,69,78,83,73,84,73,86,69,61,48,120,50,48,44,67,72,69,67,75,95,86,73,82,84,85,65,76,61,48,120,50,44,78,79,95,68,69,69,80,95,83,67,65,78,61,48,120,56,44,78,79,95,70,73,76,69,95,67,72,69,67,75,61,48,120,49,44,80,65,84,72,61,48,120,49,48,10,99,46,83,67,70,58,69,88,73,84,95,79,78,95,69,82,82,79,82,61,48,120,49,44,76,79,71,95,65,76,76,61,48,120,50,10,99,46,83,69,69,75,58,67,85,82,82,69,78,84,61,48,120,49,44,69,78,68,61,48,120,50,44,82,69,76,65,84,73,86,69,61,48,120,51,44,83,84,65,82,84,61,48,120,48,10,99,46,83,84,80,58,65,78,73,77,61,48,120,56,44,88,61,48,120,49,44,89,61,48,120,50,44,90,61,48,120,52,10,99,46,83,84,82,58,67,65,83,69,61,48,120,49,44,77,65,84,67,72,95,67,65,83,69,61,48,120,49,44,77,65,84,67,72,95,76,69,78,61,48,120,50,44,87,73,76,68,67,65,82,68,61,48,120,52,10,99,46,83,84,84,58,70,76,79,65,84,61,48,120,50,44,72,69,88,61,48,120,51,44,78,85,77,66,69,82,61,48,120,49,44,83,84,82,73,78,71,61,48,120,52,10,99,46,84,72,70,58,65,85,84,79,95,70,82,69,69,61,48,120,49,10,99,46,84,79,73,58,65,78,68,82,79,73,68,95,65,83,83,69,84,77,71,82,61,48,120,52,44,65,78,68,82,79,73,68,95,67,76,65,83,83,61,48,120,51,44,65,78,68,82,79,73,68,95,69,78,86,61,48,120,50,44,76,79,67,65,76,95,67,65,67,72,69,61,48,120,48,44,76,79,67,65,76,95,83,84,79,82,65,71,69,61,48,120,49,10,99,46,84,83,70,58,65,84,84,65,67,72,69,68,61,48,120,49,48,48,44,68,69,84,65,67,72,69,68,61,48,120,56,48,44,70,79,82,69,73,71,78,61,48,120,49,44,76,79,71,95,65,76,76,61,48,120,50,48,44,80,73,80,69,61,48,120,50,48,48,44,80,82,73,86,73,76,69,71,69,68,61,48,120,56,44,81,85,73,69,84,61,48,120,52,48,44,82,69,83,69,84,95,80,65,84,72,61,48,120,52,44,83,72,69,76,76,61,48,120,49,48,44,87,65,73,84,61,48,120,50,10,99,46,84,83,84,65,84,69,58,80,65,85,83,69,68,61,48,120,49,44,82,85,78,78,73,78,71,61,48,120,48,44,83,84,79,80,80,73,78,71,61,48,120,50,44,84,69,82,77,73,78,65,84,69,68,61,48,120,51,10,99,46,86,65,83,58,67,65,83,69,95,83,69,78,83,73,84,73,86,69,61,48,120,102,44,67,76,79,83,69,95,68,73,82,61,48,120,54,44,67,82,69,65,84,69,95,76,73,78,75,61,48,120,49,49,44,68,69,76,69,84,69,61,48,120,51,44,68,69,82,69,71,73,83,84,69,82,61,48,120,49,44,68,82,73,86,69,82,95,83,73,90,69,61,48,120,49,50,44,71,69,84,95,68,69,86,73,67,69,95,73,78,70,79,61,48,120,98,44,71,69,84,95,73,78,70,79,61,48,120,97,44,73,68,69,78,84,73,70,89,95,70,73,76,69,61,48,120,99,44,73,71,78,79,82,69,95,70,73,76,69,61,48,120,57,44,77,65,75,69,95,68,73,82,61,48,120,100,44,79,80,69,78,95,68,73,82,61,48,120,53,44,82,69,65,68,95,76,73,78,75,61,48,120,49,48,44,82,69,78,65,77,69,61,48,120,52,44,83,65,77,69,95,70,73,76,69,61,48,120,101,44,83,67,65,78,95,68,73,82,61,48,120,50,44,84,69,83,84,95,80,65,84,72,61,48,120,55,44,87,65,84,67,72,95,80,65,84,72,61,48,120,56,10,99,46,86,76,70,58,65,80,73,61,48,120,50,48,44,66,82,65,78,67,72,61,48,120,49,44,67,82,73,84,73,67,65,76,61,48,120,56,44,68,69,84,65,73,76,61,48,120,52,48,44,69,82,82,79,82,61,48,120,50,44,70,85,78,67,84,73,79,78,61,48,120,49,48,48,44,73,78,70,79,61,48,120,49,48,44,84,82,65,67,69,61,48,120,56,48,44,87,65,82,78,73,78,71,61,48,120,52,10,99,46,86,79,76,85,77,69,58,72,73,68,68,69,78,61,48,120,52,44,80,82,73,79,82,73,84,89,61,48,120,50,44,82,69,80,76,65,67,69,61,48,120,49,44,83,89,83,84,69,77,61,48,120,56,10,0 }; diff --git a/src/link/linear_rgb.cpp b/src/link/linear_rgb.cpp index 655b368b4..18d4de2f1 100644 --- a/src/link/linear_rgb.cpp +++ b/src/link/linear_rgb.cpp @@ -1,3 +1,5 @@ +// To link: target_link_libraries (${MOD} PUBLIC linear_rgb) +// To include: #include "../link/linear_rgb.h" #include #include "linear_rgb.h" diff --git a/src/svg/CMakeLists.txt b/src/svg/CMakeLists.txt index df8767946..bb2f1d37b 100644 --- a/src/svg/CMakeLists.txt +++ b/src/svg/CMakeLists.txt @@ -30,6 +30,8 @@ target_sources (${MOD} PRIVATE target_include_directories (${MOD} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/katana-parser/src") + +target_link_libraries (${MOD} PUBLIC linear_rgb) target_link_libraries (${MOD} PRIVATE ${MATH_LINK}) diff --git a/src/svg/animation.cpp b/src/svg/animation.cpp index 9adfe94de..016d98dcb 100644 --- a/src/svg/animation.cpp +++ b/src/svg/animation.cpp @@ -1,90 +1,613 @@ +// Relevant SVG materials: +// https://www.w3.org/TR/SVG11/animate.html#ToAttribute +// https://www.w3.org/TR/2001/REC-smil-animation-20010904 + +#include "../link/linear_rgb.h" //******************************************************************************************************************** +// Return an interpolated value based on the values or from/to/by settings. -static ERR animation_timer(extSVG *SVG, LARGE TimeElapsed, LARGE CurrentTime) +DOUBLE anim_base::get_numeric_value() +{ + DOUBLE from_val, to_val; + + if (not values.empty()) { + LONG vi = F2T((values.size()-1) * seek); + if (vi >= LONG(values.size())-1) vi = values.size() - 2; + + read_numseq(values[vi], { &from_val }); + read_numseq(values[vi+1], { &to_val } ); + + // Recompute the seek position to fit between the two values + const DOUBLE mod = 1.0 / DOUBLE(values.size() - 1); + seek = (seek >= 1.0) ? 1.0 : fmod(seek, mod) / mod; + } + else if (not from.empty()) { + if (not to.empty()) { + read_numseq(from, { &from_val }); + read_numseq(to, { &to_val } ); + } + else if (not by.empty()) { + return 0; + } + } + + const auto offset = to_val; + + if ((accumulate) and (repeat_count)) { + // Cumulative animation is not permitted for: + // * The 'to animation' where 'from' is undefined. + // * Animations that do not repeat + + from_val += offset * repeat_index; + to_val += offset * repeat_index; + } + + if (additive IS ADD::SUM) { + from_val += offset; + to_val += offset; + } + + if (calc_mode IS CMODE::DISCRETE) { + if (seek < 0.5) return from_val; + else return to_val; + } + else { // CMODE::LINEAR + return from_val + ((to_val - from_val) * seek); + } +} + +//******************************************************************************************************************** +// Return an interpolated value based on the values or from/to/by settings. + +DOUBLE anim_base::get_dimension() +{ + DOUBLE from_val, to_val; + + if (not values.empty()) { + LONG vi = F2T((values.size()-1) * seek); + if (vi >= LONG(values.size())-1) vi = values.size() - 2; + + read_numseq(values[vi], { &from_val }); + read_numseq(values[vi+1], { &to_val } ); + + // Recompute the seek position to fit between the two values + const DOUBLE mod = 1.0 / DOUBLE(values.size() - 1); + seek = (seek >= 1.0) ? 1.0 : fmod(seek, mod) / mod; + } + else if (not from.empty()) { + if (not to.empty()) { + read_numseq(from, { &from_val }); + read_numseq(to, { &to_val } ); + } + else if (not by.empty()) { + return 0; + } + } + + const auto offset = to_val; + + if ((accumulate) and (repeat_count)) { + // Cumulative animation is not permitted for: + // * The 'to animation' where 'from' is undefined. + // * Animations that do not repeat + + from_val += offset * repeat_index; + to_val += offset * repeat_index; + } + + if (additive IS ADD::SUM) { + from_val += offset; + to_val += offset; + } + + if (calc_mode IS CMODE::DISCRETE) { + if (seek < 0.5) return from_val; + else return to_val; + } + else { // CMODE::LINEAR + return from_val + ((to_val - from_val) * seek); + } +} + +//******************************************************************************************************************** + +FRGB anim_base::get_colour_value() +{ + VectorPainter from_col, to_col; + + if (not values.empty()) { + LONG vi = F2T((values.size()-1) * seek); + if (vi >= LONG(values.size())-1) vi = values.size() - 2; + vecReadPainter(NULL, values[vi].c_str(), &from_col, NULL); + vecReadPainter(NULL, values[vi+1].c_str(), &to_col, NULL); + + const DOUBLE mod = 1.0 / DOUBLE(values.size() - 1); + seek = (seek >= 1.0) ? 1.0 : fmod(seek, mod) / mod; + } + else if (not from.empty()) { + if (not to.empty()) { + vecReadPainter(NULL, from.c_str(), &from_col, NULL); + vecReadPainter(NULL, to.c_str(), &to_col, NULL); + } + else if (not by.empty()) { + return { 0, 0, 0, 0 }; + } + } + else return { 0, 0, 0, 0 }; + + if (calc_mode IS CMODE::DISCRETE) { + if (seek < 0.5) return from_col.Colour; + else return to_col.Colour; + } + else { // CMODE::LINEAR + // Linear RGB interpolation is superior to operating on raw RGB values. + + glLinearRGB.convert(from_col.Colour); + glLinearRGB.convert(to_col.Colour); + + auto result = FRGB { + FLOAT(from_col.Colour.Red + ((to_col.Colour.Red - from_col.Colour.Red) * seek)), + FLOAT(from_col.Colour.Green + ((to_col.Colour.Green - from_col.Colour.Green) * seek)), + FLOAT(from_col.Colour.Blue + ((to_col.Colour.Blue - from_col.Colour.Blue) * seek)), + FLOAT(from_col.Colour.Alpha + ((to_col.Colour.Alpha - from_col.Colour.Alpha) * seek)) + }; + + glLinearRGB.invert(result); + return result; + } +} + +//******************************************************************************************************************** +// Return true if the animation has started + +bool anim_base::started(DOUBLE CurrentTime) +{ + if (not first_time) first_time = CurrentTime; + + if (start_time) return true; + if (repeat_index > 0) return true; + + if (begin_offset) { + // Check if one of the animation's begin triggers has been tripped. + const DOUBLE elapsed = CurrentTime - start_time; + if (elapsed < begin_offset) return false; + } + + start_time = CurrentTime; + return true; +} + +//******************************************************************************************************************** +// Advance the seek position to represent the next frame. + +void anim_base::next_frame(DOUBLE CurrentTime) { - if (SVG->Animations.empty()) return ERR::Okay; + const DOUBLE elapsed = CurrentTime - start_time; + seek = elapsed / duration; // A value between 0 and 1.0 + + if (seek >= 1.0) { // Check if the sequence has ended. + if ((repeat_count < 0) or (repeat_index+1 < repeat_count)) { + repeat_index++; + start_time = CurrentTime; + seek = 0; + return; + } + else { + end_time = CurrentTime; // Setting the end-time will prevent further animation after the completion of this frame. + seek = 1.0; // Necessary in case the seek range calculation has overflowed + } + } + + // repeat_duration prevents the animation from running past a fixed number of seconds since it started. + if ((repeat_duration > 0) and (elapsed > repeat_duration)) { + end_time = CurrentTime; // End the animation. + seek = 1.0; + } +} + +//******************************************************************************************************************** +// Set common animation properties + +static ERR set_anim_property(extSVG *Self, anim_base &Anim, objVector *Vector, XMLTag &Tag, ULONG Hash, const std::string_view Value) +{ + switch (Hash) { + case SVF_ID: + Anim.id = Value; + add_id(Self, Tag, Value); + break; + + case SVF_ATTRIBUTENAME: // Name of the target attribute affected by the From and To values. + Anim.target_attrib = Value; + break; + + case SVF_ATTRIBUTETYPE: // Namespace of the target attribute: XML, CSS, auto + if (iequals("XML", Value)) Anim.attrib_type = ATT::XML; + else if (iequals("CSS", Value)) Anim.attrib_type = ATT::CSS; + else if (iequals("auto", Value)) Anim.attrib_type = ATT::AUTO; + break; + + case SVF_FILL: // freeze, remove + if (iequals("freeze", Value)) Anim.freeze = true; // Freeze the effect value at the last value of the duration (i.e. keep the last frame). + else if (iequals("remove", Value)) Anim.freeze = true; // The default. The effect is stopped when the duration is over. + break; + + case SVF_ADDITIVE: // replace, sum + if (iequals("replace", Value)) Anim.additive = ADD::REPLACE; // The animation values replace the underlying values of the target vector's attributes. + else if (iequals("sum", Value)) Anim.additive = ADD::SUM; // The animation adds to the underlying values of the target vector. + break; + + case SVF_ACCUMULATE: + if (iequals("none", Value)) Anim.accumulate = false; // Repeat iterations are not cumulative. This is the default. + else if (iequals("sum", Value)) Anim.accumulate = true; // Each repeated iteration builds on the last value of the previous iteration. + break; + + case SVF_FROM: // The starting value of the animation. + Anim.from = Value; + break; + + // It is not legal to specify both 'by' and 'to' attributes - if both are specified, only the to attribute will + // be used (the by will be ignored). + + case SVF_TO: // Specifies the ending value of the animation. + Anim.to = Value; + break; + + case SVF_BY: // Specifies a relative offset value for the animation. + Anim.by = Value; + break; + + case SVF_BEGIN: + // Defines when the element should become active. Specified as a semi-colon list. + // offset: A clock-value that is offset from the moment the animation is activated. + // id.end/begin: Reference to another animation's begin or end to determine when the animation starts. + // event: An event reference like 'focusin' determines that the animation starts when the event is triggered. + // id.repeat(value): Reference to another animation, repeat when the given value is reached. + // access-key: The animation starts when a keyboard key is pressed. + // clock: A real-world clock time (not supported) + Anim.begin_offset = read_time(Value); + break; + + case SVF_END: + // The animation ends when one of the triggers is reached. A semi-colon list of multiple values is permitted + // and documented as the 'end-value-list'. End is paired with 'begin' and should be parsed in the same way. + break; + + case SVF_DUR: // 4s, 02:33, 12:10:53, 45min, 4ms, 12.93, 1h, 'media', 'indefinite' + if (iequals("media", Value)) Anim.duration = 0; // Does not apply to animation + else if (iequals("indefinite", Value)) Anim.duration = -1; + else Anim.duration = read_time(Value); + break; - for (auto &anim : SVG->Animations) { - if (anim.Values.size() < 2) continue; // Skip animation if no From and To list is specified. - if (anim.EndTime) continue; -restart: - { - LARGE current_time = PreciseTime() / 1000LL; + case SVF_MIN: // Specifies the minimum value of the active duration. + if (iequals("media", Value)) Anim.min_duration = 0; // Does not apply to animation + else Anim.min_duration = read_time(Value); + break; - if (!anim.StartTime) { - // Check if one of the animation's begin triggers has been tripped. If there are no triggers then the - // animation can start immediately. + case SVF_MAX: // Specifies the maximum value of the active duration. + if (iequals("media", Value)) Anim.max_duration = 0; // Does not apply to animation + else Anim.max_duration = read_time(Value); + break; - anim.StartTime = current_time; - if (!anim.FirstTime) anim.FirstTime = anim.StartTime; + case SVF_CALCMODE: // Specifies the interpolation mode for the animation. + if (iequals("discrete", Value)) Anim.calc_mode = CMODE::DISCRETE; + else if (iequals("linear", Value)) Anim.calc_mode = CMODE::LINEAR; + else if (iequals("paced", Value)) Anim.calc_mode = CMODE::PACED; + else if (iequals("spline", Value)) Anim.calc_mode = CMODE::SPLINE; + break; + + case SVF_RESTART: // always, whenNotActive, never + if (iequals("always", Value)) Anim.restart = RST::ALWAYS; + else if (iequals("whenNotActive", Value)) Anim.restart = RST::WHEN_NOT_ACTIVE; + else if (iequals("never", Value)) Anim.restart = RST::NEVER; + break; + + case SVF_REPEATDUR: // Specifies the total duration for repeat. + if (iequals("indefinite", Value)) Anim.repeat_duration = -1; + else Anim.repeat_duration = read_time(Value); + break; + + case SVF_REPEATCOUNT: // Specifies the number of iterations of the animation function. Integer, 'indefinite' + if (iequals("indefinite", Value)) Anim.repeat_count = -1; + else Anim.repeat_count = read_time(Value); + break; + + // Similar to 'from' and 'to', this is a series of values that are interpolated over the time line. + // If a list of values is specified, any from, to and by attribute values are ignored. + case SVF_VALUES: { + Anim.values.clear(); + LONG s, v = 0; + while ((v < std::ssize(Value)) and (std::ssize(Anim.values) < MAX_VALUES)) { + while ((Value[v]) and (Value[v] <= 0x20)) v++; + for (s=v; (Value[s]) and (Value[s] != ';'); s++); + Anim.values.push_back(std::string(Value.substr(v, s-v))); + v = s; + if (Value[v] IS ';') v++; } + break; + } + + case SVF_KEYPOINTS: + // Takes a semicolon-separated list of floating point values between 0 and 1 and indicates how far along + // the motion path the object shall move at the moment in time specified by corresponding ‘keyTimes’ + // value. Distance calculations use the user agent's distance along the path algorithm. Each progress + // value in the list corresponds to a value in the ‘keyTimes’ attribute list. + break; + + case SVF_KEYTIMES: + break; + + case SVF_KEYSPLINES: + break; + + case SVF_EXTERNALRESOURCESREQUIRED: + // Deprecated + break; + } + + return ERR::Okay; +} + +//******************************************************************************************************************** + +void anim_colour::perform() +{ + +} + +//******************************************************************************************************************** + +static ERR motion_callback(objVector *Vector, LONG Index, LONG Cmd, DOUBLE X, DOUBLE Y, anim_motion &Motion) +{ + Motion.points.push_back(anim_motion::POINT { FLOAT(X), FLOAT(Y) }); + return ERR::Okay; +}; - DOUBLE elapsed = (current_time - anim.StartTime); - DOUBLE frame = elapsed / (anim.Duration * 1000.0); // A value between 0 and 1.0 +void anim_motion::perform() +{ + DOUBLE x1, y1, x2, y2, angle = -1; + + pf::ScopedObjectLock vector(target_vector, 1000); + if (vector.granted()) { + // Note that the order of processing here is important, and matches the priorities documented for SVG's + // animateMotion property. + + if ((mpath) or (not path.empty())) { + LONG new_timestamp; + vector->get(FID_PathTimestamp, &new_timestamp); - if (frame >= 1.0) { // Check if the sequence has ended. - anim.RepeatIndex++; - if ((anim.RepeatCount < 0) or (anim.RepeatIndex <= anim.RepeatCount)) { - anim.StartTime = 0; - goto restart; + if ((points.empty()) or (path_timestamp != new_timestamp)) { + // Trace the path and store its points. Transforms are completely ignored when pulling the path from + // an external source. + + auto call = C_FUNCTION(motion_callback, this); + + points.clear(); + if (mpath) { + if ((vecTracePath(mpath, &call, vector->get(FID_DisplayScale), false) != ERR::Okay) or (points.empty())) return; } - else { - anim.EndTime = current_time; // Setting the end-time will prevent further animation after the completion of this frame. - frame = 1.0; // Necessary in case the frame range calculation has overflowed + else if ((vecTracePath(*path, &call, 1.0, false) != ERR::Okay) or (points.empty())) return; + + vector->get(FID_PathTimestamp, &path_timestamp); + + if ((auto_rotate IS ART::AUTO) or (auto_rotate IS ART::AUTO_REVERSE)) { + // Rotation angles are pre-calculated. Start by calculating the angle from point to point. + + std::vector precalc(points.size()); + POINT prev = points[0]; + precalc[0] = get_angle(points[0], points[1]); + for (LONG i=1; i < std::ssize(points)-1; i++) { + precalc[i] = get_angle(prev, points[i]); + prev = points[i]; + } + precalc[points.size()-1] = precalc[points.size()-2]; + + // Average out the angle for each point so that they have a smoother flow. + + angles.clear(); + angles.reserve(precalc.size()); + angles.push_back(precalc[0]); + for (LONG i=1; i < std::ssize(precalc)-1; i++) { + angles.push_back((precalc[i] + precalc[i-1] + precalc[i+1]) / 3); + } + angles.push_back(precalc.back()); } } - // RepeatDuration prevents the animation from running past a fixed number of seconds since it started. - if ((anim.RepeatDuration > 0) and ((DOUBLE)(current_time - anim.StartTime) / 1000.0 > anim.RepeatDuration)) { - anim.EndTime = current_time; // End the animation. - frame = 1.0; + LONG vi = F2T((std::ssize(points)-1) * seek); + if (vi >= std::ssize(points)-1) vi = std::ssize(points) - 2; + + x1 = points[vi].x; + y1 = points[vi].y; + x2 = points[vi+1].x; + y2 = points[vi+1].y; + + // Recompute the seek position to fit between the two values + const DOUBLE mod = 1.0 / DOUBLE(points.size() - 1); + seek = (seek >= 1.0) ? 1.0 : fmod(seek, mod) / mod; + + if ((auto_rotate IS ART::AUTO) or (auto_rotate IS ART::AUTO_REVERSE)) { + angle = angles[vi] + ((angles[vi+1] - angles[vi]) * seek); + if (auto_rotate IS ART::AUTO_REVERSE) angle += 180.0; } + } + else if (not values.empty()) { + // Values are x,y coordinate pairs. + LONG vi = F2T((values.size()-1) * seek); + if (vi >= LONG(values.size())-1) vi = values.size() - 2; - LONG vi = F2T((anim.Values.size()-1) * frame); - if (vi >= LONG(anim.Values.size())-1) vi = anim.Values.size() - 2; + read_numseq(values[vi], { &x1, &y1 }); + read_numseq(values[vi+1], { &x2, &y2 } ); - if (anim.Transform) { // Animated transform - objVector *vector; - if (AccessObject(anim.TargetVector, 1000, &vector) IS ERR::Okay) { - if (!anim.Matrix) { - vecNewMatrix(vector, &anim.Matrix); - } + // Recompute the seek position to fit between the two values + const DOUBLE mod = 1.0 / DOUBLE(values.size() - 1); + seek = (seek >= 1.0) ? 1.0 : fmod(seek, mod) / mod; + } + else if (not from.empty()) { + if (not to.empty()) { + read_numseq(from, { &x1, &y1 }); + read_numseq(to, { &x2, &y2 } ); + } + else if (not by.empty()) { + return; + } + } + else return; + } - switch(anim.Transform) { - case AT_TRANSLATE: break; - case AT_SCALE: break; - case AT_ROTATE: { - DOUBLE from_angle = 0, from_cx = 0, from_cy = 0, to_angle = 0, to_cx = 0, to_cy = 0; - read_numseq(anim.Values[vi], { &from_angle, &from_cx, &from_cy }); - read_numseq(anim.Values[vi+1], { &to_angle, &to_cx, &to_cy } ); - - DOUBLE mod = 1.0 / (DOUBLE)(anim.Values.size() - 1); - DOUBLE ratio; - if (frame == 1.0) ratio = 1.0; - else ratio = fmod(frame, mod) / mod; - - DOUBLE new_angle = from_angle + ((to_angle - from_angle) * ratio); - DOUBLE new_cx = from_cx + ((to_cx - from_cx) * ratio); - DOUBLE new_cy = from_cy + ((to_cy - from_cy) * ratio); - - vecResetMatrix(anim.Matrix); - vecRotate(anim.Matrix, new_angle, new_cx, new_cy); - break; - } - case AT_SKEW_X: break; - case AT_SKEW_Y: break; - default: break; - } + if (not matrix) vecNewMatrix(*vector, &matrix); + vecResetMatrix(matrix); - ReleaseObject(vector); + if (angle != -1) vecRotate(matrix, angle, 0, 0); + else if (auto_rotate IS ART::FIXED) vecRotate(matrix, rotate, 0, 0); + + if (calc_mode IS CMODE::DISCRETE) { + if (seek < 0.5) vecTranslate(matrix, x1, y1); + else vecTranslate(matrix, x2, y2); + } + else { // CMODE::LINEAR + x1 = x1 + ((x2 - x1) * seek); + y1 = y1 + ((y2 - y1) * seek); + vecTranslate(matrix, x1, y1); + } +} + +//******************************************************************************************************************** + +void anim_transform::perform() +{ + pf::ScopedObjectLock vector(target_vector, 1000); + if (vector.granted()) { + if (not matrix) vecNewMatrix(*vector, &matrix); + + switch(type) { + case AT::TRANSLATE: break; + case AT::SCALE: break; + case AT::ROTATE: { + DOUBLE from_angle = 0, from_cx = 0, from_cy = 0, to_angle = 0, to_cx = 0, to_cy = 0; + + if (not values.empty()) { + LONG vi = F2T((values.size()-1) * seek); + if (vi >= LONG(values.size())-1) vi = values.size() - 2; + + read_numseq(values[vi], { &from_angle, &from_cx, &from_cy }); + read_numseq(values[vi+1], { &to_angle, &to_cx, &to_cy } ); + } + else if (not from.empty()) { + read_numseq(from, { &from_angle, &from_cx, &from_cy }); + if (not to.empty()) { + read_numseq(to, { &to_angle, &to_cx, &to_cy } ); + } + else if (not by.empty()) { + read_numseq(by, { &to_angle, &to_cx, &to_cy } ); + to_angle += from_angle; + to_cx += from_cx; + to_cy += from_cy; + } + else break; } + else break; + + DOUBLE mod = 1.0 / (DOUBLE)(values.size() - 1); + DOUBLE ratio; + if (seek == 1.0) ratio = 1.0; + else ratio = fmod(seek, mod) / mod; + + DOUBLE new_angle = from_angle + ((to_angle - from_angle) * ratio); + DOUBLE new_cx = from_cx + ((to_cx - from_cx) * ratio); + DOUBLE new_cy = from_cy + ((to_cy - from_cy) * ratio); + + vecResetMatrix(matrix); + vecRotate(matrix, new_angle, new_cx, new_cy); + break; } - else { // Animated motion + case AT::SKEW_X: break; + case AT::SKEW_Y: break; + default: break; + } + } +} +//******************************************************************************************************************** +// +// +// + +void anim_value::perform() +{ + pf::Log log(__FUNCTION__); + + pf::ScopedObjectLock vector(target_vector, 1000); + if (vector.granted()) { + // Determine the type of the attribute that we're targeting, then interpolate the value and set it. + + switch(StrHash(target_attrib)) { + case SVF_FONT_SIZE: { + auto val = get_numeric_value(); + vector->set(FID_FontSize, val); + break; + } + + case SVF_FILL: { + auto val = get_colour_value(); + vector->setArray(FID_FillColour, (FLOAT *)&val, 4); + break; + } + + case SVF_OPACITY: { + auto val = get_numeric_value(); + vector->set(FID_Opacity, val); + break; + } + + case SVF_X: { + auto val = get_dimension(); + vector->set(FID_X, val); + break; + } + + case SVF_Y: { + auto val = get_dimension(); + vector->set(FID_Y, val); + break; + } + + case SVF_WIDTH: { + auto val = get_dimension(); + vector->set(FID_Width, val); + break; + } + + case SVF_HEIGHT: { + auto val = get_dimension(); + vector->set(FID_Height, val); + break; } } } +} + +//******************************************************************************************************************** + +static ERR animation_timer(extSVG *SVG, LARGE TimeElapsed, LARGE CurrentTime) +{ + if (SVG->Animations.empty()) { + pf::Log log(__FUNCTION__); + log.msg("All animations processed, timer suspended."); + return ERR::Terminate; + } + + for (auto &record : SVG->Animations) { + std::visit([ &record ](auto &&anim) { + if (anim.end_time) return; + + DOUBLE current_time = DOUBLE(PreciseTime()) / 1000000.0; + + if (not anim.started(current_time)) return; + anim.next_frame(current_time); + anim.perform(); + }, record); + } SVG->Scene->Viewport->draw(); diff --git a/src/svg/animation.h b/src/svg/animation.h index 7efb22886..662be17cc 100644 --- a/src/svg/animation.h +++ b/src/svg/animation.h @@ -1,35 +1,121 @@ -enum { - AT_TRANSLATE=1, AT_SCALE, AT_ROTATE, AT_SKEW_X, AT_SKEW_Y +enum class AT : char { // Transform Type + TRANSLATE = 1, SCALE, ROTATE, SKEW_X, SKEW_Y +}; + +enum class ADD : char { // Additive + SUM = 0, REPLACE +}; + +enum class ATT : char { // Attribute Type + AUTO = 0, CSS, XML +}; + +enum class ART: char { + NIL = 0, + AUTO, + AUTO_REVERSE, + FIXED +}; + +enum class CMODE : char { // Specifies the interpolation mode for the animation. + LINEAR = 0, // Simple linear interpolation between values is used to calculate the animation function. + DISCRETE, // The animation function will jump from one value to the next without any interpolation. + PACED, // Defines interpolation to produce an even pace of change across the animation. This is only supported for values that define a linear numeric range, and for which some notion of "distance" between points can be calculated (e.g. position, width, height, etc.). Any keyTimes or keySplines will be ignored. + SPLINE // Interpolates from one value in the values list to the next according to a time function defined by a cubic Bezier spline. The points of the spline are defined in the keyTimes attribute, and the control points for each interval are defined in the keySplines attribute. }; constexpr LONG MAX_VALUES = 8; -struct svgAnimation { - std::vector Values; - struct VectorMatrix *Matrix = NULL; // Exclusive transform matrix for animation. - OBJECTID TargetVector = 0; - std::string TargetAttribute; // Name of the target attribute affected by the From and To values. - std::string ID; // Identifier for the animation - DOUBLE Duration = 0; // Measured in seconds, anything < 0 means infinite. - DOUBLE MinDuration = 0; // The minimum value of the active duration. If zero, the active duration is not constrained. - DOUBLE MaxDuration = 0; // The maximum value of the active duration. - DOUBLE RepeatDuration = 0; // The animation will be allowed to repeat for up to the number of seconds indicated. The time includes the initial loop. - DOUBLE End = 0; - LARGE FirstTime = 0; // Time-stamp of the first iteration - LARGE StartTime = 0; // This is time-stamped once the animation has started (the first begin event is hit) - LARGE EndTime = 0; // This is time-stamped once the animation has finished all of its cycles (including repetitions) - LONG RepeatCount; // Repetition count. Anything < 0 means infinite. - LONG RepeatIndex; // Current index within the repeat cycle. - UBYTE Transform = 0; - UBYTE Restart = 0; - bool Freeze = false; - bool Replace = false; - bool Accumulate = false; -}; - -enum { - RST_ALWAYS=0, - RST_WHEN_NOT_ACTIVE, - RST_NEVER +enum class RST : char { // Restart + ALWAYS = 0, + WHEN_NOT_ACTIVE, + NEVER +}; + +class anim_base { +public: + std::vector values; // Set of discrete values that override 'from', 'to', 'by' + std::string from; // Start from this value. Ignored if 'values' is defined. + std::string to, by; // Note that 'to' and 'by' are mutually exclusive, with 'to' as the preference. + std::string target_attrib; // Name of the target attribute affected by the From and To values. + std::string id; // Identifier for the animation + struct VectorMatrix *matrix = NULL; // Exclusive transform matrix for animation. + DOUBLE begin_offset = 0; // Start animating after this much time (in seconds) has elapsed. + DOUBLE repeat_duration = 0; // The animation will be allowed to repeat for up to the number of seconds indicated. The time includes the initial loop. + DOUBLE min_duration = 0; // The minimum value of the active duration. If zero, the active duration is not constrained. + DOUBLE max_duration = 0; // The maximum value of the active duration. + DOUBLE duration = 0; // Measured in seconds, anything < 0 means infinite. + DOUBLE first_time = 0; // Time-stamp of the first iteration + DOUBLE start_time = 0; // This is time-stamped once the animation has started (the first begin event is hit) + DOUBLE end_time = 0; // This is time-stamped once the animation has finished all of its cycles (including repetitions) + DOUBLE end = 0; + DOUBLE seek = 0; // Current seek position, between 0 - 1.0 + OBJECTID target_vector = 0; + LONG repeat_count = 0; // Repetition count. Anything < 0 means infinite. + LONG repeat_index = 0; // Current index within the repeat cycle. + CMODE calc_mode = CMODE::LINEAR; + RST restart = RST::ALWAYS; + ATT attrib_type = ATT::AUTO; + ADD additive = ADD::SUM; + bool freeze = false; + bool accumulate = false; + + anim_base(OBJECTID pTarget) : target_vector(pTarget) { } + + DOUBLE get_dimension(); + DOUBLE get_numeric_value(); + FRGB get_colour_value(); + bool started(DOUBLE); + void next_frame(DOUBLE); + + virtual void perform() = 0; + virtual bool is_valid() { + if (!values.empty()) return true; + if ((!to.empty()) or (!by.empty())) return true; + return false; + } +}; + +class anim_transform : public anim_base { +public: + AT type; + anim_transform(OBJECTID pTarget) : anim_base(pTarget) { } + void perform(); +}; + +class anim_motion : public anim_base { +public: + typedef struct { FLOAT x, y; } POINT; + ART auto_rotate = ART::NIL; // Inline rotation along the path + DOUBLE rotate = 0; // Fixed angle rotation + objVector *mpath = NULL; // External vector path + pf::GuardedObject path; // Client provided path sequence + std::vector points; + std::vector angles; // Precalc'd angles for rotation along paths + LONG path_timestamp; + + anim_motion(OBJECTID pTarget) : anim_base(pTarget) { } + void perform(); + + bool is_valid() { + if (!values.empty()) return true; + if (path.id) return true; + if (mpath) return true; + if ((!to.empty()) or (!by.empty())) return true; + return false; + } +}; + +class anim_colour : public anim_base { +public: + anim_colour(OBJECTID pTarget) : anim_base(pTarget) { } + void perform(); }; + +class anim_value : public anim_base { +public: + anim_value(OBJECTID pTarget) : anim_base(pTarget) { } + void perform(); +}; + diff --git a/src/svg/parser.cpp b/src/svg/parser.cpp index fad04b0ee..50653e4b0 100644 --- a/src/svg/parser.cpp +++ b/src/svg/parser.cpp @@ -515,13 +515,13 @@ static ERR parse_fe_colour_matrix(extSVG *Self, objVectorFilter *Filter, XMLTag } fx->set(FID_Mode, LONG(mode)); - if (mode IS CM::MATRIX) SetArray(fx, FID_Values|TDOUBLE, (APTR)m, CM_SIZE); + if ((mode IS CM::MATRIX) and (m)) fx->setArray(FID_Values, (DOUBLE *)m, CM_SIZE); break; } case SVF_VALUES: { auto m = read_array(val, CM_SIZE); - SetArray(fx, FID_Values|TDOUBLE, m); + fx->setArray(FID_Values, m.data(), CM_SIZE); break; } @@ -1553,8 +1553,10 @@ static ERR process_shape(extSVG *Self, CLASSID VectorID, svgState &State, XMLTag for (auto &child : Tag.Children) { if (child.isTag()) { switch(StrHash(child.name())) { - case SVF_ANIMATETRANSFORM: xtag_animatetransform(Self, child, vector); break; - case SVF_ANIMATEMOTION: xtag_animatemotion(Self, child, vector); break; + case SVF_ANIMATE: xtag_animate(Self, child, vector); break; + case SVF_ANIMATECOLOR: xtag_animate_colour(Self, child, vector); break; + case SVF_ANIMATETRANSFORM: xtag_animate_transform(Self, child, vector); break; + case SVF_ANIMATEMOTION: xtag_animate_motion(Self, child, vector); break; case SVF_PARASOL_MORPH: xtag_morph(Self, child, vector); break; case SVF_TEXTPATH: if (VectorID IS ID_VECTORTEXT) { @@ -1616,7 +1618,10 @@ static ERR xtag_default(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Pa case SVF_CONICGRADIENT: xtag_conicgradient(Self, Tag); break; case SVF_LINEARGRADIENT: xtag_lineargradient(Self, Tag); break; case SVF_SYMBOL: xtag_symbol(Self, Tag); break; - case SVF_ANIMATETRANSFORM: xtag_animatetransform(Self, Tag, Parent); break; + case SVF_ANIMATE: xtag_animate(Self, Tag, Parent); break; + case SVF_ANIMATECOLOR: xtag_animate_colour(Self, Tag, Parent); break; + case SVF_ANIMATETRANSFORM: xtag_animate_transform(Self, Tag, Parent); break; + case SVF_ANIMATEMOTION: xtag_animate_motion(Self, Tag, Parent); break; case SVF_FILTER: xtag_filter(Self, State, Tag); break; case SVF_DEFS: xtag_defs(Self, State, Tag, Parent); break; case SVF_CLIPPATH: xtag_clippath(Self, Tag); break; @@ -2330,6 +2335,26 @@ static void xtag_group(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Par SetOwner(group, Parent); if (!Tag.Children.empty()) state.applyTag(Tag); // Apply all group attribute values to the current state. process_attrib(Self, Tag, State, group); + + // Process child tags in advance. Unfortunately there is a requirement for to work only + // on objects that do not already have the target 'attributeName' already defined. We therefore need + // to do a bit of filtering and clone the tag for the affected elements. + + for (auto find_anim=Tag.Children.begin(); find_anim != Tag.Children.end(); ) { + if ((find_anim->isTag()) and (iequals("animate", find_anim->name()))) { + if (auto attrib = find_anim->attrib("attributeName")) { + for (auto &clone_to : Tag.Children) { + if (clone_to.isTag() and (not iequals("animate", clone_to.name()))) { + if (!clone_to.attrib(*attrib)) { + clone_to.Children.push_back(*find_anim); + } + } + } + } + find_anim = Tag.Children.erase(find_anim); + } + else find_anim++; + } // Process child tags @@ -2373,6 +2398,7 @@ static void xtag_svg(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Paren // specified a custom target, in which case there needs to be a way to discover the root of the SVG. if (!Self->Viewport) Self->Viewport = viewport; + // Process attributes auto state = State; @@ -2425,7 +2451,7 @@ static void xtag_svg(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Paren break; case SVF_ENABLE_BACKGROUND: - if ((StrMatch("true", val) IS ERR::Okay) or (StrMatch("1", val) IS ERR::Okay)) viewport->set(FID_EnableBkgd, TRUE); + if ((iequals("true", val)) or (iequals("1", val))) viewport->set(FID_EnableBkgd, TRUE); break; case SVF_ZOOMANDPAN: @@ -2463,7 +2489,7 @@ static void xtag_svg(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Paren // larger separation between "a" and "b" than "a b" (one space between "a" and "b"). case SVF_XML_SPACE: - if (StrMatch("preserve", val) IS ERR::Okay) Self->PreserveWS = TRUE; + if (iequals("preserve", val)) Self->PreserveWS = TRUE; else Self->PreserveWS = FALSE; break; @@ -2499,169 +2525,162 @@ static void xtag_svg(extSVG *Self, svgState &State, XMLTag &Tag, OBJECTPTR Paren // -static ERR xtag_animatetransform(extSVG *Self, XMLTag &Tag, OBJECTPTR Parent) +static ERR xtag_animate_transform(extSVG *Self, XMLTag &Tag, OBJECTPTR Parent) { pf::Log log(__FUNCTION__); Self->Animated = true; - svgAnimation anim; - anim.TargetVector = Parent->UID; + anim_transform anim(Parent->UID); for (LONG a=1; a < std::ssize(Tag.Attribs); a++) { auto &value = Tag.Attribs[a].Value; if (value.empty()) continue; - switch(StrHash(Tag.Attribs[a].Name)) { - case SVF_ATTRIBUTENAME: // Name of the target attribute affected by the From and To values. - anim.TargetAttribute = value; - break; - - case SVF_ATTRIBUTETYPE: // Namespace of the target attribute: XML, CSS, auto - //if (StrMatch("XML", value) IS ERR::Okay); - //else if (StrMatch("CSS", value) IS ERR::Okay); - //else if (StrMatch("auto", value) IS ERR::Okay); - break; - - case SVF_ID: - anim.ID = value; - add_id(Self, Tag, value); - break; - - case SVF_BEGIN: - // Defines when the element should become active. Specified as a semi-colon list. - // offset: A clock-value that is offset from the moment the animation is activated. - // id.end/begin: Reference to another animation's begin or end to determine when the animation starts. - // event: An event reference like 'focusin' determines that the animation starts when the event is triggered. - // id.repeat(value): Reference to another animation, repeat when the given value is reached. - // access-key: The animation starts when a keyboard key is pressed. - // clock: A real-world clock time (not supported) - break; - - case SVF_END: // The animation ends when one of the triggers is reached. Semi-colon list of multiple values permitted. - - break; - - case SVF_DUR: // 4s, 02:33, 12:10:53, 45min, 4ms, 12.93, 1h, 'media', 'indefinite' - if (StrMatch("media", value) IS ERR::Okay) anim.Duration = 0; // Does not apply to animation - else if (StrMatch("indefinite", value) IS ERR::Okay) anim.Duration = -1; - else anim.Duration = read_time(value); - break; - + auto hash = StrHash(Tag.Attribs[a].Name); + switch (hash) { case SVF_TYPE: // translate, scale, rotate, skewX, skewY - if (StrMatch("translate", value) IS ERR::Okay) anim.Transform = AT_TRANSLATE; - else if (StrMatch("scale", value) IS ERR::Okay) anim.Transform = AT_SCALE; - else if (StrMatch("rotate", value) IS ERR::Okay) anim.Transform = AT_ROTATE; - else if (StrMatch("skewX", value) IS ERR::Okay) anim.Transform = AT_SKEW_X; - else if (StrMatch("skewY", value) IS ERR::Okay) anim.Transform = AT_SKEW_Y; + if (iequals("translate", value)) anim.type = AT::TRANSLATE; + else if (iequals("scale", value)) anim.type = AT::SCALE; + else if (iequals("rotate", value)) anim.type = AT::ROTATE; + else if (iequals("skewX", value)) anim.type = AT::SKEW_X; + else if (iequals("skewY", value)) anim.type = AT::SKEW_Y; else log.warning("Unsupported type '%s'", value.c_str()); break; - case SVF_MIN: - if (StrMatch("media", value) IS ERR::Okay) anim.MinDuration = 0; // Does not apply to animation - else anim.MinDuration = read_time(value); + default: + set_anim_property(Self, anim, (objVector *)Parent, Tag, hash, value); break; + } + } - case SVF_MAX: - if (StrMatch("media", value) IS ERR::Okay) anim.MaxDuration = 0; // Does not apply to animation - else anim.MaxDuration = read_time(value); - break; + if (anim.is_valid()) Self->Animations.emplace_back(anim); + return ERR::Okay; +} - case SVF_FROM: { // The starting value of the animation. - if (anim.Values.empty()) anim.Values.push_back(value); - else anim.Values[0] = value; - break; - } +//******************************************************************************************************************** +// The ‘animate’ element is used to animate a single attribute or property over time. For example, to make a +// rectangle repeatedly fade away over 5 seconds: - case SVF_TO: { - if (anim.Values.size() >= 1) anim.Values[1] = value; - else anim.Values.insert(anim.Values.begin() + 1, value); - break; - } +static ERR xtag_animate(extSVG *Self, XMLTag &Tag, OBJECTPTR Parent) +{ + Self->Animated = true; - // Similar to from and to, this is a series of values that are interpolated over the time line. - case SVF_VALUES: { - anim.Values.clear(); - LONG s, v = 0; - while ((v < std::ssize(value)) and (std::ssize(anim.Values) < MAX_VALUES)) { - while ((value[v]) and (value[v] <= 0x20)) v++; - for (s=v; (value[s]) and (value[s] != ';'); s++); - anim.Values.push_back(value.substr(v, s-v)); - v = s; - if (value[v] IS ';') v++; - } - break; - } + anim_value anim(Parent->UID); - case SVF_RESTART: // always, whenNotActive, never - if (StrMatch("always", value) IS ERR::Okay) anim.Restart = RST_ALWAYS; - else if (StrMatch("whenNotActive", value) IS ERR::Okay) anim.Restart = RST_WHEN_NOT_ACTIVE; - else if (StrMatch("never", value) IS ERR::Okay) anim.Restart = RST_NEVER; - break; + for (LONG a=1; a < std::ssize(Tag.Attribs); a++) { + auto &value = Tag.Attribs[a].Value; + if (value.empty()) continue; - case SVF_REPEATDUR: - if (StrMatch("indefinite", value) IS ERR::Okay) anim.RepeatDuration = -1; - else anim.RepeatDuration = read_time(value); + auto hash = StrHash(Tag.Attribs[a].Name); + switch (hash) { + default: + set_anim_property(Self, anim, (objVector *)Parent, Tag, hash, value); break; + } + } - case SVF_REPEATCOUNT: // Integer, 'indefinite' - if (StrMatch("indefinite", value) IS ERR::Okay) anim.RepeatCount = -1; - else anim.RepeatCount = read_time(value); - break; + if (anim.is_valid()) Self->Animations.emplace_back(anim); + return ERR::Okay; +} - case SVF_FILL: // freeze, remove - if (StrMatch("freeze", value) IS ERR::Okay) anim.Freeze = true; // Freeze the effect value at the last value of the duration (i.e. keep the last frame). - else if (StrMatch("remove", value) IS ERR::Okay) anim.Freeze = true; // The default. The effect is stopped when the duration is over. - break; +//******************************************************************************************************************** - case SVF_ADDITIVE: // replace, sum - if (StrMatch("replace", value) IS ERR::Okay) anim.Replace = true; // The animation values replace the underlying values of the target vector's attributes. - else if (StrMatch("sum", value) IS ERR::Okay) anim.Replace = false; // The animation adds to the underlying values of the target vector. - break; +static ERR xtag_animate_colour(extSVG *Self, XMLTag &Tag, OBJECTPTR Parent) +{ + Self->Animated = true; + + anim_colour anim(Parent->UID); - case SVF_ACCUMULATE: - if (StrMatch("none", value) IS ERR::Okay) anim.Accumulate = false; // Repeat iterations are not cumulative. This is the default. - else if (StrMatch("sum", value) IS ERR::Okay) anim.Accumulate = true; // Each repeated iteration builds on the last value of the previous iteration. - break; + for (LONG a=1; a < std::ssize(Tag.Attribs); a++) { + auto &value = Tag.Attribs[a].Value; + if (value.empty()) continue; + auto hash = StrHash(Tag.Attribs[a].Name); + switch (hash) { default: + set_anim_property(Self, anim, (objVector *)Parent, Tag, hash, value); break; } } - Self->Animations.emplace_back(anim); + if (anim.is_valid()) Self->Animations.emplace_back(anim); return ERR::Okay; } //******************************************************************************************************************** // -static ERR xtag_animatemotion(extSVG *Self, XMLTag &Tag, OBJECTPTR Parent) +static ERR xtag_animate_motion(extSVG *Self, XMLTag &Tag, OBJECTPTR Parent) { Self->Animated = true; + + anim_motion anim(Parent->UID); for (LONG a=1; a < std::ssize(Tag.Attribs); a++) { - if (Tag.Attribs[a].Value.empty()) continue; + auto &value = Tag.Attribs[a].Value; + if (value.empty()) continue; - switch(StrHash(Tag.Attribs[a].Name)) { - case SVF_FROM: - break; - case SVF_TO: - break; - case SVF_DUR: - break; - case SVF_PATH: - //path="M 0 0 L 100 100" + auto hash = StrHash(Tag.Attribs[a].Name); + switch (hash) { + case SVF_PATH: // List of standard path commands, e.g. "M 0 0 L 100 100" + anim.path.set(objVectorPath::create::global( + fl::Name("motion_path"), + fl::Owner(Self->Scene->UID), + fl::Sequence(value), + fl::Visibility(VIS::HIDDEN))); + break; + + case SVF_ROTATE: + // Post-multiplies a supplemental transformation matrix onto the CTM of the target element to apply a + // rotation transformation about the origin of the current user coordinate system. The rotation + // transformation is applied after the supplemental translation transformation that is computed due to + // the ‘path’ attribute. + // + // auto: The object is rotated over time by the angle of the direction (i.e., directional tangent + // vector) of the motion path. + // + // auto-reverse: Indicates that the object is rotated over time by the angle of the direction (i.e., + // directional tangent vector) of the motion path plus 180 degrees. + // + // : Indicates that the target element has a constant rotation transformation applied to it, + // where the rotation angle is the specified number of degrees. + + if (iequals("auto", value)) anim.auto_rotate = ART::AUTO; + else if (iequals("auto-reverse", value)) anim.auto_rotate = ART::AUTO_REVERSE; + else { + anim.auto_rotate = ART::FIXED; + anim.rotate = strtod(value.c_str(), NULL); + } break; - case SVF_FILL: - // freeze = The last frame will be displayed at the end of the animation, rather than going back to the first frame. - break; + case SVF_ORIGIN: break; // Officially serves no purpose. + default: + set_anim_property(Self, anim, (objVector *)Parent, Tag, hash, value); break; } } + if (!Tag.Children.empty()) { + // Search for mpath references, e.g. + + for (auto &child : Tag.Children) { + if ((child.isTag()) and (StrMatch("mpath", child.name()) IS ERR::Okay)) { + auto href = child.attrib("xlink:href"); + if (!href) child.attrib("href"); + + if (href) { + objVector *path; + if (scFindDef(Self->Scene, href->c_str(), (OBJECTPTR *)&path) IS ERR::Okay) { + anim.mpath = path; + } + } + } + } + } + + if (anim.is_valid()) Self->Animations.emplace_back(anim); return ERR::Okay; } @@ -2935,13 +2954,13 @@ static ERR set_property(extSVG *Self, objVector *Vector, ULONG Hash, XMLTag &Tag // Note: For the time being, VectorRectangle doesn't support X2/Y2 as a concept. This would // cause problems if the client was to specify a scaled value here. auto width = FUNIT(FID_Width, StrValue); - SetField(Vector, FID_Width|TDOUBLE, std::abs(DOUBLE(width) - Vector->get(FID_X))); + Vector->setFields(fl::Width(std::abs(width - Vector->get(FID_X)))); return ERR::Okay; } case SVF_Y2: { auto height = FUNIT(FID_Height, StrValue); - SetField(Vector, FID_Height|TDOUBLE, std::abs(DOUBLE(height) - Vector->get(FID_Y))); + Vector->setFields(fl::Height(std::abs(height - Vector->get(FID_Y)))); return ERR::Okay; } } diff --git a/src/svg/svg.cpp b/src/svg/svg.cpp index d072ba9c0..2cf794420 100644 --- a/src/svg/svg.cpp +++ b/src/svg/svg.cpp @@ -19,6 +19,7 @@ Relevant SVG reference manuals: #include #include #include +#include #include #include #include @@ -37,6 +38,9 @@ JUMPTABLE_CORE JUMPTABLE_DISPLAY JUMPTABLE_VECTOR +static const DOUBLE DEG2RAD = 0.01745329251994329576923690768489; // Multiple any angle by this value to convert to radians +static const DOUBLE RAD2DEG = 57.295779513082320876798154814105; + static OBJECTPTR clSVG = NULL, clRSVG = NULL, modDisplay = NULL, modVector = NULL, modPicture = NULL; struct prvSVG { // Private variables for RSVG @@ -58,6 +62,10 @@ struct svgID { // All elements using the 'id' attribute will be registered with svgID() { TagIndex = -1; } }; +inline DOUBLE get_angle(anim_motion::POINT &A, anim_motion::POINT &B) { + return std::atan2(B.y - A.y, B.x - A.x) * RAD2DEG; +} + #include //******************************************************************************************************************** @@ -73,7 +81,7 @@ class extSVG : public objSVG { STRING Folder; std::string Colour = "rgb(0,0,0)"; // Default colour, used for 'currentColor' references OBJECTPTR Viewport; // First viewport (the tag) to be created on parsing the SVG document. - std::vector Animations; + std::vector> Animations; std::vector Inherit; TIMER AnimationTimer; WORD Cloning; // Incremented when inside a duplicated tag space, e.g. due to a tag @@ -106,30 +114,32 @@ struct svgState { //******************************************************************************************************************** -static ERR animation_timer(extSVG *, LARGE, LARGE); -static void convert_styles(objXML::TAGS &); -static ERR init_svg(void); -static ERR init_rsvg(void); -static void process_attrib(extSVG *, XMLTag &, svgState &, objVector *); -static void process_children(extSVG *, svgState &, XMLTag &, OBJECTPTR); -static void process_rule(extSVG *, objXML::TAGS &, KatanaRule *); -static ERR process_shape(extSVG *, CLASSID, svgState &, XMLTag &, OBJECTPTR, objVector * &); -static ERR save_svg_scan(extSVG *, objXML *, objVector *, LONG); -static ERR save_svg_defs(extSVG *, objXML *, objVectorScene *, LONG); -static ERR save_svg_scan_std(extSVG *, objXML *, objVector *, LONG); -static ERR save_svg_transform(VectorMatrix *, std::stringstream &); -static ERR set_property(extSVG *, objVector *, ULONG, XMLTag &, svgState &, std::string); -static ERR xtag_animatemotion(extSVG *, XMLTag &, OBJECTPTR Parent); -static ERR xtag_animatetransform(extSVG *, XMLTag &, OBJECTPTR); -static ERR xtag_default(extSVG *, svgState &, XMLTag &, OBJECTPTR, objVector * &); -static ERR xtag_defs(extSVG *, svgState &, XMLTag &, OBJECTPTR); -static void xtag_group(extSVG *, svgState &, XMLTag &, OBJECTPTR, objVector * &); -static ERR xtag_image(extSVG *, svgState &, XMLTag &, OBJECTPTR, objVector * &); -static void xtag_morph(extSVG *, XMLTag &, OBJECTPTR Parent); -static void xtag_svg(extSVG *, svgState &, XMLTag &, OBJECTPTR, objVector * &); -static void xtag_use(extSVG *, svgState &, XMLTag &, OBJECTPTR); -static ERR xtag_style(extSVG *, XMLTag &); -static void xtag_symbol(extSVG *, XMLTag &); +static ERR animation_timer(extSVG *, LARGE, LARGE); +static void convert_styles(objXML::TAGS &); +static ERR init_svg(void); +static ERR init_rsvg(void); +static void process_attrib(extSVG *, XMLTag &, svgState &, objVector *); +static void process_children(extSVG *, svgState &, XMLTag &, OBJECTPTR); +static void process_rule(extSVG *, objXML::TAGS &, KatanaRule *); +static ERR process_shape(extSVG *, CLASSID, svgState &, XMLTag &, OBJECTPTR, objVector * &); +static ERR save_svg_scan(extSVG *, objXML *, objVector *, LONG); +static ERR save_svg_defs(extSVG *, objXML *, objVectorScene *, LONG); +static ERR save_svg_scan_std(extSVG *, objXML *, objVector *, LONG); +static ERR save_svg_transform(VectorMatrix *, std::stringstream &); +static ERR set_property(extSVG *, objVector *, ULONG, XMLTag &, svgState &, std::string); +static ERR xtag_animate(extSVG *, XMLTag &, OBJECTPTR); +static ERR xtag_animate_colour(extSVG *, XMLTag &, OBJECTPTR); +static ERR xtag_animate_motion(extSVG *, XMLTag &, OBJECTPTR); +static ERR xtag_animate_transform(extSVG *, XMLTag &, OBJECTPTR); +static ERR xtag_default(extSVG *, svgState &, XMLTag &, OBJECTPTR, objVector * &); +static ERR xtag_defs(extSVG *, svgState &, XMLTag &, OBJECTPTR); +static void xtag_group(extSVG *, svgState &, XMLTag &, OBJECTPTR, objVector * &); +static ERR xtag_image(extSVG *, svgState &, XMLTag &, OBJECTPTR, objVector * &); +static void xtag_morph(extSVG *, XMLTag &, OBJECTPTR); +static void xtag_svg(extSVG *, svgState &, XMLTag &, OBJECTPTR, objVector * &); +static void xtag_use(extSVG *, svgState &, XMLTag &, OBJECTPTR); +static ERR xtag_style(extSVG *, XMLTag &); +static void xtag_symbol(extSVG *, XMLTag &); //******************************************************************************************************************** diff --git a/src/svg/tests/animation/rotate.svg b/src/svg/tests/animation/rotate.svg new file mode 100644 index 000000000..e53d6da87 --- /dev/null +++ b/src/svg/tests/animation/rotate.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + diff --git a/src/svg/tests/animation/w3-animate-elem-02-t.svg b/src/svg/tests/animation/w3-animate-elem-02-t.svg new file mode 100644 index 000000000..9fd432e44 --- /dev/null +++ b/src/svg/tests/animation/w3-animate-elem-02-t.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + anim.5 + + + + + + + + + + anim.6 + + + + + + + + + + anim.7 + + + + + + + + + + anim.8 + + + diff --git a/src/svg/tests/animation/w3-animate-elem-03-t.svg b/src/svg/tests/animation/w3-animate-elem-03-t.svg new file mode 100644 index 000000000..693a89456 --- /dev/null +++ b/src/svg/tests/animation/w3-animate-elem-03-t.svg @@ -0,0 +1,21 @@ + + + + + Turn Green + Stay the Same + Grow & Green + + + + diff --git a/src/svg/tests/animation/w3-animate-elem-04-t.svg b/src/svg/tests/animation/w3-animate-elem-04-t.svg new file mode 100644 index 000000000..f205aa9f5 --- /dev/null +++ b/src/svg/tests/animation/w3-animate-elem-04-t.svg @@ -0,0 +1,24 @@ + + + + + + Test a motion path + 'from'/'to' attribute. + + + 0 sec. + + 3+ sec. + + + + + diff --git a/src/svg/tests/animation/w3-animate-elem-05-t.svg b/src/svg/tests/animation/w3-animate-elem-05-t.svg new file mode 100644 index 000000000..55f212dbc --- /dev/null +++ b/src/svg/tests/animation/w3-animate-elem-05-t.svg @@ -0,0 +1,25 @@ + + + + + + Test a motion path + 'values' attribute. + + + 0 sec. + + 3+ + + 6+ + + + + + + diff --git a/src/svg/tests/animation/w3-animate-elem-06-t.svg b/src/svg/tests/animation/w3-animate-elem-06-t.svg new file mode 100644 index 000000000..34cf132cd --- /dev/null +++ b/src/svg/tests/animation/w3-animate-elem-06-t.svg @@ -0,0 +1,24 @@ + + + + + Test a motion path + 'path' attribute. + + + 0 sec. + + 6+ sec. + + + + + + diff --git a/src/svg/tests/animation/w3-animate-elem-07-t.svg b/src/svg/tests/animation/w3-animate-elem-07-t.svg new file mode 100644 index 000000000..1e67babe1 --- /dev/null +++ b/src/svg/tests/animation/w3-animate-elem-07-t.svg @@ -0,0 +1,25 @@ + + + + + + Test a motion path + 'mpath' element. + + + 0 sec. + + 6+ sec. + + + + + + + + diff --git a/src/svg/tests/filters/w3-filters-composite-02-b.svg b/src/svg/tests/filters/w3-filters-composite-02-b.svg index 14418c4a5..da23e9f30 100644 --- a/src/svg/tests/filters/w3-filters-composite-02-b.svg +++ b/src/svg/tests/filters/w3-filters-composite-02-b.svg @@ -149,5 +149,5 @@ - + diff --git a/src/svg/tests/test-svg.fluid b/src/svg/tests/test-svg.fluid index a8c4a840f..2ea8f0698 100644 --- a/src/svg/tests/test-svg.fluid +++ b/src/svg/tests/test-svg.fluid @@ -46,11 +46,11 @@ end ----------------------------------------------------------------------------------------------------------------------- function testCircles() hashTestSVG('paths/circles.svg', 0xc2063c38) end -function testMorph() hashTestSVG('paths/morph.svg', 0x50272d17) end +function testMorph() hashTestSVG('paths/morph.svg', 0x4f9e9c4f) end function testEllipseVertices() hashTestSVG('paths/ellipse_vertices.svg', 0x00d2264b) end function testPolygons() hashTestSVG('paths/polygons.svg', 0x2f17adec) end function testShapes() hashTestSVG('paths/shapes.svg', 0xd67937c9) end -function testSpirals() hashTestSVG('paths/spirals.svg', 0x84bb0e08) end +function testSpirals() hashTestSVG('paths/spirals.svg', 0x0d713688) end function testStrokes() hashTestSVG('paths/strokes.svg', 0x087fb44e) end function testSuperShapes() hashTestSVG('paths/supershapes.svg', 0x915ba38c) end function testSuperSpiral() hashTestSVG('paths/superspiral.svg', 0xd981df67) end @@ -71,7 +71,7 @@ function testComposite() hashTestSVG('filters/composite.svg', 0x66a96a5 function testConvolve() hashTestSVG('filters/convolve.svg', 0x29d31e69) end function testRockyLighting() hashTestSVG('filters/rocky_lighting.svg', 0x6e4322a9) end function testW3Composite1() hashTestSVG('filters/w3-composite.svg', 0xb0ac4263) end -function testW3Composite2() hashTestSVG('filters/w3-filters-composite-02-b.svg', 0xe1acd9c9) end +function testW3Composite2() hashTestSVG('filters/w3-filters-composite-02-b.svg', 0xebc363c1) end function testW3Displacement1() hashTestSVG('filters/w3-filters-displace-01-f.svg', 0xf2d1a46c) end function testW3Transfer() hashTestSVG('filters/w3-filters-comptran-01-b.svg', 0x6f8b62dd) end function testW3Transfer2() hashTestSVG('filters/w3-filters-color-02-b.svg', 0xd9e10af8) end diff --git a/src/svg/tests/text/w3-text-text-01-b.svg b/src/svg/tests/text/w3-text-text-01-b.svg index 4eadbe004..4ec58536f 100644 --- a/src/svg/tests/text/w3-text-text-01-b.svg +++ b/src/svg/tests/text/w3-text-text-01-b.svg @@ -26,7 +26,7 @@ - text-text-01-b.svg + Basic test of 'textLength' and 'lengthAdjust' attributes. diff --git a/src/svg/utility.cpp b/src/svg/utility.cpp index 6b23bdcac..b08c35b77 100644 --- a/src/svg/utility.cpp +++ b/src/svg/utility.cpp @@ -1,4 +1,7 @@ +static HSV rgb_to_hsl(FRGB Colour) __attribute__((unused)); +static FRGB hsl_to_rgb(HSV Colour) __attribute__((unused)); + #if defined(DEBUG) static void debug_tree(CSTRING Header, OBJECTPTR) __attribute__ ((unused)); static void debug_branch(CSTRING Header, OBJECTPTR, LONG &Level) __attribute__ ((unused)); @@ -41,6 +44,55 @@ static void debug_tree(CSTRING Header, OBJECTPTR Vector) #endif +//******************************************************************************************************************** + +static HSV rgb_to_hsl(FRGB Colour) +{ + DOUBLE vmax = std::ranges::max({ Colour.Red, Colour.Green, Colour.Blue }); + DOUBLE vmin = std::ranges::min({ Colour.Red, Colour.Green, Colour.Blue }); + DOUBLE light = (vmax + vmin) * 0.5; + + if (vmax IS vmin) return HSV { 0, 0, light }; + + DOUBLE sat = light, hue = light; + DOUBLE d = vmax - vmin; + sat = light > 0.5 ? d / (2 - vmax - vmin) : d / (vmax + vmin); + if (vmax IS Colour.Red) hue = (Colour.Green - Colour.Blue) / d + (Colour.Green < Colour.Blue ? 6.0 : 0.0); + if (vmax IS Colour.Green) hue = (Colour.Blue - Colour.Red) / d + 2.0; + if (vmax IS Colour.Blue) hue = (Colour.Red - Colour.Green) / d + 4.0; + hue /= 6.0; + + return HSV { hue, sat, light, Colour.Alpha }; +} + +//******************************************************************************************************************** + +static FRGB hsl_to_rgb(HSV Colour) +{ + auto hueToRgb = [](FLOAT p, FLOAT q, FLOAT t) -> FLOAT { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1.0/6.0) return p + (q - p) * 6.0 * t; + if (t < 1.0/2.0) return q; + if (t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; + return p; + }; + + if (Colour.Saturation == 0) { + return { FLOAT(Colour.Value), FLOAT(Colour.Value), FLOAT(Colour.Value), FLOAT(Colour.Alpha) }; + } + else { + const DOUBLE q = (Colour.Value < 0.5) ? Colour.Value * (1.0 + Colour.Saturation) : Colour.Value + Colour.Saturation - Colour.Value * Colour.Saturation; + const DOUBLE p = 2.0 * Colour.Value - q; + return { + hueToRgb(p, q, Colour.Hue + 1.0/3.0), + hueToRgb(p, q, Colour.Hue), + hueToRgb(p, q, Colour.Hue - 1.0/3.0), + FLOAT(Colour.Alpha) + }; + } +} + //******************************************************************************************************************** // Support for the 'currentColor' colour value. Finds the first parent with a defined fill colour and returns it. @@ -347,7 +399,7 @@ inline std::string_view next_value(const std::string_view Value) // The parser will break once the string value terminates, or an invalid character is encountered. Parsed characters // include: 0 - 9 , ( ) - + SPACE -static void read_numseq(std::string_view String, std::initializer_list Value) +static std::string_view read_numseq(std::string_view String, std::initializer_list Value) { for (DOUBLE *v : Value) { String = next_value(String); @@ -356,11 +408,13 @@ static void read_numseq(std::string_view String, std::initializer_list auto [ next, error ] = std::from_chars(String.data(), String.data() + String.size(), num); if ((!num) and ((!next) or (String IS next))) { // Invalid character or end-of-stream check. - return; + return String; } String = std::string_view(next, String.data() + String.size() - next); *v = num; } + + return String; } //******************************************************************************************************************** diff --git a/src/vector/agg/include/agg_path_storage.h b/src/vector/agg/include/agg_path_storage.h index cf2c3ff05..4a04f1162 100644 --- a/src/vector/agg/include/agg_path_storage.h +++ b/src/vector/agg/include/agg_path_storage.h @@ -78,7 +78,6 @@ namespace agg bool m_stop; }; - //-------------------------------------------------poly_container_adaptor template class poly_container_adaptor { public: typedef typename Container::value_type vertex_type; @@ -97,24 +96,20 @@ namespace agg m_stop(false) {} - void init(const Container& data, bool closed) - { + void init(const Container& data, bool closed) { m_container = &data; m_index = 0; m_closed = closed; m_stop = false; } - void rewind(unsigned) - { + void rewind(unsigned) { m_index = 0; m_stop = false; } - unsigned vertex(double* x, double* y) - { - if(m_index < m_container->size()) - { + unsigned vertex(double* x, double* y) { + if(m_index < m_container->size()) { bool first = m_index == 0; const vertex_type& v = (*m_container)[m_index++]; *x = v.x; @@ -122,8 +117,7 @@ namespace agg return first ? path_cmd_move_to : path_cmd_line_to; } *x = *y = 0.0; - if(m_closed and !m_stop) - { + if(m_closed and !m_stop) { m_stop = true; return path_cmd_end_poly | path_flags_close; } @@ -156,22 +150,19 @@ namespace agg m_stop(false) {} - void init(const Container& data, bool closed) - { + void init(const Container& data, bool closed) { m_container = &data; m_index = m_container->size() - 1; m_closed = closed; m_stop = false; } - void rewind(unsigned) - { + void rewind(unsigned) { m_index = m_container->size() - 1; m_stop = false; } - unsigned vertex(double* x, double* y) - { + unsigned vertex(double* x, double* y) { if (m_index >= 0) { bool first = m_index == int(m_container->size() - 1); const vertex_type& v = (*m_container)[m_index--]; @@ -194,23 +185,19 @@ namespace agg bool m_stop; }; - //--------------------------------------------------------line_adaptor - class line_adaptor - { + class line_adaptor { public: typedef double value_type; line_adaptor() : m_line(m_coord, 2, false) {} - line_adaptor(double x1, double y1, double x2, double y2) : m_line(m_coord, 2, false) - { + line_adaptor(double x1, double y1, double x2, double y2) : m_line(m_coord, 2, false) { m_coord[0] = x1; m_coord[1] = y1; m_coord[2] = x2; m_coord[3] = y2; } - void init(double x1, double y1, double x2, double y2) - { + void init(double x1, double y1, double x2, double y2) { m_coord[0] = x1; m_coord[1] = y1; m_coord[2] = x2; @@ -218,13 +205,11 @@ namespace agg m_line.rewind(0); } - void rewind(unsigned) - { + void rewind(unsigned) { m_line.rewind(0); } - unsigned vertex(double* x, double* y) - { + unsigned vertex(double* x, double* y) { return m_line.vertex(x, y); } @@ -233,7 +218,6 @@ namespace agg poly_plain_adaptor m_line; }; - //---------------------------------------------------------------path_base // A container to store vertices with their flags. // A path consists of a number of contours separated with "move_to" // commands. The path storage can keep and maintain more than one @@ -244,9 +228,8 @@ namespace agg // to navigate to the path afterwards. // // See also: vertex_source concept - //------------------------------------------------------------------------ - template class path_base - { + + template class path_base { public: typedef VertexContainer container_type; typedef path_base self_type; @@ -328,8 +311,6 @@ namespace agg bool empty() const { return total_vertices() == 0; } - // Accessors - //-------------------------------------------------------------------- const container_type& vertices() const { return m_vertices; } container_type& vertices() { return m_vertices; } @@ -349,9 +330,7 @@ namespace agg void modify_vertex(unsigned idx, double x, double y); void modify_vertex(unsigned idx, double x, double y, unsigned cmd); void modify_command(unsigned idx, unsigned cmd); - - // VertexSource interface - //-------------------------------------------------------------------- + void rewind(unsigned path_id); unsigned vertex(double* x, double* y); @@ -359,23 +338,20 @@ namespace agg // or in all paths. After calling arrange_orientations() or // arrange_orientations_all_paths(), all the polygons will have // the same orientation, i.e. path_flags_cw or path_flags_ccw - //-------------------------------------------------------------------- + unsigned arrange_polygon_orientation(unsigned start, int orientation); unsigned arrange_orientations(unsigned path_id, int orientation); void arrange_orientations_all_paths(int orientation); void invert_polygon(unsigned start); - // Flip all vertices horizontally or vertically, - // between x1 and x2, or between y1 and y2 respectively - //-------------------------------------------------------------------- + // Flip all vertices horizontally or vertically, between x1 and x2, or between y1 and y2 respectively + void flip_x(double x1, double x2); void flip_y(double y1, double y2); // Concatenate path. The path is added as is. - //-------------------------------------------------------------------- - template - void concat_path(VertexSource& vs, unsigned path_id = 0) - { + + template void concat_path(VertexSource& vs, unsigned path_id = 0) { double x, y; unsigned cmd; vs.rewind(path_id); @@ -386,16 +362,12 @@ namespace agg // Copy a path as-is, bypassing add_vertex() - template - void copy_path(VertexSource& vs) { + template void copy_path(VertexSource& vs) { m_vertices = vs.vertices(); } - //-------------------------------------------------------------------- - // Join path. The path is joined with the existing one, that is, - // it behaves as if the pen of a plotter was always down (drawing) - template - void join_path(VertexSource& vs, unsigned path_id = 0) + // Join path. The path is joined with the existing one, that is, it behaves as if the pen of a plotter was always down (drawing) + template void join_path(VertexSource& vs, unsigned path_id = 0) { double x, y; vs.rewind(path_id); @@ -424,7 +396,7 @@ namespace agg } // Concatenate polygon/polyline. - //-------------------------------------------------------------------- + template void concat_poly(const T* data, unsigned num_points, bool closed) { poly_plain_adaptor poly(data, num_points, closed); @@ -432,18 +404,16 @@ namespace agg } // Join polygon/polyline continuously. - //-------------------------------------------------------------------- + template void join_poly(const T* data, unsigned num_points, bool closed) { poly_plain_adaptor poly(data, num_points, closed); join_path(poly); } - //-------------------------------------------------------------------- void translate(double dx, double dy, unsigned path_id=0); void translate_all_paths(double dx, double dy); - //-------------------------------------------------------------------- template void transform(const Trans& trans, unsigned path_id=0) { auto num_ver = m_vertices.total_vertices(); @@ -458,17 +428,13 @@ namespace agg } } - //-------------------------------------------------------------------- template - void transform_all_paths(const Trans& trans) - { + void transform_all_paths(const Trans& trans) { unsigned idx; auto num_ver = m_vertices.total_vertices(); - for(idx = 0; idx < num_ver; idx++) - { + for(idx = 0; idx < num_ver; idx++) { double x, y; - if(is_vertex(m_vertices.vertex(idx, &x, &y))) - { + if(is_vertex(m_vertices.vertex(idx, &x, &y))) { trans.transform(&x, &y); m_vertices.modify_vertex(idx, x, y); } @@ -648,25 +614,20 @@ namespace agg } } - template - void path_base::curve3_rel(double dx_to, double dy_to) { + template void path_base::curve3_rel(double dx_to, double dy_to) { rel_to_abs(&dx_to, &dy_to); curve3(dx_to, dy_to); } template - void path_base::curve4(double x_ctrl1, double y_ctrl1, - double x_ctrl2, double y_ctrl2, - double x_to, double y_to) { + void path_base::curve4(double x_ctrl1, double y_ctrl1, double x_ctrl2, double y_ctrl2, double x_to, double y_to) { m_vertices.add_vertex(x_ctrl1, y_ctrl1, path_cmd_curve4); m_vertices.add_vertex(x_ctrl2, y_ctrl2, path_cmd_curve4); m_vertices.add_vertex(x_to, y_to, path_cmd_curve4); } template - void path_base::curve4_rel(double dx_ctrl1, double dy_ctrl1, - double dx_ctrl2, double dy_ctrl2, - double dx_to, double dy_to) { + void path_base::curve4_rel(double dx_ctrl1, double dy_ctrl1, double dx_ctrl2, double dy_ctrl2, double dx_to, double dy_to) { rel_to_abs(&dx_ctrl1, &dy_ctrl1); rel_to_abs(&dx_ctrl2, &dy_ctrl2); rel_to_abs(&dx_to, &dy_to); @@ -675,8 +636,7 @@ namespace agg m_vertices.add_vertex(dx_to, dy_to, path_cmd_curve4); } - template - void path_base::curve4(double x_ctrl2, double y_ctrl2, double x_to, double y_to) { + template void path_base::curve4(double x_ctrl2, double y_ctrl2, double x_to, double y_to) { double x0, y0; if (is_vertex(last_vertex(&x0, &y0))) { double x_ctrl1, y_ctrl1; @@ -693,96 +653,72 @@ namespace agg } } - template - void path_base::curve4_rel(double dx_ctrl2, double dy_ctrl2, double dx_to, double dy_to) { + template void path_base::curve4_rel(double dx_ctrl2, double dy_ctrl2, double dx_to, double dy_to) { rel_to_abs(&dx_ctrl2, &dy_ctrl2); rel_to_abs(&dx_to, &dy_to); curve4(dx_ctrl2, dy_ctrl2, dx_to, dy_to); } - template - inline void path_base::end_poly(unsigned flags) { + template inline void path_base::end_poly(unsigned flags) { if (is_vertex(m_vertices.last_command())) { m_vertices.add_vertex(0.0, 0.0, path_cmd_end_poly | flags); } } - template - inline void path_base::close_polygon(unsigned flags) { + template inline void path_base::close_polygon(unsigned flags) { end_poly(path_flags_close | flags); } - template - inline unsigned path_base::total_vertices() const { + template inline unsigned path_base::total_vertices() const { return m_vertices.total_vertices(); } - //------------------------------------------------------------------------ - template - inline unsigned path_base::last_vertex(double* x, double* y) const + template inline unsigned path_base::last_vertex(double* x, double* y) const { return m_vertices.last_vertex(x, y); } - //------------------------------------------------------------------------ - template - inline unsigned path_base::prev_vertex(double* x, double* y) const + template inline unsigned path_base::prev_vertex(double* x, double* y) const { return m_vertices.prev_vertex(x, y); } - //------------------------------------------------------------------------ - template - inline double path_base::last_x() const + template inline double path_base::last_x() const { return m_vertices.last_x(); } - //------------------------------------------------------------------------ - template - inline double path_base::last_y() const + template inline double path_base::last_y() const { return m_vertices.last_y(); } - //------------------------------------------------------------------------ - template - inline unsigned path_base::vertex(unsigned idx, double* x, double* y) const + template inline unsigned path_base::vertex(unsigned idx, double* x, double* y) const { return m_vertices.vertex(idx, x, y); } - //------------------------------------------------------------------------ - template - inline unsigned path_base::command(unsigned idx) const + template inline unsigned path_base::command(unsigned idx) const { return m_vertices.command(idx); } - //------------------------------------------------------------------------ - template - void path_base::modify_vertex(unsigned idx, double x, double y) + template void path_base::modify_vertex(unsigned idx, double x, double y) { m_vertices.modify_vertex(idx, x, y); } - //------------------------------------------------------------------------ - template - void path_base::modify_vertex(unsigned idx, double x, double y, unsigned cmd) + template void path_base::modify_vertex(unsigned idx, double x, double y, unsigned cmd) { m_vertices.modify_vertex(idx, x, y, cmd); } - //------------------------------------------------------------------------ - template - void path_base::modify_command(unsigned idx, unsigned cmd) + template void path_base::modify_command(unsigned idx, unsigned cmd) { m_vertices.modify_command(idx, cmd); } - //------------------------------------------------------------------------ - template - inline void path_base::rewind(unsigned path_id) + template inline void path_base::rewind(unsigned path_id) { m_iterator = path_id; m_last_x = 0.0; @@ -791,7 +727,6 @@ namespace agg m_curve4.reset(); } - //------------------------------------------------------------------------ template inline unsigned path_base::vertex(double* x, double* y) { @@ -836,12 +771,10 @@ namespace agg return cmd; } - //------------------------------------------------------------------------ template unsigned path_base::perceive_polygon_orientation(unsigned start, unsigned end) { // Calculate signed area (double area to be exact) - //--------------------- unsigned np = end - start; double area = 0.0; unsigned i; @@ -855,7 +788,6 @@ namespace agg return (area < 0.0) ? path_flags_cw : path_flags_ccw; } - //------------------------------------------------------------------------ template void path_base::invert_polygon(unsigned start, unsigned end) { @@ -880,27 +812,22 @@ namespace agg } } - //------------------------------------------------------------------------ template - void path_base::invert_polygon(unsigned start) - { + void path_base::invert_polygon(unsigned start) { // Skip all non-vertices at the beginning while(start < m_vertices.total_vertices() and !is_vertex(m_vertices.command(start))) ++start; // Skip all insignificant move_to - while(start+1 < m_vertices.total_vertices() and - is_move_to(m_vertices.command(start)) and + while (start+1 < m_vertices.total_vertices() and is_move_to(m_vertices.command(start)) and is_move_to(m_vertices.command(start+1))) ++start; // Find the last vertex unsigned end = start + 1; - while(end < m_vertices.total_vertices() and - !is_next_poly(m_vertices.command(end))) ++end; + while(end < m_vertices.total_vertices() and !is_next_poly(m_vertices.command(end))) ++end; invert_polygon(start, end); } - //------------------------------------------------------------------------ template unsigned path_base::arrange_polygon_orientation(unsigned start, int orientation) { @@ -933,7 +860,6 @@ namespace agg return end; } - //------------------------------------------------------------------------ template unsigned path_base::arrange_orientations(unsigned start, int orientation) { @@ -949,7 +875,6 @@ namespace agg return start; } - //------------------------------------------------------------------------ template void path_base::arrange_orientations_all_paths(int orientation) { if (orientation != path_flags_none) { @@ -978,15 +903,14 @@ namespace agg double x, y; for(i = 0; i < m_vertices.total_vertices(); i++) { unsigned cmd = m_vertices.vertex(i, &x, &y); - if(is_vertex(cmd)) { + if (is_vertex(cmd)) { m_vertices.modify_vertex(i, x, y2 - y + y1); } } } template - void path_base::translate(double dx, double dy, unsigned path_id) - { + void path_base::translate(double dx, double dy, unsigned path_id) { unsigned num_ver = m_vertices.total_vertices(); for(; path_id < num_ver; path_id++) { @@ -1004,25 +928,20 @@ namespace agg //------------------------------------------------------------------------ template - void path_base::translate_all_paths(double dx, double dy) - { + void path_base::translate_all_paths(double dx, double dy) { unsigned idx; unsigned num_ver = m_vertices.total_vertices(); - for(idx = 0; idx < num_ver; idx++) - { + for (idx = 0; idx < num_ver; idx++) { double x, y; - if(is_vertex(m_vertices.vertex(idx, &x, &y))) - { - x += dx; - y += dy; - m_vertices.modify_vertex(idx, x, y); + if (is_vertex(m_vertices.vertex(idx, &x, &y))) { + x += dx; + y += dy; + m_vertices.modify_vertex(idx, x, y); } } } - //-----------------------------------------------------vertex_stl_storage - template class vertex_stl_storage - { + template class vertex_stl_storage { public: typedef typename Container::value_type vertex_type; typedef typename vertex_type::value_type value_type; @@ -1031,9 +950,7 @@ namespace agg void free_all() { m_vertices.clear(); } void add_vertex(double x, double y, unsigned cmd) { - m_vertices.push_back(vertex_type(value_type(x), - value_type(y), - int8u(cmd))); + m_vertices.push_back(vertex_type(value_type(x), value_type(y), int8u(cmd))); } void modify_vertex(unsigned idx, double x, double y) { @@ -1074,7 +991,7 @@ namespace agg } unsigned prev_vertex(double* x, double* y) const { - if(m_vertices.size() < 2) { + if (m_vertices.size() < 2) { *x = *y = 0.0; return path_cmd_stop; } @@ -1115,5 +1032,4 @@ namespace agg typedef path_base > > path_storage; } - #endif diff --git a/src/vector/paths.cpp b/src/vector/paths.cpp index 6aa4f77d2..df5f2c8b8 100644 --- a/src/vector/paths.cpp +++ b/src/vector/paths.cpp @@ -63,6 +63,8 @@ void gen_vector_path(extVector *Vector) auto parent_view = get_parent_view(Vector); + Vector->PathTimestamp++; + if (Vector->Class->ClassID IS ID_VECTORGROUP) { Vector->Transform.reset(); apply_parent_transforms(Vector, Vector->Transform); diff --git a/src/vector/scene/scene_draw.cpp b/src/vector/scene/scene_draw.cpp index a7a27e7b2..12cca5c27 100644 --- a/src/vector/scene/scene_draw.cpp +++ b/src/vector/scene/scene_draw.cpp @@ -73,9 +73,9 @@ class SceneRenderer // This class holds the current state as the vector scene is parsed for drawing. It is most useful for managing // inheritable values that arise as part of the drawing process (transformation management being an obvious example). // -// NOTE: This feature is not intended to manage inheritable features that cross-over with SVG. For instance, it -// does not manage inheritance of painting features like fills. Wherever it is possible to do so, inheritance should -// be managed by the client, with the goal of building a scene graph that has static properties. +// NOTE: This feature is not intended to manage inheritable features that cross-over with SVG. For instance, fill +// values are not inheritable. Wherever it is possible to do so, inheritance should be managed by the client, with +// the goal of building a scene graph that has static properties. class VectorState { public: diff --git a/src/vector/utility.cpp b/src/vector/utility.cpp index 2d5e20268..1fa1733f4 100644 --- a/src/vector/utility.cpp +++ b/src/vector/utility.cpp @@ -2,6 +2,9 @@ agg::gamma_lut glGamma(2.2); DOUBLE glDisplayHDPI = 96, glDisplayVDPI = 96, glDisplayDPI = 96; +static HSV rgb_to_hsl(FRGB Colour) __attribute__((unused)); +static FRGB hsl_to_rgb(HSV Colour) __attribute__((unused)); + //******************************************************************************************************************** static CSTRING get_effect_name(UBYTE Effect) __attribute__ ((unused)); @@ -53,6 +56,55 @@ const FieldDef clAspectRatio[] = { //******************************************************************************************************************** +static HSV rgb_to_hsl(FRGB Colour) +{ + DOUBLE vmax = std::ranges::max({ Colour.Red, Colour.Green, Colour.Blue }); + DOUBLE vmin = std::ranges::min({ Colour.Red, Colour.Green, Colour.Blue }); + DOUBLE light = (vmax + vmin) * 0.5; + + if (vmax IS vmin) return HSV { 0, 0, light }; + + DOUBLE sat = light, hue = light; + DOUBLE d = vmax - vmin; + sat = light > 0.5 ? d / (2 - vmax - vmin) : d / (vmax + vmin); + if (vmax IS Colour.Red) hue = (Colour.Green - Colour.Blue) / d + (Colour.Green < Colour.Blue ? 6.0 : 0.0); + if (vmax IS Colour.Green) hue = (Colour.Blue - Colour.Red) / d + 2.0; + if (vmax IS Colour.Blue) hue = (Colour.Red - Colour.Green) / d + 4.0; + hue /= 6.0; + + return HSV { hue, sat, light, Colour.Alpha }; +} + +//******************************************************************************************************************** + +static FRGB hsl_to_rgb(HSV Colour) +{ + auto hueToRgb = [](FLOAT p, FLOAT q, FLOAT t) -> FLOAT { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1.0/6.0) return p + (q - p) * 6.0 * t; + if (t < 1.0/2.0) return q; + if (t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; + return p; + }; + + if (Colour.Saturation == 0) { + return { FLOAT(Colour.Value), FLOAT(Colour.Value), FLOAT(Colour.Value), FLOAT(Colour.Alpha) }; + } + else { + const DOUBLE q = (Colour.Value < 0.5) ? Colour.Value * (1.0 + Colour.Saturation) : Colour.Value + Colour.Saturation - Colour.Value * Colour.Saturation; + const DOUBLE p = 2.0 * Colour.Value - q; + return { + hueToRgb(p, q, Colour.Hue + 1.0/3.0), + hueToRgb(p, q, Colour.Hue), + hueToRgb(p, q, Colour.Hue - 1.0/3.0), + FLOAT(Colour.Alpha) + }; + } +} + +//******************************************************************************************************************** + CSTRING get_name(OBJECTPTR Vector) { if (!Vector) return "NULL"; diff --git a/src/vector/vector.fdl b/src/vector/vector.fdl index 7b9819c5b..5915f7feb 100644 --- a/src/vector/vector.fdl +++ b/src/vector/vector.fdl @@ -548,6 +548,7 @@ module({ name="Vector", copyright="Paul Manias © 2010-2024", version=1.0 }, fun int(PTC) Cursor # The mouse cursor to display when the pointer is within the vector's boundary. int(RQ) PathQuality # Defines the quality of rendered path outlines. int(VCS) ColourSpace # Desired colour space to use when blending colours. + int PathTimestamp # This counter is modified each time the path is regenerated. ]]) // VectorPath method ID's start high enough to give a cushion from inherited Vector methods. @@ -651,9 +652,12 @@ inline void SET_VECTOR_COLOUR(objVectorColour *Colour, DOUBLE Red, DOUBLE Green, "A", "ACHROMATOMALY", "ACHROMATOPSIA", + "ADDITIVE", "ALIGN", "ALT-FILL", "AMPLITUDE", + "ANIMATE", + "ANIMATECOLOR", "ANIMATEMOTION", "ANIMATETRANSFORM", "ARITHMETIC", @@ -1140,7 +1144,13 @@ inline void SET_VECTOR_COLOUR(objVectorColour *Colour, DOUBLE Red, DOUBLE Green, "EXPANDED", "SEMI-EXPANDED", "EXTRA-EXPANDED", - "ULTRA-EXPANDED" + "ULTRA-EXPANDED", + "CALCMODE", + "KEYPOINTS", + "ORIGIN", + "KEYTIMES", + "KEYSPLINES", + "BY" ) c_insert([[ diff --git a/src/vector/vector.h b/src/vector/vector.h index b6cf52d6f..491a4b8f2 100644 --- a/src/vector/vector.h +++ b/src/vector/vector.h @@ -55,7 +55,8 @@ using namespace pf; #include static const DOUBLE DISPLAY_DPI = 96.0; // Freetype measurements are based on this DPI. -static const DOUBLE DEG2RAD = 0.0174532925; // Multiple any angle by this value to convert to radians +static const DOUBLE DEG2RAD = 0.01745329251994329576923690768489; // Multiple any angle by this value to convert to radians +static const DOUBLE RAD2DEG = 57.295779513082320876798154814105; static const DOUBLE SQRT2 = 1.41421356237; // sqrt(2) static const DOUBLE INV_SQRT2 = 1.0 / SQRT2; @@ -377,7 +378,7 @@ class extVector : public objVector { TClipRectangle Bounds; // Must be calculated by GeneratePath() and called from calc_full_boundary() DOUBLE StrokeWidth; agg::path_storage BasePath; - agg::trans_affine Transform; + agg::trans_affine Transform; // Final transform. Accumulated from the Matrix list during path generation. CSTRING FilterString, StrokeString, FillString; STRING ID; void (*GeneratePath)(extVector *, agg::path_storage &); @@ -1101,37 +1102,37 @@ extern DOUBLE glDisplayVDPI, glDisplayHDPI, glDisplayDPI; extern void set_text_final_xy(extVectorText *); -extern "C" void vecArcTo(class SimpleVector *, DOUBLE RX, DOUBLE RY, DOUBLE Angle, DOUBLE X, DOUBLE Y, ARC Flags); -extern "C" ERR vecApplyPath(class SimpleVector *, extVectorPath *); +extern "C" void vecArcTo(class SimpleVector *, DOUBLE RX, DOUBLE RY, DOUBLE Angle, DOUBLE X, DOUBLE Y, ARC Flags); +extern "C" ERR vecApplyPath(class SimpleVector *, extVectorPath *); extern "C" DOUBLE vecCharWidth(APTR Handle, ULONG Char, ULONG KChar, DOUBLE *Kerning); -extern "C" void vecClosePath(class SimpleVector *); -extern "C" void vecCurve3(class SimpleVector *, DOUBLE CtrlX, DOUBLE CtrlY, DOUBLE X, DOUBLE Y); -extern "C" void vecCurve4(class SimpleVector *, DOUBLE CtrlX1, DOUBLE CtrlY1, DOUBLE CtrlX2, DOUBLE CtrlY2, DOUBLE X, DOUBLE Y); -extern "C" ERR vecDrawPath(objBitmap *, class SimpleVector *, DOUBLE StrokeWidth, OBJECTPTR StrokeStyle, OBJECTPTR FillStyle); -extern "C" ERR vecFlushMatrix(VectorMatrix *); -extern "C" void vecFreePath(APTR); -extern "C" ERR vecGenerateEllipse(DOUBLE, DOUBLE, DOUBLE, DOUBLE, LONG, APTR *); -extern "C" ERR vecGenerateRectangle(DOUBLE, DOUBLE, DOUBLE, DOUBLE, APTR *); -extern "C" ERR vecGeneratePath(CSTRING, APTR *); -extern "C" ERR vecGetFontHandle(CSTRING, CSTRING, LONG, LONG, APTR *); -extern "C" ERR vecGetFontMetrics(APTR, struct FontMetrics *); -extern "C" LONG vecGetVertex(class SimpleVector *, DOUBLE *, DOUBLE *); -extern "C" void vecLineTo(class SimpleVector *, DOUBLE, DOUBLE); -extern "C" void vecMoveTo(class SimpleVector *, DOUBLE, DOUBLE); -extern "C" ERR vecMultiply(struct VectorMatrix *, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE); -extern "C" ERR vecMultiplyMatrix(struct VectorMatrix *, struct VectorMatrix *); -extern "C" ERR vecParseTransform(struct VectorMatrix *, CSTRING Commands); -extern "C" ERR vecReadPainter(objVectorScene *, CSTRING, VectorPainter *, CSTRING *); -extern "C" ERR vecResetMatrix(struct VectorMatrix *); -extern "C" void vecRewindPath(class SimpleVector *); -extern "C" ERR vecRotate(struct VectorMatrix *, DOUBLE, DOUBLE, DOUBLE); -extern "C" ERR vecScale(struct VectorMatrix *, DOUBLE, DOUBLE); -extern "C" ERR vecSkew(struct VectorMatrix *, DOUBLE, DOUBLE); -extern "C" void vecSmooth3(class SimpleVector *, DOUBLE, DOUBLE); -extern "C" void vecSmooth4(class SimpleVector *, DOUBLE, DOUBLE, DOUBLE, DOUBLE); +extern "C" void vecClosePath(class SimpleVector *); +extern "C" void vecCurve3(class SimpleVector *, DOUBLE CtrlX, DOUBLE CtrlY, DOUBLE X, DOUBLE Y); +extern "C" void vecCurve4(class SimpleVector *, DOUBLE CtrlX1, DOUBLE CtrlY1, DOUBLE CtrlX2, DOUBLE CtrlY2, DOUBLE X, DOUBLE Y); +extern "C" ERR vecDrawPath(objBitmap *, class SimpleVector *, DOUBLE StrokeWidth, OBJECTPTR StrokeStyle, OBJECTPTR FillStyle); +extern "C" ERR vecFlushMatrix(VectorMatrix *); +extern "C" void vecFreePath(APTR); +extern "C" ERR vecGenerateEllipse(DOUBLE, DOUBLE, DOUBLE, DOUBLE, LONG, APTR *); +extern "C" ERR vecGenerateRectangle(DOUBLE, DOUBLE, DOUBLE, DOUBLE, APTR *); +extern "C" ERR vecGeneratePath(CSTRING, APTR *); +extern "C" ERR vecGetFontHandle(CSTRING, CSTRING, LONG, LONG, APTR *); +extern "C" ERR vecGetFontMetrics(APTR, struct FontMetrics *); +extern "C" LONG vecGetVertex(class SimpleVector *, DOUBLE *, DOUBLE *); +extern "C" void vecLineTo(class SimpleVector *, DOUBLE, DOUBLE); +extern "C" void vecMoveTo(class SimpleVector *, DOUBLE, DOUBLE); +extern "C" ERR vecMultiply(struct VectorMatrix *, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE); +extern "C" ERR vecMultiplyMatrix(struct VectorMatrix *, struct VectorMatrix *); +extern "C" ERR vecParseTransform(struct VectorMatrix *, CSTRING Commands); +extern "C" ERR vecReadPainter(objVectorScene *, CSTRING, VectorPainter *, CSTRING *); +extern "C" ERR vecResetMatrix(struct VectorMatrix *); +extern "C" void vecRewindPath(class SimpleVector *); +extern "C" ERR vecRotate(struct VectorMatrix *, DOUBLE, DOUBLE, DOUBLE); +extern "C" ERR vecScale(struct VectorMatrix *, DOUBLE, DOUBLE); +extern "C" ERR vecSkew(struct VectorMatrix *, DOUBLE, DOUBLE); +extern "C" void vecSmooth3(class SimpleVector *, DOUBLE, DOUBLE); +extern "C" void vecSmooth4(class SimpleVector *, DOUBLE, DOUBLE, DOUBLE, DOUBLE); extern "C" DOUBLE vecStringWidth(APTR, CSTRING, LONG); -extern "C" ERR vecTranslate(struct VectorMatrix *, DOUBLE, DOUBLE); -extern "C" void vecTranslatePath(class SimpleVector *, DOUBLE, DOUBLE); +extern "C" ERR vecTranslate(struct VectorMatrix *, DOUBLE, DOUBLE); +extern "C" void vecTranslatePath(class SimpleVector *, DOUBLE, DOUBLE); template TClipRectangle::TClipRectangle(const extVector *pVector) { *this = pVector->Bounds; diff --git a/src/vector/vector_functions.cpp b/src/vector/vector_functions.cpp index d12334c67..e3e0189e7 100644 --- a/src/vector/vector_functions.cpp +++ b/src/vector/vector_functions.cpp @@ -896,7 +896,7 @@ ERR vecReadPainter(objVectorScene *Scene, CSTRING IRI, VectorPainter *Painter, C if (rgb.Alpha > 1) rgb.Alpha = 1; else if (rgb.Alpha < 0) rgb.Alpha = 0; } - else if (rgb.Alpha <= 0) rgb.Alpha = 1.0; // Only set the alpha if it hasn't been set already (example: stroke-opacity) + else rgb.Alpha = 1.0; if (rgb.Red > 1) rgb.Red = 1; else if (rgb.Red < 0) rgb.Red = 0; @@ -913,6 +913,139 @@ ERR vecReadPainter(objVectorScene *Scene, CSTRING IRI, VectorPainter *Painter, C } return ERR::Okay; } + else if (StrCompare("hsl(", IRI, 4) IS ERR::Okay) { + auto &rgb = Painter->Colour; + IRI += 4; + DOUBLE hue = StrToFloat(IRI) * (1.0 / 255.0); + while ((*IRI) and (*IRI != ',')) { + if (*IRI IS '%') hue = hue * (255.0 / 100.0); + IRI++; + } + if (*IRI) IRI++; + DOUBLE sat = StrToFloat(IRI) * (1.0 / 255.0); + while ((*IRI) and (*IRI != ',')) { + if (*IRI IS '%') sat = sat * (255.0 / 100.0); + IRI++; + } + if (*IRI) IRI++; + DOUBLE light = StrToFloat(IRI) * (1.0 / 255.0); + while ((*IRI) and (*IRI != ',')) { + if (*IRI IS '%') light = light * (255.0 / 100.0); + IRI++; + } + if (*IRI) { + IRI++; + rgb.Alpha = StrToFloat(IRI) * (1.0 / 255.0); + while (*IRI) { + if (*IRI IS '%') rgb.Alpha = rgb.Alpha * (255.0 / 100.0); + IRI++; + } + if (rgb.Alpha > 1) rgb.Alpha = 1; + else if (rgb.Alpha < 0) rgb.Alpha = 0; + } + else rgb.Alpha = 1.0; + + if (hue > 1) hue = 1; + else if (hue < 0) hue = 0; + + if (sat > 1) sat = 1; + else if (sat < 0) sat = 0; + + if (light > 1) light = 1; + else if (light < 0) light = 0; + + // Convert HSL to RGB + + auto hueToRgb = [](DOUBLE p, DOUBLE q, DOUBLE t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1.0/6.0) return p + (q - p) * 6.0 * t; + if (t < 1.0/2.0) return q; + if (t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; + return p; + }; + + if (sat == 0) { + rgb.Red = rgb.Green = rgb.Blue = light; + } + else { + const DOUBLE q = (light < 0.5) ? light * (1.0 + sat) : light + sat - light * sat; + const DOUBLE p = 2.0 * light - q; + rgb.Red = hueToRgb(p, q, hue + 1.0/3.0); + rgb.Green = hueToRgb(p, q, hue); + rgb.Blue = hueToRgb(p, q, hue - 1.0/3.0); + } + + if (Result) { + while ((*IRI) and (*IRI != ';')) IRI++; + *Result = IRI[0] ? IRI : NULL; + } + return ERR::Okay; + } + else if (StrCompare("hsv(", IRI, 4) IS ERR::Okay) { + auto &rgb = Painter->Colour; + IRI += 4; + DOUBLE hue = StrToFloat(IRI) * (1.0 / 255.0); + while ((*IRI) and (*IRI != ',')) { + if (*IRI IS '%') hue = hue * (255.0 / 100.0); + IRI++; + } + if (*IRI) IRI++; + DOUBLE sat = StrToFloat(IRI) * (1.0 / 255.0); + while ((*IRI) and (*IRI != ',')) { + if (*IRI IS '%') sat = sat * (255.0 / 100.0); + IRI++; + } + if (*IRI) IRI++; + DOUBLE val = StrToFloat(IRI) * (1.0 / 255.0); + while ((*IRI) and (*IRI != ',')) { + if (*IRI IS '%') val = val * (255.0 / 100.0); + IRI++; + } + if (*IRI) { + IRI++; + rgb.Alpha = StrToFloat(IRI) * (1.0 / 255.0); + while (*IRI) { + if (*IRI IS '%') rgb.Alpha = rgb.Alpha * (255.0 / 100.0); + IRI++; + } + if (rgb.Alpha > 1) rgb.Alpha = 1; + else if (rgb.Alpha < 0) rgb.Alpha = 0; + } + else rgb.Alpha = 1.0; + + if (hue > 1) hue = 1; + else if (hue < 0) hue = 0; + + if (sat > 1) sat = 1; + else if (sat < 0) sat = 0; + + if (val > 1) val = 1; + else if (val < 0) val = 0; + + hue = hue / 60.0; + LONG i = floor(hue); + DOUBLE f = hue - i; + if (!(i & 1)) f = 1 - f; // if i is even + DOUBLE m = val * (1 - sat); + DOUBLE n = val * (1 - sat * f); + switch (i) { + case 6: + case 0: rgb.Red = val; rgb.Green = n; rgb.Blue = m; break; + case 1: rgb.Red = n; rgb.Green = val; rgb.Blue = m; break; + case 2: rgb.Red = m; rgb.Green = val; rgb.Blue = n; break; + case 3: rgb.Red = m; rgb.Green = n; rgb.Blue = val; break; + case 4: rgb.Red = n; rgb.Green = m; rgb.Blue = val; break; + case 5: rgb.Red = val; rgb.Green = m; rgb.Blue = n; break; + default: rgb.Red = 0; rgb.Green = 0; rgb.Blue = 0; break; + } + + if (Result) { + while ((*IRI) and (*IRI != ';')) IRI++; + *Result = IRI[0] ? IRI : NULL; + } + return ERR::Okay; + } else if (*IRI IS '#') { auto &rgb = Painter->Colour; IRI++; diff --git a/src/vector/vectors/text.cpp b/src/vector/vectors/text.cpp index fe1d88a48..6d03784cb 100644 --- a/src/vector/vectors/text.cpp +++ b/src/vector/vectors/text.cpp @@ -822,8 +822,14 @@ static ERR TEXT_SET_FontSize(extVectorText *Self, CSTRING Value) bool pct; auto size = read_unit(Value, pct); + // TODO: With respect to supporting sub-pixel point sizes and being cache-friendly, we could try caching fonts + // at pre-determined point sizes (4,6,8,10,12,14,20,30,40,50,60,...) and then use scaling to cater to other + // 'non-standard' sizes. This would allow for hinting to remain effective when regular point sizes are being chosen. + if (size > 0) { - Self->txFontSize = std::trunc(size); + auto new_size = std::trunc(size); + if (Self->txFontSize IS new_size) return ERR::Okay; + Self->txFontSize = new_size; Self->txScaledFontSize = pct; if (Self->initialised()) return reset_font(Self); else return ERR::Okay; @@ -1402,6 +1408,7 @@ static ERR reset_font(extVectorText *Vector, bool Force) Vector->txBitmapFont = ((bmp_font *)Vector->txHandle)->font; Vector->txFontSize = std::trunc(DOUBLE(Vector->txBitmapFont->Height) * (DISPLAY_DPI / 72.0)); } + mark_dirty(Vector, RC::ALL); return ERR::Okay; } else return log.warning(error); diff --git a/src/vector/vectors/vector.cpp b/src/vector/vectors/vector.cpp index 89fa17bfe..6cfe0788e 100644 --- a/src/vector/vectors/vector.cpp +++ b/src/vector/vectors/vector.cpp @@ -991,10 +991,13 @@ If the Callback returns `ERR::Terminate`, then no further coordinates will be pr -INPUT- ptr(func) Callback: The function to call with each coordinate of the path. +double Scale: Set to 1.0 (recommended) to trace the path at a scale of 1 to 1. +int Transform: Set to TRUE if all transforms applicable to the vector should be applied to the path. -ERRORS- Okay: NullArgs: +NoData: The vector does not define a path. *********************************************************************************************************************/ @@ -1002,27 +1005,45 @@ static ERR VECTOR_TracePath(extVector *Self, struct vecTracePath *Args) { pf::Log log; - if ((!Args) or (Args->Callback)) return log.warning(ERR::NullArgs); + if ((!Args) or (!Args->Callback)) return log.warning(ERR::NullArgs); if (Self->dirty()) gen_vector_tree(Self); - if (!Self->BasePath.total_vertices()) return ERR::NoData; + if (Self->BasePath.empty()) return ERR::NoData; Self->BasePath.rewind(0); + Self->BasePath.approximation_scale(Args->Scale); DOUBLE x, y; LONG cmd = -1; + LONG index = 0; if (Args->Callback->isC()) { - auto routine = ((void (*)(extVector *, LONG, LONG, DOUBLE, DOUBLE, APTR))(Args->Callback->Routine)); + auto routine = ((ERR (*)(extVector *, LONG, LONG, DOUBLE, DOUBLE, APTR))(Args->Callback->Routine)); pf::SwitchContext context(GetParentContext()); - LONG index = 0; - do { - cmd = Self->BasePath.vertex(&x, &y); - if (agg::is_vertex(cmd)) routine(Self, index++, cmd, x, y, Args->Callback->Meta); - } while (cmd != agg::path_cmd_stop); + if (Args->Transform) { + agg::conv_transform t_path(Self->BasePath, Self->Transform); + do { + cmd = t_path.vertex(&x, &y); + if (agg::is_vertex(cmd)) { + if (routine(Self, index++, cmd, x, y, Args->Callback->Meta) IS ERR::Terminate) { + return ERR::Okay; + } + } + } while (cmd != agg::path_cmd_stop); + } + else { + do { + cmd = Self->BasePath.vertex(&x, &y); + if (agg::is_vertex(cmd)) { + if (routine(Self, index++, cmd, x, y, Args->Callback->Meta) IS ERR::Terminate) { + return ERR::Okay; + } + } + } while (cmd != agg::path_cmd_stop); + } } else if (Args->Callback->isScript()) { std::array args {{ @@ -1034,17 +1055,35 @@ static ERR VECTOR_TracePath(extVector *Self, struct vecTracePath *Args) }}; args[0].Long = Self->UID; - LONG index = 0; - do { - cmd = Self->BasePath.vertex(&x, &y); - if (agg::is_vertex(cmd)) { - args[1].Long = index++; - args[2].Long = cmd; - args[3].Double = x; - args[4].Double = y; - scCall(*Args->Callback, args); - } - } while (cmd != agg::path_cmd_stop); + if (Args->Transform) { + agg::conv_transform t_path(Self->BasePath, Self->Transform); + ERR result; + do { + cmd = t_path.vertex(&x, &y); + if (agg::is_vertex(cmd)) { + args[1].Long = index++; + args[2].Long = cmd; + args[3].Double = x; + args[4].Double = y; + if (scCall(*Args->Callback, args, result) != ERR::Okay) return ERR::Failed; + if (result IS ERR::Terminate) return ERR::Okay; + } + } while (cmd != agg::path_cmd_stop); + } + else { + ERR result; + do { + cmd = Self->BasePath.vertex(&x, &y); + if (agg::is_vertex(cmd)) { + args[1].Long = index++; + args[2].Long = cmd; + args[3].Double = x; + args[4].Double = y; + if (scCall(*Args->Callback, args, result) != ERR::Okay) return ERR::Failed; + if (result IS ERR::Terminate) return ERR::Okay; + } + } while (cmd != agg::path_cmd_stop); + } } return ERR::Okay; @@ -1054,8 +1093,8 @@ static ERR VECTOR_TracePath(extVector *Self, struct vecTracePath *Args) -FIELD- AppendPath: Experimental. Append the path of the referenced vector during path generation. -The path of an external Vector can be appended to the base path in real-time by making a reference to that vector -here. The operation is completed immediately after the generation of the client vector's base path, prior to any +The path of an external Vector can be appended to the base path in real-time by making a reference to that vector +here. The operation is completed immediately after the generation of the client vector's base path, prior to any transforms. It is strongly recommended that the appended vector has its #Visibility set to `HIDDEN`. Any direct transform that @@ -1272,6 +1311,24 @@ static ERR VECTOR_SET_DashOffset(extVector *Self, DOUBLE Value) /********************************************************************************************************************* +-FIELD- +DisplayScale: Returns the scale of the vector as it appears on the display. + +The DisplayScale field will return the scale factor of the vector's path as it appears in the final rendering. For +instance if the vector is the child of a viewport scaled down to 50%, the resulting value would be `0.5`. + +*********************************************************************************************************************/ + +static ERR VECTOR_GET_DisplayScale(extVector *Self, DOUBLE *Value) +{ + if (!Self->initialised()) return ERR::NotInitialised; + if (Self->dirty()) gen_vector_tree(Self); + *Value = Self->Transform.scale(); + return ERR::Okay; +} + +/********************************************************************************************************************* + -FIELD- EnableBkgd: If true, allows filters to use BackgroundImage and BackgroundAlpha source types. @@ -1874,6 +1931,13 @@ Adjusting the render quality allows for fine adjustment of the paths produced by default option of `AUTO` is recommended, it is optimal to lower the rendering quality to `CRISP` if the path is composed of lines at 45 degree increments and `FAST` if points are aligned to whole numbers when rendered to a bitmap. +-FIELD- +PathTimestamp: This counter is modified each time the path is regenerated. + +The PathTimestamp can be used as a basic means of recording the state of the vector's path, and checking that state +for changes at a later time. For more active monitoring and response, clients should subscribe to the `PATH_CHANGED` +event. + -FIELD- Prev: The previous vector in the branch, or NULL. @@ -2411,9 +2475,11 @@ static const FieldArray clVectorFields[] = { { "Cursor", FDF_LONG|FDF_LOOKUP|FDF_RW, NULL, VECTOR_SET_Cursor, &clVectorCursor }, { "PathQuality", FDF_LONG|FDF_LOOKUP|FDF_RW, NULL, NULL, &clVectorPathQuality }, { "ColourSpace", FDF_LONG|FDF_LOOKUP|FDF_RW, NULL, NULL, &clVectorColourSpace }, + { "PathTimestamp", FDF_LONG|FDF_R }, // Virtual fields { "ClipRule", FDF_VIRTUAL|FDF_LONG|FDF_LOOKUP|FDF_RW, VECTOR_GET_ClipRule, VECTOR_SET_ClipRule, &clFillRule }, { "DashArray", FDF_VIRTUAL|FDF_ARRAY|FDF_DOUBLE|FD_RW, VECTOR_GET_DashArray, VECTOR_SET_DashArray }, + { "DisplayScale", FDF_VIRTUAL|FDF_DOUBLE|FDF_R, VECTOR_GET_DisplayScale }, { "Mask", FDF_VIRTUAL|FDF_OBJECT|FDF_RW, VECTOR_GET_Mask, VECTOR_SET_Mask }, { "Morph", FDF_VIRTUAL|FDF_OBJECT|FDF_RW, VECTOR_GET_Morph, VECTOR_SET_Morph }, { "AppendPath", FDF_VIRTUAL|FDF_OBJECT|FDF_RW, VECTOR_GET_AppendPath, VECTOR_SET_AppendPath }, diff --git a/src/vector/vectors/vector_def.c b/src/vector/vectors/vector_def.c index 86dbda983..cdbc9a760 100644 --- a/src/vector/vectors/vector_def.c +++ b/src/vector/vectors/vector_def.c @@ -61,7 +61,7 @@ static const struct FieldDef clVectorColourSpace[] = { }; FDEF maPush[] = { { "Position", FD_LONG }, { 0, 0 } }; -FDEF maTracePath[] = { { "Callback", FD_FUNCTIONPTR }, { 0, 0 } }; +FDEF maTracePath[] = { { "Callback", FD_FUNCTIONPTR }, { "Scale", FD_DOUBLE }, { "Transform", FD_LONG }, { 0, 0 } }; FDEF maGetBoundary[] = { { "Flags", FD_LONG }, { "X", FD_DOUBLE|FD_RESULT }, { "Y", FD_DOUBLE|FD_RESULT }, { "Width", FD_DOUBLE|FD_RESULT }, { "Height", FD_DOUBLE|FD_RESULT }, { 0, 0 } }; FDEF maPointInPath[] = { { "X", FD_DOUBLE }, { "Y", FD_DOUBLE }, { 0, 0 } }; FDEF maSubscribeInput[] = { { "Mask", FD_LONG }, { "Callback", FD_FUNCTIONPTR }, { 0, 0 } };