diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..73b7fb7
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,1101 @@
+[*]
+charset = utf-8
+end_of_line = crlf
+indent_size = 4
+indent_style = tab
+insert_final_newline = false
+max_line_length = 999
+tab_width = 4
+ij_continuation_indent_size = 8
+ij_formatter_off_tag = @formatter:off
+ij_formatter_on_tag = @formatter:on
+ij_formatter_tags_enabled = true
+ij_smart_tabs = true
+ij_visual_guides =
+ij_wrap_on_typing = false
+
+[*.css]
+indent_style = space
+ij_smart_tabs = false
+ij_css_align_closing_brace_with_properties = false
+ij_css_blank_lines_around_nested_selector = 1
+ij_css_blank_lines_between_blocks = 1
+ij_css_block_comment_add_space = false
+ij_css_brace_placement = end_of_line
+ij_css_enforce_quotes_on_format = false
+ij_css_hex_color_long_format = false
+ij_css_hex_color_lower_case = false
+ij_css_hex_color_short_format = false
+ij_css_hex_color_upper_case = false
+ij_css_keep_blank_lines_in_code = 2
+ij_css_keep_indents_on_empty_lines = false
+ij_css_keep_single_line_blocks = false
+ij_css_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
+ij_css_space_after_colon = true
+ij_css_space_before_opening_brace = true
+ij_css_use_double_quotes = true
+ij_css_value_alignment = do_not_align
+
+[*.java]
+ij_continuation_indent_size = 4
+ij_smart_tabs = false
+ij_java_align_consecutive_assignments = false
+ij_java_align_consecutive_variable_declarations = false
+ij_java_align_group_field_declarations = false
+ij_java_align_multiline_annotation_parameters = false
+ij_java_align_multiline_array_initializer_expression = false
+ij_java_align_multiline_assignment = false
+ij_java_align_multiline_binary_operation = false
+ij_java_align_multiline_chained_methods = false
+ij_java_align_multiline_deconstruction_list_components = true
+ij_java_align_multiline_extends_list = false
+ij_java_align_multiline_for = true
+ij_java_align_multiline_method_parentheses = false
+ij_java_align_multiline_parameters = true
+ij_java_align_multiline_parameters_in_calls = false
+ij_java_align_multiline_parenthesized_expression = false
+ij_java_align_multiline_records = true
+ij_java_align_multiline_resources = true
+ij_java_align_multiline_ternary_operation = false
+ij_java_align_multiline_text_blocks = false
+ij_java_align_multiline_throws_list = false
+ij_java_align_subsequent_simple_methods = false
+ij_java_align_throws_keyword = false
+ij_java_align_types_in_multi_catch = true
+ij_java_annotation_parameter_wrap = off
+ij_java_array_initializer_new_line_after_left_brace = true
+ij_java_array_initializer_right_brace_on_new_line = true
+ij_java_array_initializer_wrap = on_every_item
+ij_java_assert_statement_colon_on_next_line = false
+ij_java_assert_statement_wrap = off
+ij_java_assignment_wrap = off
+ij_java_binary_operation_sign_on_next_line = false
+ij_java_binary_operation_wrap = off
+ij_java_blank_lines_after_anonymous_class_header = 0
+ij_java_blank_lines_after_class_header = 0
+ij_java_blank_lines_after_imports = 1
+ij_java_blank_lines_after_package = 1
+ij_java_blank_lines_around_class = 1
+ij_java_blank_lines_around_field = 0
+ij_java_blank_lines_around_field_in_interface = 0
+ij_java_blank_lines_around_initializer = 1
+ij_java_blank_lines_around_method = 1
+ij_java_blank_lines_around_method_in_interface = 1
+ij_java_blank_lines_before_class_end = 0
+ij_java_blank_lines_before_imports = 1
+ij_java_blank_lines_before_method_body = 0
+ij_java_blank_lines_before_package = 0
+ij_java_block_brace_style = end_of_line
+ij_java_block_comment_add_space = false
+ij_java_block_comment_at_first_column = true
+ij_java_builder_methods =
+ij_java_call_parameters_new_line_after_left_paren = false
+ij_java_call_parameters_right_paren_on_new_line = false
+ij_java_call_parameters_wrap = off
+ij_java_case_statement_on_separate_line = true
+ij_java_catch_on_new_line = false
+ij_java_class_annotation_wrap = split_into_lines
+ij_java_class_brace_style = end_of_line
+ij_java_class_count_to_use_import_on_demand = 99
+ij_java_class_names_in_javadoc = 1
+ij_java_deconstruction_list_wrap = normal
+ij_java_do_not_indent_top_level_class_members = false
+ij_java_do_not_wrap_after_single_annotation = false
+ij_java_do_not_wrap_after_single_annotation_in_parameter = false
+ij_java_do_while_brace_force = always
+ij_java_doc_add_blank_line_after_description = true
+ij_java_doc_add_blank_line_after_param_comments = false
+ij_java_doc_add_blank_line_after_return = false
+ij_java_doc_add_p_tag_on_empty_lines = true
+ij_java_doc_align_exception_comments = true
+ij_java_doc_align_param_comments = true
+ij_java_doc_do_not_wrap_if_one_line = false
+ij_java_doc_enable_formatting = true
+ij_java_doc_enable_leading_asterisks = true
+ij_java_doc_indent_on_continuation = false
+ij_java_doc_keep_empty_lines = true
+ij_java_doc_keep_empty_parameter_tag = true
+ij_java_doc_keep_empty_return_tag = true
+ij_java_doc_keep_empty_throws_tag = true
+ij_java_doc_keep_invalid_tags = true
+ij_java_doc_param_description_on_new_line = false
+ij_java_doc_preserve_line_breaks = false
+ij_java_doc_use_throws_not_exception_tag = true
+ij_java_else_on_new_line = false
+ij_java_enum_constants_wrap = off
+ij_java_extends_keyword_wrap = off
+ij_java_extends_list_wrap = off
+ij_java_field_annotation_wrap = split_into_lines
+ij_java_field_name_prefix =
+ij_java_field_name_suffix =
+ij_java_finally_on_new_line = false
+ij_java_for_brace_force = always
+ij_java_for_statement_new_line_after_left_paren = false
+ij_java_for_statement_right_paren_on_new_line = false
+ij_java_for_statement_wrap = off
+ij_java_generate_final_locals = false
+ij_java_generate_final_parameters = false
+ij_java_if_brace_force = always
+ij_java_imports_layout = *, |, javax.**, java.**, |, $*
+ij_java_indent_case_from_switch = true
+ij_java_insert_inner_class_imports = false
+ij_java_insert_override_annotation = true
+ij_java_keep_blank_lines_before_right_brace = 2
+ij_java_keep_blank_lines_between_package_declaration_and_header = 2
+ij_java_keep_blank_lines_in_code = 2
+ij_java_keep_blank_lines_in_declarations = 2
+ij_java_keep_builder_methods_indents = false
+ij_java_keep_control_statement_in_one_line = true
+ij_java_keep_first_column_comment = true
+ij_java_keep_indents_on_empty_lines = false
+ij_java_keep_line_breaks = true
+ij_java_keep_multiple_expressions_in_one_line = false
+ij_java_keep_simple_blocks_in_one_line = false
+ij_java_keep_simple_classes_in_one_line = false
+ij_java_keep_simple_lambdas_in_one_line = false
+ij_java_keep_simple_methods_in_one_line = false
+ij_java_label_indent_absolute = false
+ij_java_label_indent_size = 0
+ij_java_lambda_brace_style = end_of_line
+ij_java_layout_static_imports_separately = true
+ij_java_line_comment_add_space = false
+ij_java_line_comment_add_space_on_reformat = false
+ij_java_line_comment_at_first_column = true
+ij_java_local_variable_name_prefix =
+ij_java_local_variable_name_suffix =
+ij_java_method_annotation_wrap = split_into_lines
+ij_java_method_brace_style = end_of_line
+ij_java_method_call_chain_wrap = off
+ij_java_method_parameters_new_line_after_left_paren = false
+ij_java_method_parameters_right_paren_on_new_line = false
+ij_java_method_parameters_wrap = off
+ij_java_modifier_list_wrap = false
+ij_java_multi_catch_types_wrap = normal
+ij_java_names_count_to_use_import_on_demand = 3
+ij_java_new_line_after_lparen_in_annotation = false
+ij_java_new_line_after_lparen_in_deconstruction_pattern = true
+ij_java_new_line_after_lparen_in_record_header = false
+ij_java_packages_to_use_import_on_demand =
+ij_java_parameter_annotation_wrap = off
+ij_java_parameter_name_prefix =
+ij_java_parameter_name_suffix =
+ij_java_parentheses_expression_new_line_after_left_paren = false
+ij_java_parentheses_expression_right_paren_on_new_line = false
+ij_java_place_assignment_sign_on_next_line = false
+ij_java_prefer_longer_names = true
+ij_java_prefer_parameters_wrap = false
+ij_java_record_components_wrap = normal
+ij_java_repeat_annotations =
+ij_java_repeat_synchronized = true
+ij_java_replace_instanceof_and_cast = false
+ij_java_replace_null_check = true
+ij_java_replace_sum_lambda_with_method_ref = true
+ij_java_resource_list_new_line_after_left_paren = false
+ij_java_resource_list_right_paren_on_new_line = false
+ij_java_resource_list_wrap = off
+ij_java_rparen_on_new_line_in_annotation = false
+ij_java_rparen_on_new_line_in_deconstruction_pattern = true
+ij_java_rparen_on_new_line_in_record_header = false
+ij_java_space_after_closing_angle_bracket_in_type_argument = false
+ij_java_space_after_colon = true
+ij_java_space_after_comma = true
+ij_java_space_after_comma_in_type_arguments = true
+ij_java_space_after_for_semicolon = true
+ij_java_space_after_quest = true
+ij_java_space_after_type_cast = true
+ij_java_space_before_annotation_array_initializer_left_brace = false
+ij_java_space_before_annotation_parameter_list = false
+ij_java_space_before_array_initializer_left_brace = false
+ij_java_space_before_catch_keyword = true
+ij_java_space_before_catch_left_brace = true
+ij_java_space_before_catch_parentheses = true
+ij_java_space_before_class_left_brace = true
+ij_java_space_before_colon = true
+ij_java_space_before_colon_in_foreach = true
+ij_java_space_before_comma = false
+ij_java_space_before_deconstruction_list = false
+ij_java_space_before_do_left_brace = true
+ij_java_space_before_else_keyword = true
+ij_java_space_before_else_left_brace = true
+ij_java_space_before_finally_keyword = true
+ij_java_space_before_finally_left_brace = true
+ij_java_space_before_for_left_brace = true
+ij_java_space_before_for_parentheses = true
+ij_java_space_before_for_semicolon = false
+ij_java_space_before_if_left_brace = true
+ij_java_space_before_if_parentheses = true
+ij_java_space_before_method_call_parentheses = false
+ij_java_space_before_method_left_brace = true
+ij_java_space_before_method_parentheses = false
+ij_java_space_before_opening_angle_bracket_in_type_parameter = false
+ij_java_space_before_quest = true
+ij_java_space_before_switch_left_brace = true
+ij_java_space_before_switch_parentheses = true
+ij_java_space_before_synchronized_left_brace = true
+ij_java_space_before_synchronized_parentheses = true
+ij_java_space_before_try_left_brace = true
+ij_java_space_before_try_parentheses = true
+ij_java_space_before_type_parameter_list = false
+ij_java_space_before_while_keyword = true
+ij_java_space_before_while_left_brace = true
+ij_java_space_before_while_parentheses = true
+ij_java_space_inside_one_line_enum_braces = false
+ij_java_space_within_empty_array_initializer_braces = false
+ij_java_space_within_empty_method_call_parentheses = false
+ij_java_space_within_empty_method_parentheses = false
+ij_java_spaces_around_additive_operators = true
+ij_java_spaces_around_annotation_eq = true
+ij_java_spaces_around_assignment_operators = true
+ij_java_spaces_around_bitwise_operators = true
+ij_java_spaces_around_equality_operators = true
+ij_java_spaces_around_lambda_arrow = true
+ij_java_spaces_around_logical_operators = true
+ij_java_spaces_around_method_ref_dbl_colon = false
+ij_java_spaces_around_multiplicative_operators = true
+ij_java_spaces_around_relational_operators = true
+ij_java_spaces_around_shift_operators = true
+ij_java_spaces_around_type_bounds_in_type_parameters = true
+ij_java_spaces_around_unary_operator = false
+ij_java_spaces_within_angle_brackets = false
+ij_java_spaces_within_annotation_parentheses = false
+ij_java_spaces_within_array_initializer_braces = false
+ij_java_spaces_within_braces = false
+ij_java_spaces_within_brackets = false
+ij_java_spaces_within_cast_parentheses = false
+ij_java_spaces_within_catch_parentheses = false
+ij_java_spaces_within_deconstruction_list = false
+ij_java_spaces_within_for_parentheses = false
+ij_java_spaces_within_if_parentheses = false
+ij_java_spaces_within_method_call_parentheses = false
+ij_java_spaces_within_method_parentheses = false
+ij_java_spaces_within_parentheses = false
+ij_java_spaces_within_record_header = false
+ij_java_spaces_within_switch_parentheses = false
+ij_java_spaces_within_synchronized_parentheses = false
+ij_java_spaces_within_try_parentheses = false
+ij_java_spaces_within_while_parentheses = false
+ij_java_special_else_if_treatment = true
+ij_java_static_field_name_prefix =
+ij_java_static_field_name_suffix =
+ij_java_subclass_name_prefix =
+ij_java_subclass_name_suffix = Impl
+ij_java_ternary_operation_signs_on_next_line = false
+ij_java_ternary_operation_wrap = off
+ij_java_test_name_prefix =
+ij_java_test_name_suffix = Test
+ij_java_throws_keyword_wrap = off
+ij_java_throws_list_wrap = off
+ij_java_use_external_annotations = false
+ij_java_use_fq_class_names = false
+ij_java_use_relative_indents = false
+ij_java_use_single_class_imports = true
+ij_java_variable_annotation_wrap = off
+ij_java_visibility = public
+ij_java_while_brace_force = always
+ij_java_while_on_new_line = false
+ij_java_wrap_comments = false
+ij_java_wrap_first_method_in_call_chain = false
+ij_java_wrap_long_lines = false
+
+[*.nbtt]
+max_line_length = 150
+ij_continuation_indent_size = 4
+ij_smart_tabs = false
+ij_nbtt_keep_indents_on_empty_lines = false
+ij_nbtt_space_after_colon = true
+ij_nbtt_space_after_comma = true
+ij_nbtt_space_before_colon = true
+ij_nbtt_space_before_comma = false
+ij_nbtt_spaces_within_brackets = false
+ij_nbtt_spaces_within_parentheses = false
+
+[*.properties]
+ij_properties_align_group_field_declarations = false
+ij_properties_keep_blank_lines = false
+ij_properties_key_value_delimiter = equals
+ij_properties_spaces_around_key_value_delimiter = false
+
+[*.proto]
+indent_size = 2
+indent_style = space
+tab_width = 2
+ij_continuation_indent_size = 4
+ij_smart_tabs = false
+ij_protobuf_keep_blank_lines_in_code = 2
+ij_protobuf_keep_indents_on_empty_lines = false
+ij_protobuf_keep_line_breaks = true
+ij_protobuf_space_after_comma = true
+ij_protobuf_space_before_comma = false
+ij_protobuf_spaces_around_assignment_operators = true
+ij_protobuf_spaces_within_braces = false
+ij_protobuf_spaces_within_brackets = false
+
+[.editorconfig]
+ij_editorconfig_align_group_field_declarations = false
+ij_editorconfig_space_after_colon = false
+ij_editorconfig_space_after_comma = true
+ij_editorconfig_space_before_colon = false
+ij_editorconfig_space_before_comma = false
+ij_editorconfig_spaces_around_assignment_operators = true
+
+[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
+ij_xml_align_attributes = true
+ij_xml_align_text = false
+ij_xml_attribute_wrap = normal
+ij_xml_block_comment_add_space = false
+ij_xml_block_comment_at_first_column = true
+ij_xml_keep_blank_lines = 2
+ij_xml_keep_indents_on_empty_lines = false
+ij_xml_keep_line_breaks = true
+ij_xml_keep_line_breaks_in_text = true
+ij_xml_keep_whitespaces = false
+ij_xml_keep_whitespaces_around_cdata = preserve
+ij_xml_keep_whitespaces_inside_cdata = false
+ij_xml_line_comment_at_first_column = true
+ij_xml_space_after_tag_name = false
+ij_xml_space_around_equals_in_attribute = false
+ij_xml_space_inside_empty_tag = false
+ij_xml_text_wrap = normal
+
+[{*.ats,*.cts,*.mts,*.ts}]
+ij_continuation_indent_size = 4
+ij_typescript_align_imports = false
+ij_typescript_align_multiline_array_initializer_expression = false
+ij_typescript_align_multiline_binary_operation = false
+ij_typescript_align_multiline_chained_methods = false
+ij_typescript_align_multiline_extends_list = false
+ij_typescript_align_multiline_for = true
+ij_typescript_align_multiline_parameters = true
+ij_typescript_align_multiline_parameters_in_calls = false
+ij_typescript_align_multiline_ternary_operation = false
+ij_typescript_align_object_properties = 0
+ij_typescript_align_union_types = false
+ij_typescript_align_var_statements = 0
+ij_typescript_array_initializer_new_line_after_left_brace = false
+ij_typescript_array_initializer_right_brace_on_new_line = false
+ij_typescript_array_initializer_wrap = off
+ij_typescript_assignment_wrap = off
+ij_typescript_binary_operation_sign_on_next_line = false
+ij_typescript_binary_operation_wrap = off
+ij_typescript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/**
+ij_typescript_blank_lines_after_imports = 1
+ij_typescript_blank_lines_around_class = 1
+ij_typescript_blank_lines_around_field = 0
+ij_typescript_blank_lines_around_field_in_interface = 0
+ij_typescript_blank_lines_around_function = 1
+ij_typescript_blank_lines_around_method = 1
+ij_typescript_blank_lines_around_method_in_interface = 1
+ij_typescript_block_brace_style = end_of_line
+ij_typescript_block_comment_add_space = false
+ij_typescript_block_comment_at_first_column = true
+ij_typescript_call_parameters_new_line_after_left_paren = false
+ij_typescript_call_parameters_right_paren_on_new_line = false
+ij_typescript_call_parameters_wrap = off
+ij_typescript_catch_on_new_line = false
+ij_typescript_chained_call_dot_on_new_line = true
+ij_typescript_class_brace_style = end_of_line
+ij_typescript_comma_on_new_line = false
+ij_typescript_do_while_brace_force = never
+ij_typescript_else_on_new_line = false
+ij_typescript_enforce_trailing_comma = keep
+ij_typescript_enum_constants_wrap = on_every_item
+ij_typescript_extends_keyword_wrap = off
+ij_typescript_extends_list_wrap = off
+ij_typescript_field_prefix = _
+ij_typescript_file_name_style = relaxed
+ij_typescript_finally_on_new_line = false
+ij_typescript_for_brace_force = never
+ij_typescript_for_statement_new_line_after_left_paren = false
+ij_typescript_for_statement_right_paren_on_new_line = false
+ij_typescript_for_statement_wrap = off
+ij_typescript_force_quote_style = false
+ij_typescript_force_semicolon_style = false
+ij_typescript_function_expression_brace_style = end_of_line
+ij_typescript_if_brace_force = never
+ij_typescript_import_merge_members = global
+ij_typescript_import_prefer_absolute_path = global
+ij_typescript_import_sort_members = true
+ij_typescript_import_sort_module_name = false
+ij_typescript_import_use_node_resolution = true
+ij_typescript_imports_wrap = on_every_item
+ij_typescript_indent_case_from_switch = true
+ij_typescript_indent_chained_calls = true
+ij_typescript_indent_package_children = 0
+ij_typescript_jsdoc_include_types = false
+ij_typescript_jsx_attribute_value = braces
+ij_typescript_keep_blank_lines_in_code = 2
+ij_typescript_keep_first_column_comment = true
+ij_typescript_keep_indents_on_empty_lines = false
+ij_typescript_keep_line_breaks = true
+ij_typescript_keep_simple_blocks_in_one_line = false
+ij_typescript_keep_simple_methods_in_one_line = false
+ij_typescript_line_comment_add_space = true
+ij_typescript_line_comment_at_first_column = false
+ij_typescript_method_brace_style = end_of_line
+ij_typescript_method_call_chain_wrap = off
+ij_typescript_method_parameters_new_line_after_left_paren = false
+ij_typescript_method_parameters_right_paren_on_new_line = false
+ij_typescript_method_parameters_wrap = off
+ij_typescript_object_literal_wrap = on_every_item
+ij_typescript_object_types_wrap = on_every_item
+ij_typescript_parentheses_expression_new_line_after_left_paren = false
+ij_typescript_parentheses_expression_right_paren_on_new_line = false
+ij_typescript_place_assignment_sign_on_next_line = false
+ij_typescript_prefer_as_type_cast = false
+ij_typescript_prefer_explicit_types_function_expression_returns = false
+ij_typescript_prefer_explicit_types_function_returns = false
+ij_typescript_prefer_explicit_types_vars_fields = false
+ij_typescript_prefer_parameters_wrap = false
+ij_typescript_property_prefix =
+ij_typescript_reformat_c_style_comments = false
+ij_typescript_space_after_colon = true
+ij_typescript_space_after_comma = true
+ij_typescript_space_after_dots_in_rest_parameter = false
+ij_typescript_space_after_generator_mult = true
+ij_typescript_space_after_property_colon = true
+ij_typescript_space_after_quest = true
+ij_typescript_space_after_type_colon = true
+ij_typescript_space_after_unary_not = false
+ij_typescript_space_before_async_arrow_lparen = true
+ij_typescript_space_before_catch_keyword = true
+ij_typescript_space_before_catch_left_brace = true
+ij_typescript_space_before_catch_parentheses = true
+ij_typescript_space_before_class_lbrace = true
+ij_typescript_space_before_class_left_brace = true
+ij_typescript_space_before_colon = true
+ij_typescript_space_before_comma = false
+ij_typescript_space_before_do_left_brace = true
+ij_typescript_space_before_else_keyword = true
+ij_typescript_space_before_else_left_brace = true
+ij_typescript_space_before_finally_keyword = true
+ij_typescript_space_before_finally_left_brace = true
+ij_typescript_space_before_for_left_brace = true
+ij_typescript_space_before_for_parentheses = true
+ij_typescript_space_before_for_semicolon = false
+ij_typescript_space_before_function_left_parenth = true
+ij_typescript_space_before_generator_mult = false
+ij_typescript_space_before_if_left_brace = true
+ij_typescript_space_before_if_parentheses = true
+ij_typescript_space_before_method_call_parentheses = false
+ij_typescript_space_before_method_left_brace = true
+ij_typescript_space_before_method_parentheses = false
+ij_typescript_space_before_property_colon = false
+ij_typescript_space_before_quest = true
+ij_typescript_space_before_switch_left_brace = true
+ij_typescript_space_before_switch_parentheses = true
+ij_typescript_space_before_try_left_brace = true
+ij_typescript_space_before_type_colon = false
+ij_typescript_space_before_unary_not = false
+ij_typescript_space_before_while_keyword = true
+ij_typescript_space_before_while_left_brace = true
+ij_typescript_space_before_while_parentheses = true
+ij_typescript_spaces_around_additive_operators = true
+ij_typescript_spaces_around_arrow_function_operator = true
+ij_typescript_spaces_around_assignment_operators = true
+ij_typescript_spaces_around_bitwise_operators = true
+ij_typescript_spaces_around_equality_operators = true
+ij_typescript_spaces_around_logical_operators = true
+ij_typescript_spaces_around_multiplicative_operators = true
+ij_typescript_spaces_around_relational_operators = true
+ij_typescript_spaces_around_shift_operators = true
+ij_typescript_spaces_around_unary_operator = false
+ij_typescript_spaces_within_array_initializer_brackets = false
+ij_typescript_spaces_within_brackets = false
+ij_typescript_spaces_within_catch_parentheses = false
+ij_typescript_spaces_within_for_parentheses = false
+ij_typescript_spaces_within_if_parentheses = false
+ij_typescript_spaces_within_imports = false
+ij_typescript_spaces_within_interpolation_expressions = false
+ij_typescript_spaces_within_method_call_parentheses = false
+ij_typescript_spaces_within_method_parentheses = false
+ij_typescript_spaces_within_object_literal_braces = false
+ij_typescript_spaces_within_object_type_braces = true
+ij_typescript_spaces_within_parentheses = false
+ij_typescript_spaces_within_switch_parentheses = false
+ij_typescript_spaces_within_type_assertion = false
+ij_typescript_spaces_within_union_types = true
+ij_typescript_spaces_within_while_parentheses = false
+ij_typescript_special_else_if_treatment = true
+ij_typescript_ternary_operation_signs_on_next_line = false
+ij_typescript_ternary_operation_wrap = off
+ij_typescript_union_types_wrap = on_every_item
+ij_typescript_use_chained_calls_group_indents = false
+ij_typescript_use_double_quotes = true
+ij_typescript_use_explicit_js_extension = auto
+ij_typescript_use_import_type = auto
+ij_typescript_use_path_mapping = always
+ij_typescript_use_public_modifier = false
+ij_typescript_use_semicolon_after_statement = true
+ij_typescript_var_declaration_wrap = normal
+ij_typescript_while_brace_force = never
+ij_typescript_while_on_new_line = false
+ij_typescript_wrap_comments = false
+
+[{*.bash,*.sh,*.zsh}]
+indent_size = 2
+indent_style = space
+tab_width = 2
+ij_shell_binary_ops_start_line = false
+ij_shell_keep_column_alignment_padding = false
+ij_shell_minify_program = false
+ij_shell_redirect_followed_by_space = false
+ij_shell_switch_cases_indented = false
+ij_shell_use_unix_line_separator = true
+
+[{*.cjs,*.js}]
+indent_size = 2
+tab_width = 2
+ij_continuation_indent_size = 4
+ij_javascript_align_imports = false
+ij_javascript_align_multiline_array_initializer_expression = false
+ij_javascript_align_multiline_binary_operation = false
+ij_javascript_align_multiline_chained_methods = false
+ij_javascript_align_multiline_extends_list = false
+ij_javascript_align_multiline_for = true
+ij_javascript_align_multiline_parameters = true
+ij_javascript_align_multiline_parameters_in_calls = false
+ij_javascript_align_multiline_ternary_operation = false
+ij_javascript_align_object_properties = 0
+ij_javascript_align_union_types = false
+ij_javascript_align_var_statements = 0
+ij_javascript_array_initializer_new_line_after_left_brace = false
+ij_javascript_array_initializer_right_brace_on_new_line = false
+ij_javascript_array_initializer_wrap = off
+ij_javascript_assignment_wrap = off
+ij_javascript_binary_operation_sign_on_next_line = false
+ij_javascript_binary_operation_wrap = off
+ij_javascript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/**
+ij_javascript_blank_lines_after_imports = 1
+ij_javascript_blank_lines_around_class = 1
+ij_javascript_blank_lines_around_field = 0
+ij_javascript_blank_lines_around_function = 1
+ij_javascript_blank_lines_around_method = 1
+ij_javascript_block_brace_style = end_of_line
+ij_javascript_block_comment_add_space = false
+ij_javascript_block_comment_at_first_column = true
+ij_javascript_call_parameters_new_line_after_left_paren = false
+ij_javascript_call_parameters_right_paren_on_new_line = false
+ij_javascript_call_parameters_wrap = off
+ij_javascript_catch_on_new_line = false
+ij_javascript_chained_call_dot_on_new_line = true
+ij_javascript_class_brace_style = end_of_line
+ij_javascript_comma_on_new_line = false
+ij_javascript_do_while_brace_force = never
+ij_javascript_else_on_new_line = false
+ij_javascript_enforce_trailing_comma = keep
+ij_javascript_extends_keyword_wrap = off
+ij_javascript_extends_list_wrap = off
+ij_javascript_field_prefix = _
+ij_javascript_file_name_style = relaxed
+ij_javascript_finally_on_new_line = false
+ij_javascript_for_brace_force = never
+ij_javascript_for_statement_new_line_after_left_paren = false
+ij_javascript_for_statement_right_paren_on_new_line = false
+ij_javascript_for_statement_wrap = off
+ij_javascript_force_quote_style = false
+ij_javascript_force_semicolon_style = false
+ij_javascript_function_expression_brace_style = end_of_line
+ij_javascript_if_brace_force = never
+ij_javascript_import_merge_members = global
+ij_javascript_import_prefer_absolute_path = global
+ij_javascript_import_sort_members = true
+ij_javascript_import_sort_module_name = false
+ij_javascript_import_use_node_resolution = true
+ij_javascript_imports_wrap = on_every_item
+ij_javascript_indent_case_from_switch = true
+ij_javascript_indent_chained_calls = true
+ij_javascript_indent_package_children = 0
+ij_javascript_jsx_attribute_value = braces
+ij_javascript_keep_blank_lines_in_code = 2
+ij_javascript_keep_first_column_comment = true
+ij_javascript_keep_indents_on_empty_lines = false
+ij_javascript_keep_line_breaks = true
+ij_javascript_keep_simple_blocks_in_one_line = false
+ij_javascript_keep_simple_methods_in_one_line = false
+ij_javascript_line_comment_add_space = true
+ij_javascript_line_comment_at_first_column = false
+ij_javascript_method_brace_style = end_of_line
+ij_javascript_method_call_chain_wrap = off
+ij_javascript_method_parameters_new_line_after_left_paren = false
+ij_javascript_method_parameters_right_paren_on_new_line = false
+ij_javascript_method_parameters_wrap = off
+ij_javascript_object_literal_wrap = on_every_item
+ij_javascript_object_types_wrap = on_every_item
+ij_javascript_parentheses_expression_new_line_after_left_paren = false
+ij_javascript_parentheses_expression_right_paren_on_new_line = false
+ij_javascript_place_assignment_sign_on_next_line = false
+ij_javascript_prefer_as_type_cast = false
+ij_javascript_prefer_explicit_types_function_expression_returns = false
+ij_javascript_prefer_explicit_types_function_returns = false
+ij_javascript_prefer_explicit_types_vars_fields = false
+ij_javascript_prefer_parameters_wrap = false
+ij_javascript_property_prefix =
+ij_javascript_reformat_c_style_comments = false
+ij_javascript_space_after_colon = true
+ij_javascript_space_after_comma = true
+ij_javascript_space_after_dots_in_rest_parameter = false
+ij_javascript_space_after_generator_mult = true
+ij_javascript_space_after_property_colon = true
+ij_javascript_space_after_quest = true
+ij_javascript_space_after_type_colon = true
+ij_javascript_space_after_unary_not = false
+ij_javascript_space_before_async_arrow_lparen = true
+ij_javascript_space_before_catch_keyword = true
+ij_javascript_space_before_catch_left_brace = true
+ij_javascript_space_before_catch_parentheses = true
+ij_javascript_space_before_class_lbrace = true
+ij_javascript_space_before_class_left_brace = true
+ij_javascript_space_before_colon = true
+ij_javascript_space_before_comma = false
+ij_javascript_space_before_do_left_brace = true
+ij_javascript_space_before_else_keyword = true
+ij_javascript_space_before_else_left_brace = true
+ij_javascript_space_before_finally_keyword = true
+ij_javascript_space_before_finally_left_brace = true
+ij_javascript_space_before_for_left_brace = true
+ij_javascript_space_before_for_parentheses = true
+ij_javascript_space_before_for_semicolon = false
+ij_javascript_space_before_function_left_parenth = true
+ij_javascript_space_before_generator_mult = false
+ij_javascript_space_before_if_left_brace = true
+ij_javascript_space_before_if_parentheses = true
+ij_javascript_space_before_method_call_parentheses = false
+ij_javascript_space_before_method_left_brace = true
+ij_javascript_space_before_method_parentheses = false
+ij_javascript_space_before_property_colon = false
+ij_javascript_space_before_quest = true
+ij_javascript_space_before_switch_left_brace = true
+ij_javascript_space_before_switch_parentheses = true
+ij_javascript_space_before_try_left_brace = true
+ij_javascript_space_before_type_colon = false
+ij_javascript_space_before_unary_not = false
+ij_javascript_space_before_while_keyword = true
+ij_javascript_space_before_while_left_brace = true
+ij_javascript_space_before_while_parentheses = true
+ij_javascript_spaces_around_additive_operators = true
+ij_javascript_spaces_around_arrow_function_operator = true
+ij_javascript_spaces_around_assignment_operators = true
+ij_javascript_spaces_around_bitwise_operators = true
+ij_javascript_spaces_around_equality_operators = true
+ij_javascript_spaces_around_logical_operators = true
+ij_javascript_spaces_around_multiplicative_operators = true
+ij_javascript_spaces_around_relational_operators = true
+ij_javascript_spaces_around_shift_operators = true
+ij_javascript_spaces_around_unary_operator = false
+ij_javascript_spaces_within_array_initializer_brackets = false
+ij_javascript_spaces_within_brackets = false
+ij_javascript_spaces_within_catch_parentheses = false
+ij_javascript_spaces_within_for_parentheses = false
+ij_javascript_spaces_within_if_parentheses = false
+ij_javascript_spaces_within_imports = false
+ij_javascript_spaces_within_interpolation_expressions = false
+ij_javascript_spaces_within_method_call_parentheses = false
+ij_javascript_spaces_within_method_parentheses = false
+ij_javascript_spaces_within_object_literal_braces = false
+ij_javascript_spaces_within_object_type_braces = true
+ij_javascript_spaces_within_parentheses = false
+ij_javascript_spaces_within_switch_parentheses = false
+ij_javascript_spaces_within_type_assertion = false
+ij_javascript_spaces_within_union_types = true
+ij_javascript_spaces_within_while_parentheses = false
+ij_javascript_special_else_if_treatment = true
+ij_javascript_ternary_operation_signs_on_next_line = false
+ij_javascript_ternary_operation_wrap = off
+ij_javascript_union_types_wrap = on_every_item
+ij_javascript_use_chained_calls_group_indents = false
+ij_javascript_use_double_quotes = true
+ij_javascript_use_explicit_js_extension = auto
+ij_javascript_use_import_type = auto
+ij_javascript_use_path_mapping = always
+ij_javascript_use_public_modifier = false
+ij_javascript_use_semicolon_after_statement = true
+ij_javascript_var_declaration_wrap = normal
+ij_javascript_while_brace_force = never
+ij_javascript_while_on_new_line = false
+ij_javascript_wrap_comments = false
+
+[{*.comp,*.frag,*.fsh,*.geom,*.glsl,*.tesc,*.tese,*.vert,*.vsh}]
+ij_glsl_keep_indents_on_empty_lines = false
+
+[{*.gant,*.groovy,*.gy}]
+ij_smart_tabs = false
+ij_groovy_align_group_field_declarations = false
+ij_groovy_align_multiline_array_initializer_expression = false
+ij_groovy_align_multiline_assignment = false
+ij_groovy_align_multiline_binary_operation = false
+ij_groovy_align_multiline_chained_methods = false
+ij_groovy_align_multiline_extends_list = false
+ij_groovy_align_multiline_for = true
+ij_groovy_align_multiline_list_or_map = true
+ij_groovy_align_multiline_method_parentheses = false
+ij_groovy_align_multiline_parameters = true
+ij_groovy_align_multiline_parameters_in_calls = false
+ij_groovy_align_multiline_resources = true
+ij_groovy_align_multiline_ternary_operation = false
+ij_groovy_align_multiline_throws_list = false
+ij_groovy_align_named_args_in_map = true
+ij_groovy_align_throws_keyword = false
+ij_groovy_array_initializer_new_line_after_left_brace = false
+ij_groovy_array_initializer_right_brace_on_new_line = false
+ij_groovy_array_initializer_wrap = off
+ij_groovy_assert_statement_wrap = off
+ij_groovy_assignment_wrap = off
+ij_groovy_binary_operation_wrap = off
+ij_groovy_blank_lines_after_class_header = 0
+ij_groovy_blank_lines_after_imports = 1
+ij_groovy_blank_lines_after_package = 1
+ij_groovy_blank_lines_around_class = 1
+ij_groovy_blank_lines_around_field = 0
+ij_groovy_blank_lines_around_field_in_interface = 0
+ij_groovy_blank_lines_around_method = 1
+ij_groovy_blank_lines_around_method_in_interface = 1
+ij_groovy_blank_lines_before_imports = 1
+ij_groovy_blank_lines_before_method_body = 0
+ij_groovy_blank_lines_before_package = 0
+ij_groovy_block_brace_style = end_of_line
+ij_groovy_block_comment_add_space = false
+ij_groovy_block_comment_at_first_column = true
+ij_groovy_call_parameters_new_line_after_left_paren = false
+ij_groovy_call_parameters_right_paren_on_new_line = false
+ij_groovy_call_parameters_wrap = off
+ij_groovy_catch_on_new_line = false
+ij_groovy_class_annotation_wrap = split_into_lines
+ij_groovy_class_brace_style = end_of_line
+ij_groovy_class_count_to_use_import_on_demand = 5
+ij_groovy_do_while_brace_force = never
+ij_groovy_else_on_new_line = false
+ij_groovy_enable_groovydoc_formatting = true
+ij_groovy_enum_constants_wrap = off
+ij_groovy_extends_keyword_wrap = off
+ij_groovy_extends_list_wrap = off
+ij_groovy_field_annotation_wrap = split_into_lines
+ij_groovy_finally_on_new_line = false
+ij_groovy_for_brace_force = never
+ij_groovy_for_statement_new_line_after_left_paren = false
+ij_groovy_for_statement_right_paren_on_new_line = false
+ij_groovy_for_statement_wrap = off
+ij_groovy_ginq_general_clause_wrap_policy = 2
+ij_groovy_ginq_having_wrap_policy = 1
+ij_groovy_ginq_indent_having_clause = true
+ij_groovy_ginq_indent_on_clause = true
+ij_groovy_ginq_on_wrap_policy = 1
+ij_groovy_ginq_space_after_keyword = true
+ij_groovy_if_brace_force = never
+ij_groovy_import_annotation_wrap = 2
+ij_groovy_imports_layout = *, |, javax.**, java.**, |, $*
+ij_groovy_indent_case_from_switch = true
+ij_groovy_indent_label_blocks = true
+ij_groovy_insert_inner_class_imports = false
+ij_groovy_keep_blank_lines_before_right_brace = 2
+ij_groovy_keep_blank_lines_in_code = 2
+ij_groovy_keep_blank_lines_in_declarations = 2
+ij_groovy_keep_control_statement_in_one_line = true
+ij_groovy_keep_first_column_comment = true
+ij_groovy_keep_indents_on_empty_lines = false
+ij_groovy_keep_line_breaks = true
+ij_groovy_keep_multiple_expressions_in_one_line = false
+ij_groovy_keep_simple_blocks_in_one_line = false
+ij_groovy_keep_simple_classes_in_one_line = true
+ij_groovy_keep_simple_lambdas_in_one_line = true
+ij_groovy_keep_simple_methods_in_one_line = true
+ij_groovy_label_indent_absolute = false
+ij_groovy_label_indent_size = 0
+ij_groovy_lambda_brace_style = end_of_line
+ij_groovy_layout_static_imports_separately = true
+ij_groovy_line_comment_add_space = false
+ij_groovy_line_comment_add_space_on_reformat = false
+ij_groovy_line_comment_at_first_column = true
+ij_groovy_method_annotation_wrap = split_into_lines
+ij_groovy_method_brace_style = end_of_line
+ij_groovy_method_call_chain_wrap = off
+ij_groovy_method_parameters_new_line_after_left_paren = false
+ij_groovy_method_parameters_right_paren_on_new_line = false
+ij_groovy_method_parameters_wrap = off
+ij_groovy_modifier_list_wrap = false
+ij_groovy_names_count_to_use_import_on_demand = 3
+ij_groovy_packages_to_use_import_on_demand = java.awt.*, javax.swing.*
+ij_groovy_parameter_annotation_wrap = off
+ij_groovy_parentheses_expression_new_line_after_left_paren = false
+ij_groovy_parentheses_expression_right_paren_on_new_line = false
+ij_groovy_prefer_parameters_wrap = false
+ij_groovy_resource_list_new_line_after_left_paren = false
+ij_groovy_resource_list_right_paren_on_new_line = false
+ij_groovy_resource_list_wrap = off
+ij_groovy_space_after_assert_separator = true
+ij_groovy_space_after_colon = true
+ij_groovy_space_after_comma = true
+ij_groovy_space_after_comma_in_type_arguments = true
+ij_groovy_space_after_for_semicolon = true
+ij_groovy_space_after_quest = true
+ij_groovy_space_after_type_cast = true
+ij_groovy_space_before_annotation_parameter_list = false
+ij_groovy_space_before_array_initializer_left_brace = false
+ij_groovy_space_before_assert_separator = false
+ij_groovy_space_before_catch_keyword = true
+ij_groovy_space_before_catch_left_brace = true
+ij_groovy_space_before_catch_parentheses = true
+ij_groovy_space_before_class_left_brace = true
+ij_groovy_space_before_closure_left_brace = true
+ij_groovy_space_before_colon = true
+ij_groovy_space_before_comma = false
+ij_groovy_space_before_do_left_brace = true
+ij_groovy_space_before_else_keyword = true
+ij_groovy_space_before_else_left_brace = true
+ij_groovy_space_before_finally_keyword = true
+ij_groovy_space_before_finally_left_brace = true
+ij_groovy_space_before_for_left_brace = true
+ij_groovy_space_before_for_parentheses = true
+ij_groovy_space_before_for_semicolon = false
+ij_groovy_space_before_if_left_brace = true
+ij_groovy_space_before_if_parentheses = true
+ij_groovy_space_before_method_call_parentheses = false
+ij_groovy_space_before_method_left_brace = true
+ij_groovy_space_before_method_parentheses = false
+ij_groovy_space_before_quest = true
+ij_groovy_space_before_record_parentheses = false
+ij_groovy_space_before_switch_left_brace = true
+ij_groovy_space_before_switch_parentheses = true
+ij_groovy_space_before_synchronized_left_brace = true
+ij_groovy_space_before_synchronized_parentheses = true
+ij_groovy_space_before_try_left_brace = true
+ij_groovy_space_before_try_parentheses = true
+ij_groovy_space_before_while_keyword = true
+ij_groovy_space_before_while_left_brace = true
+ij_groovy_space_before_while_parentheses = true
+ij_groovy_space_in_named_argument = true
+ij_groovy_space_in_named_argument_before_colon = false
+ij_groovy_space_within_empty_array_initializer_braces = false
+ij_groovy_space_within_empty_method_call_parentheses = false
+ij_groovy_spaces_around_additive_operators = true
+ij_groovy_spaces_around_assignment_operators = true
+ij_groovy_spaces_around_bitwise_operators = true
+ij_groovy_spaces_around_equality_operators = true
+ij_groovy_spaces_around_lambda_arrow = true
+ij_groovy_spaces_around_logical_operators = true
+ij_groovy_spaces_around_multiplicative_operators = true
+ij_groovy_spaces_around_regex_operators = true
+ij_groovy_spaces_around_relational_operators = true
+ij_groovy_spaces_around_shift_operators = true
+ij_groovy_spaces_within_annotation_parentheses = false
+ij_groovy_spaces_within_array_initializer_braces = false
+ij_groovy_spaces_within_braces = true
+ij_groovy_spaces_within_brackets = false
+ij_groovy_spaces_within_cast_parentheses = false
+ij_groovy_spaces_within_catch_parentheses = false
+ij_groovy_spaces_within_for_parentheses = false
+ij_groovy_spaces_within_gstring_injection_braces = false
+ij_groovy_spaces_within_if_parentheses = false
+ij_groovy_spaces_within_list_or_map = false
+ij_groovy_spaces_within_method_call_parentheses = false
+ij_groovy_spaces_within_method_parentheses = false
+ij_groovy_spaces_within_parentheses = false
+ij_groovy_spaces_within_switch_parentheses = false
+ij_groovy_spaces_within_synchronized_parentheses = false
+ij_groovy_spaces_within_try_parentheses = false
+ij_groovy_spaces_within_tuple_expression = false
+ij_groovy_spaces_within_while_parentheses = false
+ij_groovy_special_else_if_treatment = true
+ij_groovy_ternary_operation_wrap = off
+ij_groovy_throws_keyword_wrap = off
+ij_groovy_throws_list_wrap = off
+ij_groovy_use_flying_geese_braces = false
+ij_groovy_use_fq_class_names = false
+ij_groovy_use_fq_class_names_in_javadoc = true
+ij_groovy_use_relative_indents = false
+ij_groovy_use_single_class_imports = true
+ij_groovy_variable_annotation_wrap = off
+ij_groovy_while_brace_force = never
+ij_groovy_while_on_new_line = false
+ij_groovy_wrap_chain_calls_after_dot = false
+ij_groovy_wrap_long_lines = false
+
+[{*.gradle.kts,*.kt,*.kts,*.main.kts,*.space.kts}]
+indent_style = space
+ij_smart_tabs = false
+ij_kotlin_align_in_columns_case_branch = false
+ij_kotlin_align_multiline_binary_operation = false
+ij_kotlin_align_multiline_extends_list = false
+ij_kotlin_align_multiline_method_parentheses = false
+ij_kotlin_align_multiline_parameters = true
+ij_kotlin_align_multiline_parameters_in_calls = false
+ij_kotlin_allow_trailing_comma = false
+ij_kotlin_allow_trailing_comma_on_call_site = false
+ij_kotlin_assignment_wrap = normal
+ij_kotlin_blank_lines_after_class_header = 0
+ij_kotlin_blank_lines_around_block_when_branches = 0
+ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
+ij_kotlin_block_comment_add_space = false
+ij_kotlin_block_comment_at_first_column = true
+ij_kotlin_call_parameters_new_line_after_left_paren = true
+ij_kotlin_call_parameters_right_paren_on_new_line = true
+ij_kotlin_call_parameters_wrap = on_every_item
+ij_kotlin_catch_on_new_line = false
+ij_kotlin_class_annotation_wrap = split_into_lines
+ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
+ij_kotlin_continuation_indent_for_chained_calls = false
+ij_kotlin_continuation_indent_for_expression_bodies = false
+ij_kotlin_continuation_indent_in_argument_lists = false
+ij_kotlin_continuation_indent_in_elvis = false
+ij_kotlin_continuation_indent_in_if_conditions = false
+ij_kotlin_continuation_indent_in_parameter_lists = false
+ij_kotlin_continuation_indent_in_supertype_lists = false
+ij_kotlin_else_on_new_line = false
+ij_kotlin_enum_constants_wrap = off
+ij_kotlin_extends_list_wrap = normal
+ij_kotlin_field_annotation_wrap = split_into_lines
+ij_kotlin_finally_on_new_line = false
+ij_kotlin_if_rparen_on_new_line = true
+ij_kotlin_import_nested_classes = false
+ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^
+ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
+ij_kotlin_keep_blank_lines_before_right_brace = 2
+ij_kotlin_keep_blank_lines_in_code = 2
+ij_kotlin_keep_blank_lines_in_declarations = 2
+ij_kotlin_keep_first_column_comment = true
+ij_kotlin_keep_indents_on_empty_lines = false
+ij_kotlin_keep_line_breaks = true
+ij_kotlin_lbrace_on_next_line = false
+ij_kotlin_line_break_after_multiline_when_entry = true
+ij_kotlin_line_comment_add_space = false
+ij_kotlin_line_comment_add_space_on_reformat = false
+ij_kotlin_line_comment_at_first_column = true
+ij_kotlin_method_annotation_wrap = split_into_lines
+ij_kotlin_method_call_chain_wrap = normal
+ij_kotlin_method_parameters_new_line_after_left_paren = true
+ij_kotlin_method_parameters_right_paren_on_new_line = true
+ij_kotlin_method_parameters_wrap = on_every_item
+ij_kotlin_name_count_to_use_star_import = 5
+ij_kotlin_name_count_to_use_star_import_for_members = 3
+ij_kotlin_packages_to_use_import_on_demand = java.util.*, kotlinx.android.synthetic.**, io.ktor.**
+ij_kotlin_parameter_annotation_wrap = off
+ij_kotlin_space_after_comma = true
+ij_kotlin_space_after_extend_colon = true
+ij_kotlin_space_after_type_colon = true
+ij_kotlin_space_before_catch_parentheses = true
+ij_kotlin_space_before_comma = false
+ij_kotlin_space_before_extend_colon = true
+ij_kotlin_space_before_for_parentheses = true
+ij_kotlin_space_before_if_parentheses = true
+ij_kotlin_space_before_lambda_arrow = true
+ij_kotlin_space_before_type_colon = false
+ij_kotlin_space_before_when_parentheses = true
+ij_kotlin_space_before_while_parentheses = true
+ij_kotlin_spaces_around_additive_operators = true
+ij_kotlin_spaces_around_assignment_operators = true
+ij_kotlin_spaces_around_equality_operators = true
+ij_kotlin_spaces_around_function_type_arrow = true
+ij_kotlin_spaces_around_logical_operators = true
+ij_kotlin_spaces_around_multiplicative_operators = true
+ij_kotlin_spaces_around_range = false
+ij_kotlin_spaces_around_relational_operators = true
+ij_kotlin_spaces_around_unary_operator = false
+ij_kotlin_spaces_around_when_arrow = true
+ij_kotlin_variable_annotation_wrap = off
+ij_kotlin_while_on_new_line = false
+ij_kotlin_wrap_elvis_expressions = 1
+ij_kotlin_wrap_expression_body_functions = 1
+ij_kotlin_wrap_first_method_in_call_chain = false
+
+[{*.har,*.jsb2,*.jsb3,*.json,*.jsonc,*.png.mcmeta,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config,mcmod.info,pack.mcmeta}]
+indent_size = 2
+tab_width = 2
+ij_smart_tabs = false
+ij_json_array_wrapping = on_every_item
+ij_json_keep_blank_lines_in_code = 0
+ij_json_keep_indents_on_empty_lines = false
+ij_json_keep_line_breaks = true
+ij_json_keep_trailing_comma = false
+ij_json_object_wrapping = split_into_lines
+ij_json_property_alignment = do_not_align
+ij_json_space_after_colon = true
+ij_json_space_after_comma = true
+ij_json_space_before_colon = false
+ij_json_space_before_comma = false
+ij_json_spaces_within_braces = false
+ij_json_spaces_within_brackets = false
+ij_json_wrap_long_lines = false
+
+[*.mixins.json]
+ij_json_array_wrapping = split_into_lines
+
+[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
+ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
+ij_html_align_attributes = true
+ij_html_align_text = false
+ij_html_attribute_wrap = normal
+ij_html_block_comment_add_space = false
+ij_html_block_comment_at_first_column = true
+ij_html_do_not_align_children_of_min_lines = 0
+ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p
+ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot
+ij_html_enforce_quotes = false
+ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var
+ij_html_keep_blank_lines = 2
+ij_html_keep_indents_on_empty_lines = false
+ij_html_keep_line_breaks = true
+ij_html_keep_line_breaks_in_text = true
+ij_html_keep_whitespaces = false
+ij_html_keep_whitespaces_inside = span, pre, textarea
+ij_html_line_comment_at_first_column = true
+ij_html_new_line_after_last_attribute = never
+ij_html_new_line_before_first_attribute = never
+ij_html_quote_style = double
+ij_html_remove_new_line_before_tags = br
+ij_html_space_after_tag_name = false
+ij_html_space_around_equality_in_attribute = false
+ij_html_space_inside_empty_tag = false
+ij_html_text_wrap = normal
+
+[{*.markdown,*.md}]
+indent_style = space
+ij_smart_tabs = false
+ij_markdown_force_one_space_after_blockquote_symbol = true
+ij_markdown_force_one_space_after_header_symbol = true
+ij_markdown_force_one_space_after_list_bullet = true
+ij_markdown_force_one_space_between_words = true
+ij_markdown_format_tables = true
+ij_markdown_insert_quote_arrows_on_wrap = true
+ij_markdown_keep_indents_on_empty_lines = false
+ij_markdown_keep_line_breaks_inside_text_blocks = true
+ij_markdown_max_lines_around_block_elements = 1
+ij_markdown_max_lines_around_header = 1
+ij_markdown_max_lines_between_paragraphs = 1
+ij_markdown_min_lines_around_block_elements = 1
+ij_markdown_min_lines_around_header = 1
+ij_markdown_min_lines_between_paragraphs = 1
+ij_markdown_wrap_text_if_long = true
+ij_markdown_wrap_text_inside_blockquotes = true
+
+[{*.pb,*.textproto,*.txtpb}]
+indent_size = 2
+indent_style = space
+tab_width = 2
+ij_continuation_indent_size = 4
+ij_smart_tabs = false
+ij_prototext_keep_blank_lines_in_code = 2
+ij_prototext_keep_indents_on_empty_lines = false
+ij_prototext_keep_line_breaks = true
+ij_prototext_space_after_colon = true
+ij_prototext_space_after_comma = true
+ij_prototext_space_before_colon = false
+ij_prototext_space_before_comma = false
+ij_prototext_spaces_within_braces = true
+ij_prototext_spaces_within_brackets = false
+
+[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}]
+indent_style = space
+ij_smart_tabs = false
+ij_toml_keep_indents_on_empty_lines = false
+
+[{*.yaml,*.yml}]
+indent_size = 2
+ij_yaml_align_values_properties = do_not_align
+ij_yaml_autoinsert_sequence_marker = true
+ij_yaml_block_mapping_on_new_line = false
+ij_yaml_indent_sequence_value = true
+ij_yaml_keep_indents_on_empty_lines = false
+ij_yaml_keep_line_breaks = true
+ij_yaml_sequence_on_new_line = false
+ij_yaml_space_before_colon = false
+ij_yaml_spaces_within_braces = true
+ij_yaml_spaces_within_brackets = true
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..9b0abb0
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,31 @@
+name: Java CI
+
+on:
+ push:
+ branches:
+ - 'main'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ if: |
+ !contains(github.event.head_commit.message, '[ci skip]')
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Set up JDK 17
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+ cache: gradle
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+ - name: Validate Gradle Wrapper
+ uses: gradle/wrapper-validation-action@v1
+ - name: Build and Publish with Gradle
+ uses: gradle/gradle-build-action@v2
+ env:
+ MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }}
+ with:
+ arguments: build publish --stacktrace --no-daemon
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index a5b1113..3c37caf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,21 +1,118 @@
+# User-specific stuff
+.idea/
+
+*.iml
+*.ipr
+*.iws
+
+# IntelliJ
+out/
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
.gradle
-**/build/
-!src/**/build/
+build/
# Ignore Gradle GUI config
gradle-app.setting
-# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
-!gradle-wrapper.jar
-
-# Avoid ignore Gradle wrappper properties
-!gradle-wrapper.properties
-
# Cache of project
.gradletasknamecache
-# Eclipse Gradle plugin generated files
-# Eclipse Core
-.project
-# JDT-specific (Eclipse Java Development Tools)
-.classpath
+**/build/
+
+# Common working directory
+run/
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..a814fc2
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,90 @@
+import java.time.Instant
+
+plugins {
+ id 'fabric-loom' version '1.4-SNAPSHOT'
+ id 'maven-publish'
+}
+
+var ENV = System.getenv()
+version = "${mod_version}-${ENV.GITHUB_RUN_NUMBER ? 'build.' + ENV.GITHUB_RUN_NUMBER : 'local.' + Instant.now().epochSecond}"
+group = project.maven_group
+println("Version: $group:$archives_base_name:$version")
+
+loom {
+ accessWidenerPath = file('src/main/resources/kmath.accessWidener')
+}
+
+repositories {
+}
+
+dependencies {
+ minecraft "com.mojang:minecraft:${project.minecraft_version}"
+ mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
+
+ modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
+ modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
+}
+
+processResources {
+ inputs.property "version", project.version
+ filteringCharset "UTF-8"
+
+ filesMatching("fabric.mod.json") {
+ expand "version": project.version
+ }
+}
+
+def targetJavaVersion = 17
+tasks.withType(JavaCompile).configureEach {
+ // ensure that the encoding is set to UTF-8, no matter what the system default is
+ // this fixes some edge cases with special characters not displaying correctly
+ // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
+ // If Javadoc is generated, this must be specified in that task too.
+ it.options.encoding = "UTF-8"
+ if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
+ it.options.release = targetJavaVersion
+ }
+}
+
+java {
+ def javaVersion = JavaVersion.toVersion(targetJavaVersion)
+ if (JavaVersion.current() < javaVersion) {
+ toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
+ }
+ archivesBaseName = project.archives_base_name
+ // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
+ // if it is present.
+ // If you remove this line, sources will not be generated.
+ withSourcesJar()
+}
+
+jar {
+ from("LICENSE") {
+ rename { "${it}_${project.archivesBaseName}" }
+ }
+
+ from("LICENSES") {
+ into "LICENSES"
+ }
+}
+
+publishing {
+ publications {
+ mavenKMath(MavenPublication) {
+ artifactId = 'kmath'
+ from components.java
+ }
+ }
+
+ repositories {
+ if (ENV.MAVEN_TOKEN) {
+ maven {
+ url "https://maven.latvian.dev/releases"
+ credentials {
+ username = "lat"
+ password = "${ENV.MAVEN_TOKEN}"
+ }
+ }
+ }
+ }
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..25f3e33
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,13 @@
+# Done to increase the memory available to gradle.
+org.gradle.jvmargs=-Xmx3G
+org.gradle.daemon=false
+# Fabric Properties
+# check these on https://modmuss50.me/fabric.html
+minecraft_version=1.20.1
+yarn_mappings=1.20.1+build.10
+fabric_version=0.86.1+1.20.1
+loader_version=0.14.22
+# Mod Properties
+mod_version=2001.1.0
+maven_group=dev.latvian.mods
+archives_base_name=kmath
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..136ea08
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..027b233
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,9 @@
+pluginManagement {
+ repositories {
+ maven {
+ name = 'Fabric'
+ url = 'https://maven.fabricmc.net/'
+ }
+ gradlePluginPortal()
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/KMath.java b/src/main/java/dev/latvian/mods/kmath/KMath.java
new file mode 100644
index 0000000..97566d9
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/KMath.java
@@ -0,0 +1,135 @@
+package dev.latvian.mods.kmath;
+
+import net.minecraft.nbt.AbstractNbtNumber;
+import net.minecraft.nbt.NbtByte;
+import net.minecraft.nbt.NbtFloat;
+import net.minecraft.nbt.NbtInt;
+import net.minecraft.nbt.NbtShort;
+
+@SuppressWarnings("ManualMinMaxCalculation")
+public interface KMath {
+
+ static String format(float value) {
+ if (value == (int) value) {
+ return Integer.toString((int) value);
+ }
+
+ return Float.toString(value);
+ }
+
+ static AbstractNbtNumber efficient(float num) {
+ var i = (int) num;
+
+ if (i == num) {
+ if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
+ return NbtByte.of((byte) i);
+ }
+
+ if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
+ return NbtShort.of((short) i);
+ }
+
+ return NbtInt.of(i);
+ } else {
+ return NbtFloat.of(num);
+ }
+ }
+
+ static double sq(double t) {
+ return t * t;
+ }
+
+ static float sq(float t) {
+ return t * t;
+ }
+
+ static int floor(double value) {
+ int i = (int) value;
+ return value < i ? i - 1 : i;
+ }
+
+ static long lfloor(double value) {
+ long i = (long) value;
+ return value < i ? i - 1L : i;
+ }
+
+ static int floor(float value) {
+ int i = (int) value;
+ return value < i ? i - 1 : i;
+ }
+
+ static int ceil(double value) {
+ int i = (int) value;
+ return value > i ? i + 1 : i;
+ }
+
+ static long lceil(double value) {
+ long i = (long) value;
+ return value > i ? i + 1L : i;
+ }
+
+ static int ceil(float value) {
+ int i = (int) value;
+ return value > i ? i + 1 : i;
+ }
+
+ static double clamp(double value, double min, double max) {
+ return value < min ? min : value <= max ? value : max;
+ }
+
+ static float clamp(float value, float min, float max) {
+ return value < min ? min : value <= max ? value : max;
+ }
+
+ static int clamp(int value, int min, int max) {
+ return value < min ? min : value <= max ? value : max;
+ }
+
+ static double lerp(double value, double min, double max) {
+ return min + value * (max - min);
+ }
+
+ static float lerp(float value, float min, float max) {
+ return min + value * (max - min);
+ }
+
+ static double clerp(double value, double min, double max) {
+ return value < 0D ? min : value > 1D ? max : (min + value * (max - min));
+ }
+
+ static float clerp(float value, float min, float max) {
+ return value < 0F ? min : value > 1F ? max : (min + value * (max - min));
+ }
+
+ static double map(double value, double min0, double max0, double min1, double max1) {
+ return min1 + (max1 - min1) * ((value - min0) / (max0 - min0));
+ }
+
+ static float map(float value, float min0, float max0, float min1, float max1) {
+ return min1 + (max1 - min1) * ((value - min0) / (max0 - min0));
+ }
+
+ static double smoothstep(double t) {
+ return t * t * (3D - 2D * t);
+ }
+
+ static float smoothstep(float t) {
+ return t * t * (3F - 2F * t);
+ }
+
+ static double ismoothstep(double t) {
+ return t + (t - (t * t * (3D - 2D * t)));
+ }
+
+ static float ismoothstep(float t) {
+ return t + (t - (t * t * (3F - 2F * t)));
+ }
+
+ static double smootherstep(double t) {
+ return t * t * t * (t * (t * 6D - 15D) + 10D);
+ }
+
+ static float smootherstep(float t) {
+ return t * t * t * (t * (t * 6F - 15F) + 10F);
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/KStore.java b/src/main/java/dev/latvian/mods/kmath/KStore.java
new file mode 100644
index 0000000..6a55817
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/KStore.java
@@ -0,0 +1,7 @@
+package dev.latvian.mods.kmath;
+
+import net.minecraft.world.World;
+
+public interface KStore {
+ World getWorld();
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/num/DummyNumber.java b/src/main/java/dev/latvian/mods/kmath/num/DummyNumber.java
new file mode 100644
index 0000000..bae4111
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/num/DummyNumber.java
@@ -0,0 +1,23 @@
+package dev.latvian.mods.kmath.num;
+
+public class DummyNumber extends Number {
+ @Override
+ public int intValue() {
+ return 0;
+ }
+
+ @Override
+ public long longValue() {
+ return 0;
+ }
+
+ @Override
+ public float floatValue() {
+ return 0;
+ }
+
+ @Override
+ public double doubleValue() {
+ return 0;
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/num/KColor.java b/src/main/java/dev/latvian/mods/kmath/num/KColor.java
new file mode 100644
index 0000000..2cc6a00
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/num/KColor.java
@@ -0,0 +1,88 @@
+package dev.latvian.mods.kmath.num;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import dev.latvian.mods.kmath.KStore;
+import dev.latvian.mods.kmath.util.DeltaTicking;
+import org.joml.Vector4f;
+
+public class KColor implements DeltaTicking {
+ public final KNumberHolder red, green, blue, alpha;
+
+ public KColor(KStore parent) {
+ this.red = new KNumberHolder(parent, KNumber.ONE);
+ this.green = new KNumberHolder(parent, KNumber.ONE);
+ this.blue = new KNumberHolder(parent, KNumber.ONE);
+ this.alpha = new KNumberHolder(parent, KNumber.ONE);
+ }
+
+ @Override
+ public void snap() {
+ red.snap();
+ green.snap();
+ blue.snap();
+ alpha.snap();
+ }
+
+ @Override
+ public void tickValue() {
+ red.tickValue();
+ green.tickValue();
+ blue.tickValue();
+ alpha.tickValue();
+ }
+
+ public void set(Vector4f color, float delta, float colorMod) {
+ color.x = (float) (red.get(delta) * colorMod);
+ color.y = (float) (green.get(delta) * colorMod);
+ color.z = (float) (blue.get(delta) * colorMod);
+ color.w = (float) alpha.get(delta);
+ }
+
+ public void update(JsonElement json) {
+ if (json instanceof JsonArray a) {
+ if (a.size() == 1) {
+ red.update(a.get(0));
+ green.source = red.source;
+ blue.source = red.source;
+ alpha.source = KNumber.ONE;
+ } else if (a.size() == 2) {
+ red.update(a.get(0));
+ green.source = red.source;
+ blue.source = red.source;
+ alpha.update(a.get(1));
+ } else if (a.size() == 3) {
+ red.update(a.get(0));
+ green.update(a.get(1));
+ blue.update(a.get(2));
+ alpha.source = KNumber.ONE;
+ } else if (a.size() == 4) {
+ red.update(a.get(0));
+ green.update(a.get(1));
+ blue.update(a.get(2));
+ alpha.update(a.get(3));
+ }
+ } else if (json instanceof JsonPrimitive p) {
+ if (p.isString()) {
+ var hex = p.getAsString();
+
+ if (hex.length() == 7 && hex.charAt(0) == '#') {
+ red.source = Integer.parseInt(hex.substring(1, 3), 16) / 255D;
+ green.source = Integer.parseInt(hex.substring(3, 5), 16) / 255D;
+ blue.source = Integer.parseInt(hex.substring(5, 7), 16) / 255D;
+ alpha.source = KNumber.ONE;
+ } else if (hex.length() == 9 && hex.charAt(0) == '#') {
+ alpha.source = Integer.parseInt(hex.substring(1, 3), 16) / 255D;
+ red.source = Integer.parseInt(hex.substring(3, 5), 16) / 255D;
+ green.source = Integer.parseInt(hex.substring(5, 7), 16) / 255D;
+ blue.source = Integer.parseInt(hex.substring(7, 9), 16) / 255D;
+ }
+ } else {
+ var num = p.getAsDouble();
+ red.source = green.source = blue.source = num;
+ alpha.source = KNumber.ONE;
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/num/KLazyNumber.java b/src/main/java/dev/latvian/mods/kmath/num/KLazyNumber.java
new file mode 100644
index 0000000..2070fa8
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/num/KLazyNumber.java
@@ -0,0 +1,19 @@
+package dev.latvian.mods.kmath.num;
+
+public class KLazyNumber extends KNumber {
+ private final String value;
+ private Number number;
+
+ public KLazyNumber(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public double doubleValue() {
+ if (number == null) {
+ number = KNumber.parse(value);
+ }
+
+ return number.doubleValue();
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/num/KNumber.java b/src/main/java/dev/latvian/mods/kmath/num/KNumber.java
new file mode 100644
index 0000000..cd7d76f
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/num/KNumber.java
@@ -0,0 +1,57 @@
+package dev.latvian.mods.kmath.num;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import dev.latvian.mods.kmath.KStore;
+
+public abstract class KNumber extends Number {
+ public static final Number ZERO = 0D;
+ public static final Number ONE = 1D;
+ public static final Number ZEROF = 0F;
+ public static final Number ONEF = 1F;
+ public static final Number NaN = Double.NaN;
+ public static final Number PInf = Double.POSITIVE_INFINITY;
+ public static final Number NInf = Double.NEGATIVE_INFINITY;
+
+ public static Number fromJson(KStore parent, JsonElement json) {
+ if (json instanceof JsonPrimitive p) {
+ if (p.isNumber()) {
+ return p.getAsNumber();
+ } else if (p.isString()) {
+ var str = p.getAsString();
+
+ return switch (str) {
+ case "0", "false" -> ZERO;
+ case "1", "true" -> ONE;
+ case "nan", "NaN" -> NaN;
+ case "+inf", "+Inf" -> PInf;
+ case "-inf", "-Inf" -> NInf;
+ default -> new KLazyNumber(str.trim());
+ };
+ } else if (p.isBoolean()) {
+ return p.getAsBoolean() ? ONE : ZERO;
+ }
+ }
+
+ throw new IllegalArgumentException("Invalid number JSON: " + json);
+ }
+
+ protected static Number parse(String value) {
+ return Double.parseDouble(value);
+ }
+
+ @Override
+ public int intValue() {
+ return (int) doubleValue();
+ }
+
+ @Override
+ public long longValue() {
+ return (long) doubleValue();
+ }
+
+ @Override
+ public float floatValue() {
+ return (float) doubleValue();
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/num/KNumberHolder.java b/src/main/java/dev/latvian/mods/kmath/num/KNumberHolder.java
new file mode 100644
index 0000000..3c2e889
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/num/KNumberHolder.java
@@ -0,0 +1,71 @@
+package dev.latvian.mods.kmath.num;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import dev.latvian.mods.kmath.KStore;
+import dev.latvian.mods.kmath.util.DeltaTicking;
+import net.minecraft.util.math.MathHelper;
+
+public class KNumberHolder implements DeltaTicking {
+ public static Number fromJson(KStore parent, JsonElement json) {
+ if (json instanceof JsonPrimitive p) {
+ if (p.isNumber()) {
+ return p.getAsNumber();
+ } else if (p.isString()) {
+ var str = p.getAsString();
+
+ return switch (str) {
+ case "0", "false" -> KNumber.ZERO;
+ case "1", "true" -> KNumber.ONE;
+ case "nan", "NaN" -> KNumber.NaN;
+ case "+inf", "+Inf" -> KNumber.PInf;
+ case "-inf", "-Inf" -> KNumber.NInf;
+ default -> new KLazyNumber(str.trim());
+ };
+ } else if (p.isBoolean()) {
+ return p.getAsBoolean() ? KNumber.ONE : KNumber.ZERO;
+ }
+ }
+
+ throw new IllegalArgumentException("Invalid number JSON: " + json);
+ }
+
+ public final KStore parent;
+ public Number source;
+ public double value;
+ public double prevValue;
+
+ public KNumberHolder(KStore parent, Number source) {
+ this.parent = parent;
+ this.source = source;
+ this.value = this.prevValue = source.doubleValue();
+ }
+
+ @Override
+ public void snap() {
+ prevValue = value;
+ }
+
+ @Override
+ public void tickValue() {
+ value = source.doubleValue();
+ }
+
+ public double get(double delta) {
+ return MathHelper.lerp(delta, prevValue, value);
+ }
+
+ public void update(JsonElement json) {
+ if (json != null) {
+ source = fromJson(parent, json);
+ }
+ }
+
+ public boolean getBoolean() {
+ return value != 0;
+ }
+
+ public boolean changed() {
+ return value != prevValue;
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/pos/KApproachPos.java b/src/main/java/dev/latvian/mods/kmath/pos/KApproachPos.java
new file mode 100644
index 0000000..b035328
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/pos/KApproachPos.java
@@ -0,0 +1,47 @@
+package dev.latvian.mods.kmath.pos;
+
+import dev.latvian.mods.kmath.num.KNumber;
+import org.joml.Vector3d;
+
+public class KApproachPos implements KPos {
+ public final KPos kpos;
+ public final Vector3d approach = new Vector3d(0D, 0D, 0D);
+ public final Vector3d temp = new Vector3d(0D, 0D, 0D);
+ public Number speed = 0.2D;
+ public Number minDistance = KNumber.ZERO;
+ public Number avoid = KNumber.ZERO;
+ public Number distExp = 0.25D;
+
+ public KApproachPos(KPos kpos) {
+ this.kpos = kpos;
+ }
+
+ @Override
+ public void tick(Vector3d pos) {
+ kpos.tick(approach);
+
+ var _speed = speed.doubleValue();
+
+ if (_speed <= 0D) {
+ pos.set(approach);
+ return;
+ }
+
+ var _minDistance = minDistance.doubleValue();
+
+ var dist = approach.distance(pos.x, pos.y, pos.z);
+
+ if (dist > _minDistance || avoid.doubleValue() != 0D) {
+ temp.set(approach);
+ temp.sub(pos);
+
+ if (_minDistance > 0D) {
+ temp.mul((dist - _minDistance) / dist);
+ dist -= _minDistance;
+ }
+
+ double s = Math.min(_speed, dist) / Math.pow(dist, distExp.doubleValue());
+ pos.add(temp.x * s, temp.y * s, temp.z * s);
+ }
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/pos/KArithmeticPos.java b/src/main/java/dev/latvian/mods/kmath/pos/KArithmeticPos.java
new file mode 100644
index 0000000..976617c
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/pos/KArithmeticPos.java
@@ -0,0 +1,68 @@
+package dev.latvian.mods.kmath.pos;
+
+import org.jetbrains.annotations.Nullable;
+import org.joml.Vector3d;
+
+public record KArithmeticPos(Operation op, KPos kposA, KPos kposB) implements KPos {
+ public enum Operation {
+ ADD,
+ SUB,
+ MUL,
+ DIV,
+ MOD,
+ POW,
+ MIN,
+ MAX;
+
+ @Nullable
+ public static Operation get(String op) {
+ return switch (op) {
+ case "add", "+" -> ADD;
+ case "sub", "-" -> SUB;
+ case "mul", "*" -> MUL;
+ case "div", "/" -> DIV;
+ case "mod", "%" -> MOD;
+ case "pow", "**" -> POW;
+ case "min" -> MIN;
+ case "max" -> MAX;
+ default -> null;
+ };
+ }
+ }
+
+ @Override
+ public void tick(Vector3d pos) {
+ kposB.tick(pos);
+ var x = pos.x;
+ var y = pos.y;
+ var z = pos.z;
+ kposA.tick(pos);
+
+ switch (op) {
+ case ADD -> pos.add(x, y, z);
+ case SUB -> pos.sub(x, y, z);
+ case MUL -> pos.mul(x, y, z);
+ case DIV -> pos.div(x, y, z);
+ case MOD -> {
+ pos.x = x == 0D ? pos.x : (pos.x % x);
+ pos.y = y == 0D ? pos.y : (pos.y % y);
+ pos.z = z == 0D ? pos.z : (pos.z % z);
+ }
+ case POW -> {
+ pos.x = Math.pow(x, pos.x);
+ pos.y = Math.pow(y, pos.y);
+ pos.z = Math.pow(z, pos.z);
+ }
+ case MIN -> {
+ pos.x = Math.min(x, pos.x);
+ pos.y = Math.min(y, pos.y);
+ pos.z = Math.min(z, pos.z);
+ }
+ case MAX -> {
+ pos.x = Math.max(x, pos.x);
+ pos.y = Math.max(y, pos.y);
+ pos.z = Math.max(z, pos.z);
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/pos/KEntityPos.java b/src/main/java/dev/latvian/mods/kmath/pos/KEntityPos.java
new file mode 100644
index 0000000..075051c
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/pos/KEntityPos.java
@@ -0,0 +1,97 @@
+package dev.latvian.mods.kmath.pos;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.mojang.brigadier.StringReader;
+import com.mojang.util.UUIDTypeAdapter;
+import dev.latvian.mods.kmath.KStore;
+import dev.latvian.mods.kmath.num.DummyNumber;
+import dev.latvian.mods.kmath.num.KNumber;
+import net.minecraft.command.EntitySelectorReader;
+import net.minecraft.entity.Entity;
+import net.minecraft.world.World;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Vector3d;
+
+import java.util.UUID;
+
+public class KEntityPos implements KPos {
+ public static final Number EYE = new DummyNumber();
+ public static final Number LEASH = new DummyNumber();
+
+ public static KEntityPos parseEntity(KStore parent, JsonPrimitive entity) {
+ if (entity.isNumber()) {
+ return new KEntityPos(parent, entity.getAsInt());
+ } else if (entity.isString()) {
+ var s = entity.getAsString();
+
+ if (s.startsWith("@")) {
+ try {
+ return new KEntitySelectorPos(parent, new EntitySelectorReader(new StringReader(s), true).read());
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Invalid entity selector: " + s);
+ }
+ } else if (s.matches("\\w{8}\\w{4}\\w{4}\\w{4}\\w{12}")) {
+ return new KEntityUUIDPos(parent, UUIDTypeAdapter.fromString(s));
+ } else {
+ return new KEntityUUIDPos(parent, UUID.fromString(s));
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid entity pos: " + entity);
+ }
+ }
+
+ public final KStore parent;
+ public final int entityId;
+ public Number offset;
+
+ public Entity entity;
+
+ public KEntityPos(KStore parent, int entityId) {
+ this.parent = parent;
+ this.entityId = entityId;
+ this.offset = KNumber.ZERO;
+ }
+
+ public void update(JsonObject json) {
+ var off = json.get("offset");
+
+ offsetBreak:
+ if (off != null) {
+ if (off instanceof JsonPrimitive p && p.isString()) {
+ if (p.getAsString().equals("eye")) {
+ offset = EYE;
+ break offsetBreak;
+ } else if (p.getAsString().equals("leash")) {
+ offset = LEASH;
+ break offsetBreak;
+ }
+ }
+
+ offset = KNumber.fromJson(parent, off);
+ }
+ }
+
+ @Nullable
+ public Entity getEntity(World world) {
+ return world.getEntityById(entityId);
+ }
+
+ @Override
+ public void tick(Vector3d pos) {
+ if (entity == null || entity.isRemoved()) {
+ entity = getEntity(parent.getWorld());
+ }
+
+ if (entity != null) {
+ if (offset == EYE) {
+ pos.set(entity.getX(), entity.getEyeY(), entity.getZ());
+ } else if (offset == LEASH) {
+ var p = entity.getLeashPos(1F);
+ pos.set(p.x, p.y, p.z);
+ } else {
+ pos.set(entity.getX(), entity.getY() + entity.getHeight() * offset.doubleValue(), entity.getZ());
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/pos/KEntitySelectorPos.java b/src/main/java/dev/latvian/mods/kmath/pos/KEntitySelectorPos.java
new file mode 100644
index 0000000..472530e
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/pos/KEntitySelectorPos.java
@@ -0,0 +1,28 @@
+package dev.latvian.mods.kmath.pos;
+
+import dev.latvian.mods.kmath.KStore;
+import net.minecraft.command.EntitySelector;
+import net.minecraft.entity.Entity;
+import net.minecraft.world.World;
+import org.jetbrains.annotations.Nullable;
+
+public class KEntitySelectorPos extends KEntityPos {
+ public final EntitySelector entitySelector;
+
+ public KEntitySelectorPos(KStore parent, EntitySelector entitySelector) {
+ super(parent, 0);
+ this.entitySelector = entitySelector;
+ }
+
+ @Override
+ @Nullable
+ public Entity getEntity(World world) {
+ for (var e : world.getEntityLookup().iterate()) {
+ if (entitySelector.basePredicate.test(e)) {
+ return e;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/pos/KEntityUUIDPos.java b/src/main/java/dev/latvian/mods/kmath/pos/KEntityUUIDPos.java
new file mode 100644
index 0000000..6ac692c
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/pos/KEntityUUIDPos.java
@@ -0,0 +1,23 @@
+package dev.latvian.mods.kmath.pos;
+
+import dev.latvian.mods.kmath.KStore;
+import net.minecraft.entity.Entity;
+import net.minecraft.world.World;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.UUID;
+
+public class KEntityUUIDPos extends KEntityPos {
+ public final UUID uuid;
+
+ public KEntityUUIDPos(KStore parent, UUID uuid) {
+ super(parent, 0);
+ this.uuid = uuid;
+ }
+
+ @Override
+ @Nullable
+ public Entity getEntity(World world) {
+ return world.getEntityLookup().get(uuid);
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/pos/KFixedPos.java b/src/main/java/dev/latvian/mods/kmath/pos/KFixedPos.java
new file mode 100644
index 0000000..ff57879
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/pos/KFixedPos.java
@@ -0,0 +1,13 @@
+package dev.latvian.mods.kmath.pos;
+
+import org.joml.Vector3d;
+
+public record KFixedPos(Number x, Number y, Number z) implements KPos {
+ public static final KFixedPos ZERO = new KFixedPos(0D, 0D, 0D);
+ public static final KFixedPos ONE = new KFixedPos(1D, 1D, 1D);
+
+ @Override
+ public void tick(Vector3d pos) {
+ pos.set(x.doubleValue(), y.doubleValue(), z.doubleValue());
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/pos/KPos.java b/src/main/java/dev/latvian/mods/kmath/pos/KPos.java
new file mode 100644
index 0000000..5ff2ae8
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/pos/KPos.java
@@ -0,0 +1,79 @@
+package dev.latvian.mods.kmath.pos;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import dev.latvian.mods.kmath.KStore;
+import dev.latvian.mods.kmath.num.KNumber;
+import org.joml.Vector3d;
+
+public interface KPos {
+ static KPos fromJson(KStore parent, JsonElement json, Vector3d previous) {
+ if (json instanceof JsonArray arr && (arr.size() == 3 || arr.size() == 4)) {
+ if (arr.get(1) instanceof JsonPrimitive p && p.isString()) {
+ var op = KArithmeticPos.Operation.get(p.getAsString());
+
+ if (op != null) {
+ return new KArithmeticPos(op, fromJson(parent, arr.get(0), previous), fromJson(parent, arr.get(2), previous));
+ }
+ }
+
+ var x = arr.get(0).getAsDouble();
+ var y = arr.get(1).getAsDouble();
+ var z = arr.get(2).getAsDouble();
+
+ if (arr.size() == 3) {
+ return new KFixedPos(x, y, z);
+ }
+
+ return switch (arr.get(3).getAsString()) {
+ case "+" -> new KFixedPos(previous.x + x, previous.y + y, previous.z + z);
+ case "-" -> new KFixedPos(previous.x - x, previous.y - y, previous.z - z);
+ case "*" -> new KFixedPos(previous.x * x, previous.y * y, previous.z * z);
+ case "/" -> new KFixedPos(previous.x / x, previous.y / y, previous.z / z);
+ case "%" -> new KFixedPos(x == 0D ? previous.x : (previous.x % x), previous.y % y, z == 0D ? previous.z : (previous.z % z));
+ case "**" -> new KFixedPos(Math.pow(previous.x, x), Math.pow(previous.y, y), Math.pow(previous.z, z));
+ default -> new KFixedPos(x, y, z);
+ };
+ } else if (json instanceof JsonObject o) {
+ if (o.get("entity") instanceof JsonPrimitive entity) {
+ var ep = KEntityPos.parseEntity(parent, entity);
+ ep.update(o);
+ return ep;
+ } else if (o.has("approach")) {
+ var ap = new KApproachPos(fromJson(parent, o.get("approach"), previous));
+
+ if (o.has("speed")) {
+ ap.speed = KNumber.fromJson(parent, o.get("speed"));
+ }
+
+ if (o.has("minDistance")) {
+ ap.minDistance = KNumber.fromJson(parent, o.get("minDistance"));
+ }
+
+ if (o.has("avoid")) {
+ ap.avoid = KNumber.fromJson(parent, o.get("avoid"));
+ }
+
+ if (o.has("distExp")) {
+ ap.distExp = KNumber.fromJson(parent, o.get("distExp"));
+ }
+
+ return ap;
+ } else {
+ var x = o.has("x") ? KNumber.fromJson(parent, o.get("x")) : previous.x;
+ var y = o.has("y") ? KNumber.fromJson(parent, o.get("y")) : previous.y;
+ var z = o.has("z") ? KNumber.fromJson(parent, o.get("z")) : previous.z;
+ return new KFixedPos(x, y, z);
+ }
+ } else if (json instanceof JsonPrimitive) {
+ var num = KNumber.fromJson(parent, json);
+ return new KFixedPos(num, num, num);
+ }
+
+ return new KFixedPos(previous.x, previous.y, previous.z);
+ }
+
+ void tick(Vector3d pos);
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/pos/KPosHolder.java b/src/main/java/dev/latvian/mods/kmath/pos/KPosHolder.java
new file mode 100644
index 0000000..3f98187
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/pos/KPosHolder.java
@@ -0,0 +1,54 @@
+package dev.latvian.mods.kmath.pos;
+
+import com.google.gson.JsonElement;
+import dev.latvian.mods.kmath.KStore;
+import dev.latvian.mods.kmath.util.DeltaTicking;
+import net.minecraft.util.math.MathHelper;
+import org.joml.Vector3d;
+
+public class KPosHolder implements DeltaTicking {
+ public final KStore parent;
+ public KPos source;
+ public final Vector3d pos;
+ public final Vector3d prevPos;
+
+ public KPosHolder(KStore parent, KPos source) {
+ this.parent = parent;
+ this.source = source;
+ this.pos = new Vector3d(0D, 0D, 0D);
+ this.source.tick(pos);
+ this.prevPos = new Vector3d(pos);
+ }
+
+ @Override
+ public void snap() {
+ prevPos.set(pos);
+ }
+
+ @Override
+ public void tickValue() {
+ source.tick(pos);
+ }
+
+ public double getX(float delta) {
+ return MathHelper.lerp(delta, prevPos.x, pos.x);
+ }
+
+ public double getY(float delta) {
+ return MathHelper.lerp(delta, prevPos.y, pos.y);
+ }
+
+ public double getZ(float delta) {
+ return MathHelper.lerp(delta, prevPos.z, pos.z);
+ }
+
+ public void update(JsonElement json) {
+ if (json != null) {
+ source = KPos.fromJson(parent, json, pos);
+ }
+ }
+
+ public boolean changed() {
+ return pos.x != prevPos.x || pos.y != prevPos.y || pos.z != prevPos.z;
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/random/NullRandom.java b/src/main/java/dev/latvian/mods/kmath/random/NullRandom.java
new file mode 100644
index 0000000..c277311
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/random/NullRandom.java
@@ -0,0 +1,72 @@
+package dev.latvian.mods.kmath.random;
+
+import net.minecraft.util.math.random.Random;
+import net.minecraft.util.math.random.RandomSplitter;
+
+public class NullRandom implements Random, RandomSplitter {
+
+ public static final NullRandom INSTANCE = new NullRandom();
+
+ @Override
+ public Random split() {
+ return this;
+ }
+
+ @Override
+ public RandomSplitter nextSplitter() {
+ return this;
+ }
+
+ @Override
+ public void setSeed(long seed) {
+ }
+
+ @Override
+ public int nextInt() {
+ return 0;
+ }
+
+ @Override
+ public int nextInt(int bound) {
+ return 0;
+ }
+
+ @Override
+ public long nextLong() {
+ return 0L;
+ }
+
+ @Override
+ public boolean nextBoolean() {
+ return false;
+ }
+
+ @Override
+ public float nextFloat() {
+ return 0F;
+ }
+
+ @Override
+ public double nextDouble() {
+ return 0D;
+ }
+
+ @Override
+ public double nextGaussian() {
+ return 0D;
+ }
+
+ @Override
+ public Random split(String seed) {
+ return this;
+ }
+
+ @Override
+ public Random split(int x, int y, int z) {
+ return this;
+ }
+
+ @Override
+ public void addDebugInfo(StringBuilder info) {
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/random/RandomUtils.java b/src/main/java/dev/latvian/mods/kmath/random/RandomUtils.java
new file mode 100644
index 0000000..bf7115c
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/random/RandomUtils.java
@@ -0,0 +1,37 @@
+package dev.latvian.mods.kmath.random;
+
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.world.World;
+
+import java.util.Random;
+
+public class RandomUtils {
+
+ /**
+ * Returns a value between {@code -range} and {@code range}.
+ *
+ * @param random random instance
+ * @param range maximum value range
+ * @return a value between {@code -range} and {@code range}.
+ */
+ public static double range(Random random, double range) {
+ return (random.nextDouble() - 0.5) * range * 2;
+ }
+
+ public static double range(net.minecraft.util.math.random.Random random, double range) {
+ return (random.nextDouble() - 0.5) * range * 2;
+ }
+
+ public static double outer(World world, double min, double max) {
+ if (world.random.nextDouble() <= 0.5) {
+ return min + world.random.nextDouble() * max;
+ } else {
+ return -(min + world.random.nextDouble() * max);
+ }
+ }
+
+ public static Vec3d outerVector(World world, double min, double max) {
+ double select = min + world.random.nextDouble() * (max - min);
+ return new Vec3d(world.random.nextDouble() - 0.5, 0, world.random.nextDouble() - 0.5).normalize().multiply(select, 0, select);
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/render/BoxRenderConsumer.java b/src/main/java/dev/latvian/mods/kmath/render/BoxRenderConsumer.java
new file mode 100644
index 0000000..a7766d3
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/render/BoxRenderConsumer.java
@@ -0,0 +1,12 @@
+package dev.latvian.mods.kmath.render;
+
+import net.minecraft.client.render.VertexConsumer;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.util.shape.VoxelShapes;
+
+public record BoxRenderConsumer(MatrixStack matrices, VertexConsumer lines, float red, float green, float blue, float alpha) implements VoxelShapes.BoxConsumer {
+ @Override
+ public void consume(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
+ LineBoxRenderer.lineBox(matrices, lines, minX, minY, minZ, maxX, maxY, maxZ, red, green, blue, alpha);
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/render/LaserRenderer.java b/src/main/java/dev/latvian/mods/kmath/render/LaserRenderer.java
new file mode 100644
index 0000000..a2c23c8
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/render/LaserRenderer.java
@@ -0,0 +1,95 @@
+package dev.latvian.mods.kmath.render;
+
+import dev.latvian.mods.kmath.tex.KLight;
+import dev.latvian.mods.kmath.tex.KTexture;
+import dev.latvian.mods.kmath.util.Rotations;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.render.VertexConsumer;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.util.math.RotationAxis;
+import org.joml.Matrix3f;
+import org.joml.Matrix4f;
+import org.joml.Quaternionf;
+import org.joml.Vector3d;
+import org.joml.Vector4f;
+
+public class LaserRenderer {
+ public final Vector3d targetPos = new Vector3d(0D, 0D, 0D);
+ private float length = 0F;
+ private Quaternionf rotationY = RotationAxis.POSITIVE_Y.rotation(0F);
+ private Quaternionf rotationX = RotationAxis.POSITIVE_X.rotation(0F);
+
+ public Vector4f color = new Vector4f(0.5F, 0.75F, 1F, 1F);
+ public float startRadius = 0.5F;
+ public float endRadius = 0.5F;
+ public float offset = 0F;
+ public KTexture texture = KTexture.WHITE;
+ public KLight light = KLight.FULLBRIGHT;
+
+ public void updateTarget() {
+ var tx = targetPos.x;
+ var ty = targetPos.y;
+ var tz = targetPos.z;
+ length = (float) Math.sqrt(tx * tx + ty * ty + tz * tz);
+
+ if (length - offset <= 0F) {
+ return;
+ }
+
+ rotationY = RotationAxis.POSITIVE_Y.rotationDegrees(-Rotations.getYaw(tx / length, tz / length));
+ rotationX = RotationAxis.POSITIVE_X.rotationDegrees(90 + Rotations.getPitch(ty / length));
+ }
+
+ @Environment(EnvType.CLIENT)
+ public void render(MatrixStack matrices, VertexConsumer buffer) {
+ float sr = startRadius;
+ float er = endRadius;
+
+ if (sr <= 0F && er <= 0F) {
+ return;
+ }
+
+ if (length - offset <= 0F) {
+ return;
+ }
+
+ matrices.push();
+ matrices.multiply(rotationY);
+ matrices.multiply(rotationX);
+
+ var entry = matrices.peek();
+ var m = entry.getPositionMatrix();
+ var n = entry.getNormalMatrix();
+ var uvs = texture.getUVs();
+ var u0 = uvs[0].floatValue();
+ var v0 = uvs[1].floatValue();
+ var u1 = uvs[2].floatValue();
+ var v1 = uvs[3].floatValue();
+
+ renderBeamFace(buffer, m, n, sr, er, -1F, -1F, 1F, -1F, 0F, -1F, u0, v0, u1, v1); // north
+ renderBeamFace(buffer, m, n, sr, er, -1F, -1F, -1F, 1F, -1F, 0F, u0, v0, u1, v1); // west
+ renderBeamFace(buffer, m, n, sr, er, 1F, -1F, 1F, 1F, 1F, 0F, u0, v0, u1, v1); // east
+ renderBeamFace(buffer, m, n, sr, er, -1F, 1F, 1F, 1F, 0F, 1F, u0, v0, u1, v1); // south
+
+ matrices.pop();
+ }
+
+ @Environment(EnvType.CLIENT)
+ private void renderBeamFace(VertexConsumer buffer, Matrix4f m, Matrix3f n, float sr, float er, float x0, float z0, float x1, float z1, float nx, float nz, float u0, float v0, float u1, float v1) {
+ float r = color.x;
+ float g = color.y;
+ float b = color.z;
+ float a = color.w;
+
+ int lu = light.lightU;
+ int lv = light.lightV;
+ int ou = light.overlayU;
+ int ov = light.overlayV;
+
+ buffer.vertex(m, x0 * er, length, z0 * er).color(r, g, b, a).texture(u1, v0).overlay(ou, ov).light(lu, lv).normal(n, nx, 0F, nz).next();
+ buffer.vertex(m, x0 * sr, offset, z0 * sr).color(r, g, b, a).texture(u1, v1).overlay(ou, ov).light(lu, lv).normal(n, nx, 0F, nz).next();
+ buffer.vertex(m, x1 * sr, offset, z1 * sr).color(r, g, b, a).texture(u0, v1).overlay(ou, ov).light(lu, lv).normal(n, nx, 0F, nz).next();
+ buffer.vertex(m, x1 * er, length, z1 * er).color(r, g, b, a).texture(u0, v0).overlay(ou, ov).light(lu, lv).normal(n, nx, 0F, nz).next();
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/render/LineBoxRenderer.java b/src/main/java/dev/latvian/mods/kmath/render/LineBoxRenderer.java
new file mode 100644
index 0000000..76af984
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/render/LineBoxRenderer.java
@@ -0,0 +1,43 @@
+package dev.latvian.mods.kmath.render;
+
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.render.VertexConsumer;
+import net.minecraft.client.util.math.MatrixStack;
+
+public class LineBoxRenderer {
+ @Environment(EnvType.CLIENT)
+ public static void lineBox(MatrixStack matrices, VertexConsumer lines, float x1, float y1, float z1, float x2, float y2, float z2, float red, float green, float blue, float alpha) {
+ var m = matrices.peek().getPositionMatrix();
+ var n = matrices.peek().getNormalMatrix();
+ lines.vertex(m, x1, y1, z1).color(red, green, blue, alpha).normal(n, 1F, 0F, 0F).next();
+ lines.vertex(m, x2, y1, z1).color(red, green, blue, alpha).normal(n, 1F, 0F, 0F).next();
+ lines.vertex(m, x1, y1, z1).color(red, green, blue, alpha).normal(n, 0F, 1F, 0F).next();
+ lines.vertex(m, x1, y2, z1).color(red, green, blue, alpha).normal(n, 0F, 1F, 0F).next();
+ lines.vertex(m, x1, y1, z1).color(red, green, blue, alpha).normal(n, 0F, 0F, 1F).next();
+ lines.vertex(m, x1, y1, z2).color(red, green, blue, alpha).normal(n, 0F, 0F, 1F).next();
+ lines.vertex(m, x2, y1, z1).color(red, green, blue, alpha).normal(n, 0F, 1F, 0F).next();
+ lines.vertex(m, x2, y2, z1).color(red, green, blue, alpha).normal(n, 0F, 1F, 0F).next();
+ lines.vertex(m, x2, y2, z1).color(red, green, blue, alpha).normal(n, -1F, 0F, 0F).next();
+ lines.vertex(m, x1, y2, z1).color(red, green, blue, alpha).normal(n, -1F, 0F, 0F).next();
+ lines.vertex(m, x1, y2, z1).color(red, green, blue, alpha).normal(n, 0F, 0F, 1F).next();
+ lines.vertex(m, x1, y2, z2).color(red, green, blue, alpha).normal(n, 0F, 0F, 1F).next();
+ lines.vertex(m, x1, y2, z2).color(red, green, blue, alpha).normal(n, 0F, -1F, 0F).next();
+ lines.vertex(m, x1, y1, z2).color(red, green, blue, alpha).normal(n, 0F, -1F, 0F).next();
+ lines.vertex(m, x1, y1, z2).color(red, green, blue, alpha).normal(n, 1F, 0F, 0F).next();
+ lines.vertex(m, x2, y1, z2).color(red, green, blue, alpha).normal(n, 1F, 0F, 0F).next();
+ lines.vertex(m, x2, y1, z2).color(red, green, blue, alpha).normal(n, 0F, 0F, -1F).next();
+ lines.vertex(m, x2, y1, z1).color(red, green, blue, alpha).normal(n, 0F, 0F, -1F).next();
+ lines.vertex(m, x1, y2, z2).color(red, green, blue, alpha).normal(n, 1F, 0F, 0F).next();
+ lines.vertex(m, x2, y2, z2).color(red, green, blue, alpha).normal(n, 1F, 0F, 0F).next();
+ lines.vertex(m, x2, y1, z2).color(red, green, blue, alpha).normal(n, 0F, 1F, 0F).next();
+ lines.vertex(m, x2, y2, z2).color(red, green, blue, alpha).normal(n, 0F, 1F, 0F).next();
+ lines.vertex(m, x2, y2, z1).color(red, green, blue, alpha).normal(n, 0F, 0F, 1F).next();
+ lines.vertex(m, x2, y2, z2).color(red, green, blue, alpha).normal(n, 0F, 0F, 1F).next();
+ }
+
+ @Environment(EnvType.CLIENT)
+ public static void lineBox(MatrixStack matrices, VertexConsumer lines, double x1, double y1, double z1, double x2, double y2, double z2, float red, float green, float blue, float alpha) {
+ lineBox(matrices, lines, (float) x1, (float) y1, (float) z1, (float) x2, (float) y2, (float) z2, red, green, blue, alpha);
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/render/SphereRenderer.java b/src/main/java/dev/latvian/mods/kmath/render/SphereRenderer.java
new file mode 100644
index 0000000..df89475
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/render/SphereRenderer.java
@@ -0,0 +1,66 @@
+package dev.latvian.mods.kmath.render;
+
+import dev.latvian.mods.kmath.KMath;
+import dev.latvian.mods.kmath.tex.KLight;
+import dev.latvian.mods.kmath.tex.KTexture;
+import dev.latvian.mods.kmath.util.SpherePoints;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.render.VertexConsumer;
+import net.minecraft.client.util.math.MatrixStack;
+import org.joml.Vector4f;
+
+public class SphereRenderer {
+ public final SpherePoints points;
+ public Vector4f color = new Vector4f(1F, 1F, 1F, 1F);
+ public KTexture texture = KTexture.WHITE;
+ public KLight light = KLight.NORMAL;
+
+ public SphereRenderer(SpherePoints points) {
+ this.points = points;
+ }
+
+ // POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL
+ @Environment(EnvType.CLIENT)
+ public void renderEntity(MatrixStack ms, VertexConsumer layer) {
+ var e = ms.peek();
+ var m = e.getPositionMatrix();
+ var n = e.getNormalMatrix();
+
+ var uvs = texture.getUVs();
+ var u0 = uvs[0].floatValue();
+ var v0 = uvs[1].floatValue();
+ var u1 = uvs[2].floatValue();
+ var v1 = uvs[3].floatValue();
+
+ int ou = light.overlayU;
+ int ov = light.overlayV;
+ int lu = light.lightU;
+ int lv = light.lightV;
+
+ float colR = color.x;
+ float colG = color.y;
+ float colB = color.z;
+ float colA = color.w;
+
+ for (int r = 0; r < points.rows.length - 1; r++) {
+ for (int c = 0; c < points.cols.length - 1; c++) {
+ var cr = points.rows[r];
+ var nr = points.rows[r + 1];
+ var cc = points.cols[c];
+ var nc = points.cols[c + 1];
+ var nv = points.normals[c][r];
+
+ var u0l = KMath.lerp(cc.u(), u0, u1);
+ var v0l = KMath.lerp(cr.v(), v0, v1);
+ var u1l = KMath.lerp(nc.u(), u0, u1);
+ var v1l = KMath.lerp(nr.v(), v0, v1);
+
+ layer.vertex(m, cc.x() * nr.m(), nr.y(), cc.z() * nr.m()).color(colR, colG, colB, colA).texture(u0l, v1l).overlay(ou, ov).light(lu, lv).normal(n, nv.x, nv.y, nv.z).next();
+ layer.vertex(m, cc.x() * cr.m(), cr.y(), cc.z() * cr.m()).color(colR, colG, colB, colA).texture(u0l, v0l).overlay(ou, ov).light(lu, lv).normal(n, nv.x, nv.y, nv.z).next();
+ layer.vertex(m, nc.x() * cr.m(), cr.y(), nc.z() * cr.m()).color(colR, colG, colB, colA).texture(u1l, v0l).overlay(ou, ov).light(lu, lv).normal(n, nv.x, nv.y, nv.z).next();
+ layer.vertex(m, nc.x() * nr.m(), nr.y(), nc.z() * nr.m()).color(colR, colG, colB, colA).texture(u1l, v1l).overlay(ou, ov).light(lu, lv).normal(n, nv.x, nv.y, nv.z).next();
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/tex/KAtlasSpriteTexture.java b/src/main/java/dev/latvian/mods/kmath/tex/KAtlasSpriteTexture.java
new file mode 100644
index 0000000..a09e796
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/tex/KAtlasSpriteTexture.java
@@ -0,0 +1,24 @@
+package dev.latvian.mods.kmath.tex;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.util.Identifier;
+
+public record KAtlasSpriteTexture(Identifier atlas, Identifier sprite, Number[] uvs) implements KTexture {
+ public static final Identifier BLOCKS = new Identifier("minecraft", "textures/atlas/blocks.png");
+ public static final Identifier PARTICLES = new Identifier("minecraft", "textures/atlas/particles.png");
+
+ @Override
+ public Identifier getPath() {
+ return atlas;
+ }
+
+ @Override
+ public Number[] getUVs() {
+ var s = MinecraftClient.getInstance().getSpriteAtlas(atlas).apply(sprite);
+ uvs[0] = s.getMinU();
+ uvs[1] = s.getMinV();
+ uvs[2] = s.getMaxU();
+ uvs[3] = s.getMaxV();
+ return uvs;
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/tex/KLight.java b/src/main/java/dev/latvian/mods/kmath/tex/KLight.java
new file mode 100644
index 0000000..65cf735
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/tex/KLight.java
@@ -0,0 +1,28 @@
+package dev.latvian.mods.kmath.tex;
+
+public class KLight {
+ public static final KLight NORMAL = new KLight(0xF00000, 0xA0000);
+ public static final KLight FULLBRIGHT = new KLight(0xF000F0, 0xA0000);
+ public static final KLight NORMAL_HURT = new KLight(0xF00000, 0x30000);
+ public static final KLight FULLBRIGHT_HURT = new KLight(0xF000F0, 0x30000);
+
+ public static KLight get(boolean fullbright, boolean hurt) {
+ return fullbright ? (hurt ? FULLBRIGHT_HURT : FULLBRIGHT) : (hurt ? NORMAL_HURT : NORMAL);
+ }
+
+ public final int light;
+ public final int overlay;
+ public final int lightU;
+ public final int lightV;
+ public final int overlayU;
+ public final int overlayV;
+
+ public KLight(int light, int overlay) {
+ this.light = light;
+ this.overlay = overlay;
+ this.lightU = light & '\uffff';
+ this.lightV = light >> 16 & '\uffff';
+ this.overlayU = overlay & '\uffff';
+ this.overlayV = overlay >> 16 & '\uffff';
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/tex/KPathTexture.java b/src/main/java/dev/latvian/mods/kmath/tex/KPathTexture.java
new file mode 100644
index 0000000..e436433
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/tex/KPathTexture.java
@@ -0,0 +1,15 @@
+package dev.latvian.mods.kmath.tex;
+
+import net.minecraft.util.Identifier;
+
+public record KPathTexture(Identifier path, Number[] uvs) implements KTexture {
+ @Override
+ public Identifier getPath() {
+ return path;
+ }
+
+ @Override
+ public Number[] getUVs() {
+ return uvs;
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/tex/KTexture.java b/src/main/java/dev/latvian/mods/kmath/tex/KTexture.java
new file mode 100644
index 0000000..1bd878c
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/tex/KTexture.java
@@ -0,0 +1,56 @@
+package dev.latvian.mods.kmath.tex;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import dev.latvian.mods.kmath.KStore;
+import dev.latvian.mods.kmath.num.KNumber;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.util.Identifier;
+
+public interface KTexture {
+ Number[] DEFAULT_UVS = new Number[]{KNumber.ZEROF, KNumber.ZEROF, KNumber.ONEF, KNumber.ONEF};
+ KTexture WHITE = new KPathTexture(new Identifier("textures/misc/white.png"), DEFAULT_UVS);
+
+ static KTexture fromJson(KStore parent, JsonElement json) {
+ if (json.isJsonPrimitive()) {
+ var str = json.getAsString();
+ return str.equals("white") ? WHITE : new KPathTexture(new Identifier(str), DEFAULT_UVS);
+ } else if (json instanceof JsonObject o) {
+ if (o.has("sprite")) {
+ var atlasStr = o.has("atlas") ? o.get("atlas").getAsString() : "blocks";
+
+ return new KAtlasSpriteTexture(switch (atlasStr) {
+ case "blocks" -> KAtlasSpriteTexture.BLOCKS;
+ case "particles" -> KAtlasSpriteTexture.PARTICLES;
+ default -> new Identifier(atlasStr);
+ }, new Identifier(o.get("sprite").getAsString()), new Number[4]);
+ } else if (o.has("path")) {
+ var path = new Identifier(o.get("path").getAsString());
+ var uvs = DEFAULT_UVS;
+
+ if (o.has("uvs")) {
+ var uvsArray = o.get("uvs").getAsJsonArray();
+
+ if (uvsArray.size() == 4) {
+ uvs = new Number[4];
+
+ for (int i = 0; i < 4; i++) {
+ uvs[i] = KNumber.fromJson(parent, uvsArray.get(i));
+ }
+ }
+ }
+
+ return new KPathTexture(path, uvs);
+ }
+ }
+
+ return WHITE;
+ }
+
+ @Environment(EnvType.CLIENT)
+ Identifier getPath();
+
+ @Environment(EnvType.CLIENT)
+ Number[] getUVs();
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/DeltaTicking.java b/src/main/java/dev/latvian/mods/kmath/util/DeltaTicking.java
new file mode 100644
index 0000000..36d9662
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/DeltaTicking.java
@@ -0,0 +1,7 @@
+package dev.latvian.mods.kmath.util;
+
+public interface DeltaTicking {
+ void snap();
+
+ void tickValue();
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/Directions.java b/src/main/java/dev/latvian/mods/kmath/util/Directions.java
new file mode 100644
index 0000000..984c864
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/Directions.java
@@ -0,0 +1,9 @@
+package dev.latvian.mods.kmath.util;
+
+import net.minecraft.util.math.Direction;
+
+public interface Directions {
+ Direction[] ALL = Direction.values();
+ Direction[] HORIZONTAL = {Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
+ Direction[] VERTICAL = {Direction.DOWN, Direction.UP};
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/Easing.java b/src/main/java/dev/latvian/mods/kmath/util/Easing.java
new file mode 100644
index 0000000..a82ff83
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/Easing.java
@@ -0,0 +1,83 @@
+package dev.latvian.mods.kmath.util;
+
+import dev.latvian.mods.kmath.KMath;
+import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction;
+import net.minecraft.util.math.Vec3d;
+
+@FunctionalInterface
+public interface Easing extends Double2DoubleFunction {
+ double ease(double x);
+
+ default double easeClamped(double x) {
+ return ease(KMath.clamp(x, 0D, 1D));
+ }
+
+ @Override
+ default double get(double x) {
+ return ease(x);
+ }
+
+ default double lerp(double t, double a, double b) {
+ return a + get(t) * (b - a);
+ }
+
+ default Vec3d lerp(double t, Vec3d a, Vec3d b) {
+ return new Vec3d(lerp(t, a.x, b.x), lerp(t, a.y, b.y), lerp(t, a.z, b.z));
+ }
+
+ Easing LINEAR = x -> x;
+ Easing SMOOTHSTEP = KMath::smoothstep;
+ Easing ISMOOTHSTEP = KMath::ismoothstep;
+ Easing SMOOTHERSTEP = KMath::smootherstep;
+
+ Easing SINE_IN = x -> 1 - Math.cos((x - Math.PI) / 2);
+ Easing SINE_OUT = x -> Math.sin((x * Math.PI) / 2);
+ Easing SINE_IN_OUT = x -> -(Math.cos(Math.PI * x) - 1) / 2;
+
+ Easing QUAD_IN = x -> x * x;
+ Easing QUAD_OUT = x -> 1 - (1 - x) * (1 - x);
+ Easing QUAD_IN_OUT = x -> x < 0.5 ? 2 * x * x : 1 - Math.pow(-2 * x + 2, 2) / 2;
+
+ Easing CUBIC_IN = x -> x * x * x;
+ Easing CUBIC_OUT = x -> 1 - Math.pow(1 - x, 3);
+ Easing CUBIC_IN_OUT = x -> x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2D;
+
+ Easing QUART_IN = x -> x * x * x * x;
+ Easing QUART_OUT = x -> 1 - Math.pow(1 - x, 4);
+ Easing QUART_IN_OUT = x -> x < 0.5 ? 8 * x * x * x * x : 1 - Math.pow(-2 * x + 2, 4) / 2D;
+
+ Easing QUINT_IN = x -> x * x * x * x * x;
+ Easing QUINT_OUT = x -> 1 - Math.pow(1 - x, 5);
+ Easing QUINT_IN_OUT = x -> x < 0.5 ? 16 * x * x * x * x * x : 1 - Math.pow(-2 * x + 2, 5) / 2D;
+
+ Easing EXPO_IN = x -> x == 0 ? 0 : Math.pow(2, 10 * x - 10);
+ Easing EXPO_OUT = x -> x == 1 ? 1 : 1 - Math.pow(2, -10 * x);
+ Easing EXPO_IN_OUT = x -> x == 0 ? 0 : x == 1 ? 1 : x < 0.5 ? Math.pow(2, 20 * x - 10) / 2 : (2 - Math.pow(2, -20 * x + 10)) / 2;
+
+ Easing CIRC_IN = x -> 1 - Math.sqrt(1 - x * x);
+ Easing CIRC_OUT = x -> Math.sqrt(1 - (x - 1) * (x - 1));
+ Easing CIRC_IN_OUT = x -> x < 0.5 ? (1 - Math.sqrt(1 - 4 * x * x)) / 2 : (Math.sqrt(1 - (-2 * x + 2) * (-2 * x + 2)) + 1) / 2;
+
+ Easing BACK_IN = x -> x * x * (2.70158 * x - 1.70158);
+ Easing BACK_OUT = x -> 1 - (1 - x) * (1 - x) * (2.70158 * (1 - x) - 1.70158);
+ Easing BACK_IN_OUT = x -> x < 0.5 ? Math.pow(2 * x, 2) * ((2.5949095 + 1) * 2 * x - 2.5949095) / 2 : (Math.pow(2 * x - 2, 2) * ((2.5949095 + 1) * (x * 2 - 2) + 2.5949095) + 2) / 2;
+
+ Easing ELASTIC_IN = x -> Math.sin(13 * Math.PI / 2 * x) * Math.pow(2, 10 * x - 10);
+ Easing ELASTIC_OUT = x -> Math.sin(-13 * Math.PI / 2 * (x + 1)) * Math.pow(2, -10 * x) + 1;
+ Easing ELASTIC_IN_OUT = x -> x < 0.5 ? Math.sin(13 * Math.PI / 2 * (2 * x)) * Math.pow(2, 10 * (2 * x) - 10) / 2 : Math.sin(-13 * Math.PI / 2 * (2 * x - 1)) * Math.pow(2, -10 * (2 * x - 1)) / 2 + 1;
+
+ Easing BOUNCE_OUT = x -> {
+ if (x < 1 / 2.75) {
+ return 7.5625 * x * x;
+ } else if (x < 2 / 2.75) {
+ return 7.5625 * (x -= 1.5 / 2.75) * x + 0.75;
+ } else if (x < 2.5 / 2.75) {
+ return 7.5625 * (x -= 2.25 / 2.75) * x + 0.9375;
+ } else {
+ return 7.5625 * (x -= 2.625 / 2.75) * x + 0.984375;
+ }
+ };
+
+ Easing BOUNCE_IN = x -> 1 - BOUNCE_OUT.ease(1 - x);
+ Easing BOUNCE_IN_OUT = x -> x < 0.5 ? BOUNCE_IN.ease(x * 2) / 2 : BOUNCE_OUT.ease(x * 2 - 1) / 2 + 0.5;
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/EasingGroup.java b/src/main/java/dev/latvian/mods/kmath/util/EasingGroup.java
new file mode 100644
index 0000000..a20d252
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/EasingGroup.java
@@ -0,0 +1,58 @@
+package dev.latvian.mods.kmath.util;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Source
+ */
+public enum EasingGroup {
+ LINEAR("linear", Easing.LINEAR),
+ SMOOTHSTEP("smoothstep", Easing.SMOOTHSTEP),
+ ISMOOTHSTEP("ismoothstep", Easing.ISMOOTHSTEP),
+ SMOOTHERSTEP("smootherstep", Easing.SMOOTHERSTEP),
+ SINE("sine", Easing.SINE_IN, Easing.SINE_OUT, Easing.SINE_IN_OUT),
+ QUAD("quad", Easing.QUAD_IN, Easing.QUAD_OUT, Easing.QUAD_IN_OUT),
+ CUBIC("cubic", Easing.CUBIC_IN, Easing.CUBIC_OUT, Easing.CUBIC_IN_OUT),
+ QUART("quart", Easing.QUART_IN, Easing.QUART_OUT, Easing.QUART_IN_OUT),
+ QUINT("quint", Easing.QUINT_IN, Easing.QUINT_OUT, Easing.QUINT_IN_OUT),
+ EXPO("expo", Easing.EXPO_IN, Easing.EXPO_OUT, Easing.EXPO_IN_OUT),
+ CIRC("circ", Easing.CIRC_IN, Easing.CIRC_OUT, Easing.CIRC_IN_OUT),
+ BACK("back", Easing.BACK_IN, Easing.BACK_OUT, Easing.BACK_IN_OUT),
+ ELASTIC("elastic", Easing.ELASTIC_IN, Easing.ELASTIC_OUT, Easing.ELASTIC_IN_OUT),
+ BOUNCE("bounce", Easing.BOUNCE_IN, Easing.BOUNCE_OUT, Easing.BOUNCE_IN_OUT);
+
+ public static final EasingGroup[] VALUES = values();
+ public static final Map GROUPS = new LinkedHashMap<>();
+ public static final Map FUNCTIONS = new LinkedHashMap<>();
+
+ static {
+ for (var group : VALUES) {
+ GROUPS.put(group.id, group);
+
+ if (group.in == group.out && group.in == group.inOut) {
+ FUNCTIONS.put(group.id, group.in);
+ } else {
+ FUNCTIONS.put(group.id + "_in", group.in);
+ FUNCTIONS.put(group.id + "_out", group.out);
+ FUNCTIONS.put(group.id + "_in_out", group.inOut);
+ }
+ }
+ }
+
+ public final String id;
+ public final Easing in;
+ public final Easing out;
+ public final Easing inOut;
+
+ EasingGroup(String id, Easing in, Easing out, Easing inOut) {
+ this.id = id;
+ this.in = in;
+ this.out = out;
+ this.inOut = inOut;
+ }
+
+ EasingGroup(String id, Easing common) {
+ this(id, common, common, common);
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/FloatPair.java b/src/main/java/dev/latvian/mods/kmath/util/FloatPair.java
new file mode 100644
index 0000000..c3f2bb4
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/FloatPair.java
@@ -0,0 +1,72 @@
+package dev.latvian.mods.kmath.util;
+
+import dev.latvian.mods.kmath.KMath;
+import net.minecraft.nbt.AbstractNbtNumber;
+import net.minecraft.nbt.NbtByteArray;
+import net.minecraft.nbt.NbtElement;
+import net.minecraft.nbt.NbtFloat;
+import net.minecraft.nbt.NbtIntArray;
+import net.minecraft.nbt.NbtList;
+
+public record FloatPair(float a, float b) {
+ public static final FloatPair ZERO = new FloatPair(0F, 0F);
+ public static final FloatPair ONE = new FloatPair(1F, 1F);
+
+ public static FloatPair of(float a, float b) {
+ return a == 0F && b == 0F ? ZERO : a == 1F && b == 1F ? ONE : Math.abs(a - b) < 0.0001F ? new FloatPair(a, a) : new FloatPair(a, b);
+ }
+
+ public static FloatPair of(float value) {
+ return of(value, value);
+ }
+
+ public static FloatPair of(NbtElement nbt) {
+ if (nbt instanceof NbtList list) {
+ return of(list.getFloat(0), list.getFloat(1));
+ } else if (nbt instanceof AbstractNbtNumber num) {
+ return of(num.floatValue());
+ } else if (nbt instanceof NbtIntArray arr) {
+ return of(arr.get(0).floatValue(), arr.get(1).floatValue());
+ } else if (nbt instanceof NbtByteArray arr) {
+ return of(arr.get(0).floatValue(), arr.get(1).floatValue());
+ } else {
+ return null;
+ }
+ }
+
+ public static NbtElement toNbt(float a, float b) {
+ if (a == b) {
+ return KMath.efficient(a);
+ }
+
+ var mn = KMath.efficient(a);
+ var mx = KMath.efficient(b);
+
+ if (mn.getType() == mx.getType()) {
+ if (mn.getType() == NbtElement.BYTE_TYPE) {
+ return new NbtByteArray(new byte[]{(byte) a, (byte) b});
+ } else if (mn.getType() == NbtElement.INT_TYPE || mn.getType() == NbtElement.SHORT_TYPE) {
+ return new NbtIntArray(new int[]{(int) a, (int) b});
+ } else {
+ var list = new NbtList();
+ list.add(mn);
+ list.add(mx);
+ return list;
+ }
+ } else {
+ var list = new NbtList();
+ list.add(NbtFloat.of(a));
+ list.add(NbtFloat.of(b));
+ return list;
+ }
+ }
+
+ public NbtElement toNbt() {
+ return toNbt(a, b);
+ }
+
+ @Override
+ public String toString() {
+ return a == b ? KMath.format(a) : (KMath.format(a) + " & " + KMath.format(b));
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/LightningPoints.java b/src/main/java/dev/latvian/mods/kmath/util/LightningPoints.java
new file mode 100644
index 0000000..5da9c5f
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/LightningPoints.java
@@ -0,0 +1,103 @@
+package dev.latvian.mods.kmath.util;
+
+import dev.latvian.mods.kmath.KMath;
+import dev.latvian.mods.kmath.render.LaserRenderer;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.render.VertexConsumer;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.util.math.random.Random;
+import org.joml.Math;
+import org.joml.Matrix3d;
+import org.joml.Vector3d;
+
+public class LightningPoints implements DeltaTicking {
+ public final LaserRenderer laser;
+ public final int segments;
+ public final Random random;
+ public final float[] prevAngles;
+ public final float[] prevDist;
+ public final float[] angles;
+ public final float[] dist;
+ public float spread;
+ public final Vector3d target = new Vector3d();
+ private final Matrix3d matrix = new Matrix3d();
+
+ public LightningPoints(LaserRenderer laser, int segments, Random random) {
+ this.laser = laser;
+ this.segments = segments;
+ this.random = random;
+ this.prevAngles = new float[segments - 1];
+ this.prevDist = new float[segments - 1];
+ this.angles = new float[segments - 1];
+ this.dist = new float[segments - 1];
+ this.spread = 2F;
+ }
+
+ @Environment(EnvType.CLIENT)
+ public void render(MatrixStack ms, float delta, VertexConsumer buffer) {
+ var length = target.length();
+
+ if (length <= 0F) {
+ return;
+ }
+
+ var startRadius = laser.startRadius;
+ var endRadius = laser.endRadius;
+
+ var prevPoint = new Vector3d(0D, 0D, 0D);
+ var point = new Vector3d(0D, 0D, 0D);
+
+ matrix.identity();
+ matrix.rotateY(Math.toRadians(-Rotations.getYaw(target.x / length, target.z / length)));
+ matrix.rotateX(Math.toRadians(90 + Rotations.getPitch(target.y / length)));
+
+ for (int i = 0; i < angles.length; i++) {
+ laser.startRadius = i == 0 ? endRadius : startRadius;
+ laser.endRadius = startRadius;
+
+ var angle = KMath.lerp(delta, prevAngles[i], angles[i]) * Math.PI * 2D;
+ var d = KMath.lerp(delta, prevDist[i], dist[i]) * spread;
+
+ prevPoint.set(point);
+
+ point.set(Math.cos(angle) * d, (i + 1F) * length / (segments + 1F), Math.sin(angle) * d).mul(matrix);
+
+ ms.push();
+ ms.translate(prevPoint.x, prevPoint.y, prevPoint.z);
+ laser.targetPos.set(point.x - prevPoint.x, point.y - prevPoint.y, point.z - prevPoint.z);
+ laser.updateTarget();
+ laser.render(ms, buffer);
+ ms.pop();
+ }
+
+ laser.startRadius = startRadius;
+ laser.endRadius = endRadius;
+
+ prevPoint.set(point);
+ point.set(0D, length, 0D).mul(matrix);
+
+ ms.push();
+ ms.translate(prevPoint.x, prevPoint.y, prevPoint.z);
+ laser.targetPos.set(point.x - prevPoint.x, point.y - prevPoint.y, point.z - prevPoint.z);
+ laser.updateTarget();
+ laser.render(ms, buffer);
+ ms.pop();
+ }
+
+ @Override
+ public void snap() {
+ for (int i = 0; i < angles.length; i++) {
+ prevAngles[i] = angles[i];
+ prevDist[i] = dist[i];
+ }
+ }
+
+ @Override
+ public void tickValue() {
+ for (int i = 0; i < angles.length; i++) {
+ angles[i] = random.nextFloat();
+ dist[i] = random.nextFloat() * spread;
+ }
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/Range.java b/src/main/java/dev/latvian/mods/kmath/util/Range.java
new file mode 100644
index 0000000..22c7986
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/Range.java
@@ -0,0 +1,40 @@
+package dev.latvian.mods.kmath.util;
+
+import dev.latvian.mods.kmath.KMath;
+import net.minecraft.nbt.NbtElement;
+import net.minecraft.util.math.random.Random;
+
+public record Range(float min, float max) {
+ public static final Range ZERO = new Range(0F, 0F);
+ public static final Range ONE = new Range(1F, 1F);
+
+ public static Range of(float min, float max) {
+ return min == 0F && max == 0F ? ZERO : min == 1F && max == 1F ? ONE : Math.abs(max - min) < 0.0001F ? new Range(min, min) : new Range(Math.min(min, max), Math.max(min, max));
+ }
+
+ public static Range of(float value) {
+ return of(value, value);
+ }
+
+ public static Range of(NbtElement nbt) {
+ var fp = FloatPair.of(nbt);
+ return fp == null ? null : of(fp.a(), fp.b());
+ }
+
+ public NbtElement toNbt() {
+ return FloatPair.toNbt(min, max);
+ }
+
+ public float get(float delta) {
+ return min == max ? min : delta * (max - min) + min;
+ }
+
+ public float get(Random random) {
+ return min == max ? min : random.nextFloat() * (max - min) + min;
+ }
+
+ @Override
+ public String toString() {
+ return min == max ? KMath.format(min) : (KMath.format(min) + " - " + KMath.format(max));
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/Rotations.java b/src/main/java/dev/latvian/mods/kmath/util/Rotations.java
new file mode 100644
index 0000000..ef29a4a
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/Rotations.java
@@ -0,0 +1,51 @@
+package dev.latvian.mods.kmath.util;
+
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.util.math.Vec3d;
+
+public class Rotations {
+ /**
+ * Calculates the minecraft yaw angle in degrees for the given x and z delta values
+ * Note: Default facing is south for vanilla entities(0)
+ *
+ * @param x x delta
+ * @param z z delta
+ * @return angle in degree
+ */
+ public static float getYaw(double x, double z) {
+ // Minecraft yaw is always offset by 90 degree
+ return MathHelper.wrapDegrees((float) (Math.toDegrees(Math.atan2(z, x)) - 90));
+ }
+
+ public static float getYaw(Vec3d direction) {
+ return getYaw(direction.getX(), direction.getZ());
+ }
+
+ public static float getYaw(BlockPos direction) {
+ return getYaw(direction.getX(), direction.getZ());
+ }
+
+ /**
+ * Calculates the minecraft pitch angle in degrees for the given y delta. Needs to be normalized to 0-1.
+ *
+ * @param y delta 0-1
+ * @return angle in degree
+ */
+ public static float getPitch(double y) {
+ if (y > 1) {
+ return Float.NaN;
+ }
+
+ // Minecraft pitch is inverted
+ return MathHelper.wrapDegrees((float) -Math.toDegrees(Math.asin(y)));
+ }
+
+ public static float getPitch(Vec3d direction) {
+ return getPitch(direction.normalize().y);
+ }
+
+ public static float getPitch(BlockPos direction) {
+ return getPitch(Vec3d.of(direction));
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/SpherePoints.java b/src/main/java/dev/latvian/mods/kmath/util/SpherePoints.java
new file mode 100644
index 0000000..42bc142
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/SpherePoints.java
@@ -0,0 +1,90 @@
+package dev.latvian.mods.kmath.util;
+
+import org.joml.Vector3f;
+
+public class SpherePoints {
+ public static final SpherePoints L = new SpherePoints(9, 7);
+ public static final SpherePoints M = new SpherePoints(24, 9);
+ public static final SpherePoints H = new SpherePoints(36, 16);
+ public static final SpherePoints X = new SpherePoints(108, 27);
+
+ public static SpherePoints get(int hd, int vd) {
+ if (hd == 9 && vd == 7) {
+ return L;
+ } else if (hd == 24 && vd == 9) {
+ return M;
+ } else if (hd == 36 && vd == 16) {
+ return H;
+ } else if (hd == 108 && vd == 27) {
+ return X;
+ } else {
+ return new SpherePoints(hd, vd);
+ }
+ }
+
+ public record Col(int index, float u, float x, float z) {
+ }
+
+ public record Row(int index, float v, float y, float m) {
+ }
+
+ public final int hdetail;
+ public final int vdetail;
+ public final Col[] cols;
+ public final Row[] rows;
+ public final Vector3f[][] normals;
+
+ private SpherePoints(int hd, int vd) {
+ this.hdetail = hd;
+ this.vdetail = vd;
+ this.cols = new Col[hd + 1];
+ this.rows = new Row[vd + 1];
+ this.normals = new Vector3f[hd][vd];
+ calculate();
+ }
+
+ public void calculate() {
+ for (int i = 0; i < cols.length; i++) {
+ double d = i / (cols.length - 1D);
+ this.cols[i] = new Col(i, (float) (1D - d), (float) (Math.cos(d * Math.PI * 2D) * 0.5D), (float) (Math.sin(d * Math.PI * 2D) * 0.5D));
+ }
+
+ for (int i = 1; i < rows.length - 1; i++) {
+ double d = i / (rows.length - 1D);
+ this.rows[i] = new Row(i, (float) d, (float) (Math.cos(d * Math.PI) * 0.5D), (float) (((Math.sin(d * Math.PI) + 1D) / 2D - 0.5D) * 2D));
+ }
+
+ rows[0] = new Row(0, 0F, 0.5F, 0F);
+ rows[rows.length - 1] = new Row(rows.length - 1, 1F, -0.5F, 0F);
+
+ for (int r = 0; r < rows.length - 1; r++) {
+ for (int c = 0; c < cols.length - 1; c++) {
+ var cr = rows[r];
+ var nr = rows[r + 1];
+ var cc = cols[c];
+ var nc = cols[c + 1];
+
+ // var n = new Vector3f(cr.x * nc.m, cr.y, cr.z * nc.m).cross(new Vector3f(nr.x * cc.m, nr.y, nr.z * cc.m)).normalize();
+ normals[c][r] = new Vector3f(0F, 1F, 0F);
+ }
+ }
+
+ /* FIXME
+ for (int r = 0; r < rows.length - 1; r++) {
+ for (int c = 0; c < cols.length - 1; c++) {
+ var cr = rows[r];
+ var nr = rows[r + 1];
+ var cc = cols[c];
+ var nc = cols[c + 1];
+
+ var va = new Vector3f(cc.x * nr.m, nr.y, cc.z * nr.m);
+ var vb = new Vector3f(cc.x * cr.m, cr.y, cc.z * cr.m);
+ var vc = new Vector3f(nc.x * cr.m, cr.y, nc.z * cr.m);
+ var vd = new Vector3f(nc.x * nr.m, nr.y, nc.z * nr.m);
+
+ // normals[c][r] = vb.sub(va).mul(vc.sub(vb)).normalize();
+ }
+ }
+ */
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/Split.java b/src/main/java/dev/latvian/mods/kmath/util/Split.java
new file mode 100644
index 0000000..8c55374
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/Split.java
@@ -0,0 +1,115 @@
+package dev.latvian.mods.kmath.util;
+
+import net.minecraft.util.math.Box;
+import net.minecraft.util.math.Direction;
+import net.minecraft.util.math.random.Xoroshiro128PlusPlusRandom;
+import org.joml.Vector3f;
+
+public class Split {
+ public static final Vector3f[][] FACE_POS = new Vector3f[6][];
+ public static final Vector3f[] NORMALS = new Vector3f[6];
+
+ static {
+ float mns = -0.5F;
+ float mxs = 0.5F;
+
+ var vWDN = new Vector3f(mns, mns, mns);
+ var vEDN = new Vector3f(mxs, mns, mns);
+ var vEDS = new Vector3f(mxs, mns, mxs);
+ var vWDS = new Vector3f(mns, mns, mxs);
+ var vWUN = new Vector3f(mns, mxs, mns);
+ var vEUN = new Vector3f(mxs, mxs, mns);
+ var vEUS = new Vector3f(mxs, mxs, mxs);
+ var vWUS = new Vector3f(mns, mxs, mxs);
+
+ FACE_POS[0] = new Vector3f[]{vWDS, vWDN, vEDN, vEDS}; // down
+ FACE_POS[1] = new Vector3f[]{vWUN, vWUS, vEUS, vEUN}; // up
+ FACE_POS[2] = new Vector3f[]{vEUN, vEDN, vWDN, vWUN}; // north
+ FACE_POS[3] = new Vector3f[]{vWUS, vWDS, vEDS, vEUS}; // south
+ FACE_POS[4] = new Vector3f[]{vWUN, vWDN, vWDS, vWUS}; // west
+ FACE_POS[5] = new Vector3f[]{vEUS, vEDS, vEDN, vEUN}; // east
+
+ for (int i = 0; i < 6; i++) {
+ NORMALS[i] = Direction.byId(i).getUnitVector();
+ }
+ }
+
+ public final int id;
+ public final int split;
+ public final int count;
+ public final SplitBox[] boxes;
+ public final float scale;
+ public final Vector3f min;
+ public final Vector3f max;
+ public final Box box;
+
+ public Split(int id) {
+ this.id = id;
+ this.split = 1 << id;
+ this.count = split * split * split;
+ this.boxes = new SplitBox[count];
+ this.scale = 1F / (float) split;
+ float mns = -scale / 2F;
+ float mxs = scale / 2F;
+ this.min = new Vector3f(mns, mns, mns);
+ this.max = new Vector3f(mxs, mxs, mxs);
+ this.box = new Box(mns, mns, mns, mxs, mxs, mxs);
+ }
+
+ public void calculateUV(long seed, float weirdness) {
+ var r = new Xoroshiro128PlusPlusRandom(seed);
+
+ for (int x = 0; x < split; x++) {
+ for (int y = 0; y < split; y++) {
+ for (int z = 0; z < split; z++) {
+ int i = x + z * split + y * split * split;
+
+ float x0 = x * scale;
+ float y0 = y * scale;
+ float z0 = z * scale;
+ float x1 = (x + 1F) * scale;
+ float y1 = (y + 1F) * scale;
+ float z1 = (z + 1F) * scale;
+
+ boxes[i] = new SplitBox(this, i, (x + 0.5F) * scale, (y + 0.5F) * scale, (z + 0.5F) * scale, new UV[6], new Vector3f[6][]);
+
+ boxes[i].uvs()[0] = new UV(x0, 1F - z1, x1, 1F - z0); // down
+ boxes[i].uvs()[1] = new UV(x0, z0, x1, z1); // up
+ boxes[i].uvs()[2] = new UV(x1, 1F - y1, x0, 1F - y0); // north
+ boxes[i].uvs()[3] = new UV(x0, 1F - y1, x1, 1F - y0); // south
+ boxes[i].uvs()[4] = new UV(z0, 1F - y1, z1, 1F - y0); // west
+ boxes[i].uvs()[5] = new UV(1F - z1, 1F - y1, 1F - z0, 1F - y0); // east
+
+ float wa = 1F - weirdness;
+ float wm = weirdness * 2F;
+
+ for (int f = 0; f < 6; f++) {
+ float mns = -0.5F;
+ float mxs = 0.5F;
+
+ var vWDN = new Vector3f(mns * (wa + r.nextFloat() * wm), mns * (wa + r.nextFloat() * wm), mns * (wa + r.nextFloat() * wm));
+ var vEDN = new Vector3f(mxs * (wa + r.nextFloat() * wm), mns * (wa + r.nextFloat() * wm), mns * (wa + r.nextFloat() * wm));
+ var vEDS = new Vector3f(mxs * (wa + r.nextFloat() * wm), mns * (wa + r.nextFloat() * wm), mxs * (wa + r.nextFloat() * wm));
+ var vWDS = new Vector3f(mns * (wa + r.nextFloat() * wm), mns * (wa + r.nextFloat() * wm), mxs * (wa + r.nextFloat() * wm));
+ var vWUN = new Vector3f(mns * (wa + r.nextFloat() * wm), mxs * (wa + r.nextFloat() * wm), mns * (wa + r.nextFloat() * wm));
+ var vEUN = new Vector3f(mxs * (wa + r.nextFloat() * wm), mxs * (wa + r.nextFloat() * wm), mns * (wa + r.nextFloat() * wm));
+ var vEUS = new Vector3f(mxs * (wa + r.nextFloat() * wm), mxs * (wa + r.nextFloat() * wm), mxs * (wa + r.nextFloat() * wm));
+ var vWUS = new Vector3f(mns * (wa + r.nextFloat() * wm), mxs * (wa + r.nextFloat() * wm), mxs * (wa + r.nextFloat() * wm));
+
+ boxes[i].facePos()[0] = new Vector3f[]{vWDS, vWDN, vEDN, vEDS}; // down
+ boxes[i].facePos()[1] = new Vector3f[]{vWUN, vWUS, vEUS, vEUN}; // up
+ boxes[i].facePos()[2] = new Vector3f[]{vEUN, vEDN, vWDN, vWUN}; // north
+ boxes[i].facePos()[3] = new Vector3f[]{vWUS, vWDS, vEDS, vEUS}; // south
+ boxes[i].facePos()[4] = new Vector3f[]{vWUN, vWDN, vWDS, vWUS}; // west
+ boxes[i].facePos()[5] = new Vector3f[]{vEUS, vEDS, vEDN, vEUN}; // east
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Split_1/" + split;
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/SplitBox.java b/src/main/java/dev/latvian/mods/kmath/util/SplitBox.java
new file mode 100644
index 0000000..0d29129
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/SplitBox.java
@@ -0,0 +1,16 @@
+package dev.latvian.mods.kmath.util;
+
+import org.joml.Vector3f;
+
+import java.util.Arrays;
+
+public record SplitBox(Split split, int index, float centerX, float centerY, float centerZ, UV[] uvs, Vector3f[][] facePos) {
+ @Override
+ public String toString() {
+ return "SplitBox#" + index + "@" + centerX + ";" + centerY + ";" + centerZ + "=" + Arrays.toString(uvs);
+ }
+
+ public int key() {
+ return (split.id << 7) | index;
+ }
+}
diff --git a/src/main/java/dev/latvian/mods/kmath/util/UV.java b/src/main/java/dev/latvian/mods/kmath/util/UV.java
new file mode 100644
index 0000000..6c3cfb1
--- /dev/null
+++ b/src/main/java/dev/latvian/mods/kmath/util/UV.java
@@ -0,0 +1,19 @@
+package dev.latvian.mods.kmath.util;
+
+import net.minecraft.util.math.MathHelper;
+
+public record UV(float u0, float v0, float u1, float v1) {
+ public UV mul(UV uv) {
+ return new UV(
+ MathHelper.lerp(uv.u0, u0, u1),
+ MathHelper.lerp(uv.v0, v0, v1),
+ MathHelper.lerp(uv.u1, u0, u1),
+ MathHelper.lerp(uv.v1, v0, v1)
+ );
+ }
+
+ @Override
+ public String toString() {
+ return "[" + u0 + "," + v0 + "," + u1 + "," + v1 + "]";
+ }
+}
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
new file mode 100644
index 0000000..41a3214
--- /dev/null
+++ b/src/main/resources/fabric.mod.json
@@ -0,0 +1,18 @@
+{
+ "schemaVersion": 1,
+ "id": "kmath",
+ "version": "${version}",
+ "name": "KMath",
+ "description": "Math stuff",
+ "authors": [
+ "Lat"
+ ],
+ "license": "MIT",
+ "environment": "*",
+ "accessWidener": "kmath.accessWidener",
+ "depends": {
+ "fabricloader": "*",
+ "fabric": "*",
+ "minecraft": ">=1.20.1"
+ }
+}
diff --git a/src/main/resources/kmath.accessWidener b/src/main/resources/kmath.accessWidener
new file mode 100644
index 0000000..f0d6929
--- /dev/null
+++ b/src/main/resources/kmath.accessWidener
@@ -0,0 +1,5 @@
+accessWidener v2 named
+
+accessible field net/minecraft/command/EntitySelector basePredicate Ljava/util/function/Predicate;
+accessible method net/minecraft/world/World getEntityLookup ()Lnet/minecraft/world/entity/EntityLookup;
+accessible method net/minecraft/client/render/Frustum isVisible (DDDDDD)Z
\ No newline at end of file