From: Gabriel Dunne Date: Tue, 18 Jun 2013 02:26:20 +0000 (-0700) Subject: Import from SVN. X-Git-Url: https://git.quilime.com/?a=commitdiff_plain;h=HEAD;p=mekstension.git Import from SVN. --- 87a478fac273bb21ded6ce14a3f9a09738c9629e diff --git a/java/Mekstension/assets/NOTES b/java/Mekstension/assets/NOTES new file mode 100644 index 0000000..d27b511 --- /dev/null +++ b/java/Mekstension/assets/NOTES @@ -0,0 +1,88 @@ + MEKSTENSION DESIGN DOCUMENT + + software to be used for animation tool. + software extends geometric objects into space creating depth + each object is a unique object that can be placed and rotated + for animation purposes + + 11th hour: + edges of objects can turn into floating tenticles + + + stop animation displayed on Apple Cinema display + 2560 x 1600 /2= 1280 x 800 + + + + + + three shapes: + square + triangle + circle + + each shape extends as a wireframe object into space + variables are: + depth + line thickness + solid / wire + edge color + dotted lines + points (no lines) + + interface: + move and scale to position objects for photography + ability to change depth + ability to change direction + + + + + shape object + + methods + direction ( degrees ) + length ( float ) + strokeWeight ( float ) + fillColor ( uint ) + strokeColor ( uint ) + + + + + + + + + + + + + + + + -- + hide cursor + + global: + color (front, back) + stroke color + transparency + stroke transparency + segments + angle + + obj: + move + rotate + scale + depth + + + + record movie + + + + + diff --git a/java/Mekstension/data/HelveticaNeue-Light-48.vlw b/java/Mekstension/data/HelveticaNeue-Light-48.vlw new file mode 100644 index 0000000..0733337 Binary files /dev/null and b/java/Mekstension/data/HelveticaNeue-Light-48.vlw differ diff --git a/java/Mekstension/data/fonts/HelveticaNeueLight.ttf b/java/Mekstension/data/fonts/HelveticaNeueLight.ttf new file mode 100644 index 0000000..93a07d1 Binary files /dev/null and b/java/Mekstension/data/fonts/HelveticaNeueLight.ttf differ diff --git a/java/Mekstension/lib/core.jar b/java/Mekstension/lib/core.jar new file mode 100644 index 0000000..9d25267 Binary files /dev/null and b/java/Mekstension/lib/core.jar differ diff --git a/java/Mekstension/lib/opengl/.classpath b/java/Mekstension/lib/opengl/.classpath new file mode 100644 index 0000000..ed41012 --- /dev/null +++ b/java/Mekstension/lib/opengl/.classpath @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/java/Mekstension/lib/opengl/.project b/java/Mekstension/lib/opengl/.project new file mode 100644 index 0000000..c799b3b --- /dev/null +++ b/java/Mekstension/lib/opengl/.project @@ -0,0 +1,17 @@ + + + opengl + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/java/Mekstension/lib/opengl/.settings/org.eclipse.jdt.core.prefs b/java/Mekstension/lib/opengl/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..a3b0015 --- /dev/null +++ b/java/Mekstension/lib/opengl/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,263 @@ +#Mon May 05 22:36:11 EDT 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2 +org.eclipse.jdt.core.compiler.compliance=1.4 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning +org.eclipse.jdt.core.compiler.source=1.3 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=18 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=18 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=18 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=36 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=18 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=18 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=1 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=2 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=80 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=2 +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true diff --git a/java/Mekstension/lib/opengl/.settings/org.eclipse.jdt.ui.prefs b/java/Mekstension/lib/opengl/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..9acb195 --- /dev/null +++ b/java/Mekstension/lib/opengl/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,4 @@ +#Thu Jan 10 10:50:38 PST 2008 +eclipse.preferences.version=1 +formatter_profile=_fry +formatter_settings_version=11 diff --git a/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL$1.class b/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL$1.class new file mode 100644 index 0000000..c04da4a Binary files /dev/null and b/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL$1.class differ diff --git a/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL$ImageCache.class b/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL$ImageCache.class new file mode 100644 index 0000000..436ee25 Binary files /dev/null and b/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL$ImageCache.class differ diff --git a/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL$TessCallback.class b/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL$TessCallback.class new file mode 100644 index 0000000..13980fb Binary files /dev/null and b/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL$TessCallback.class differ diff --git a/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL.class b/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL.class new file mode 100644 index 0000000..eaf9b0a Binary files /dev/null and b/java/Mekstension/lib/opengl/bin/processing/opengl/PGraphicsOpenGL.class differ diff --git a/java/Mekstension/lib/opengl/jogl-src.zip b/java/Mekstension/lib/opengl/jogl-src.zip new file mode 100644 index 0000000..d872a61 Binary files /dev/null and b/java/Mekstension/lib/opengl/jogl-src.zip differ diff --git a/java/Mekstension/lib/opengl/library/export.txt b/java/Mekstension/lib/opengl/library/export.txt new file mode 100644 index 0000000..f8c4b62 --- /dev/null +++ b/java/Mekstension/lib/opengl/library/export.txt @@ -0,0 +1,13 @@ +# If you want to support more platforms, see the jogl.dev.java.net to get the +# natives libraries for the platform in question (i.e. solaris). Then, add it +# them to the applet line for export. For applications, you'll have to make the +# changes by hand, i.e. use the linux version of the export, and modify its +# contents to include the necessary files for your platform. + +application.macosx = opengl.jar, jogl.jar, libjogl.jnilib, libjogl_awt.jnilib, libjogl_cg.jnilib, gluegen-rt.jar, libgluegen-rt.jnilib + +application.windows = opengl.jar, jogl.jar, jogl.dll, jogl_awt.dll, jogl_cg.dll, gluegen-rt.jar, gluegen-rt.dll + +application.linux = opengl.jar, jogl.jar, gluegen-rt.jar, libjogl.so, libjogl_awt.so, libjogl_cg.so, libgluegen-rt.so + +applet = opengl.jar, jogl.jar, jogl.jar.pack.gz, jogl-natives-linux-i586.jar, jogl-natives-linux-amd64.jar, jogl-natives-macosx-ppc.jar, jogl-natives-macosx-universal.jar, jogl-natives-windows-i586.jar, jogl-natives-windows-amd64.jar, gluegen-rt.jar, gluegen-rt.jar.pack.gz, gluegen-rt-natives-linux-amd64.jar, gluegen-rt-natives-windows-amd64.jar, gluegen-rt-natives-linux-i586.jar, gluegen-rt-natives-windows-i586.jar, gluegen-rt-natives-macosx-ppc.jar, gluegen-rt-natives-macosx-universal.jar diff --git a/java/Mekstension/lib/opengl/library/gluegen-rt-natives-linux-amd64.jar b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-linux-amd64.jar new file mode 100644 index 0000000..b4c6da4 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-linux-amd64.jar differ diff --git a/java/Mekstension/lib/opengl/library/gluegen-rt-natives-linux-i586.jar b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-linux-i586.jar new file mode 100644 index 0000000..0090020 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-linux-i586.jar differ diff --git a/java/Mekstension/lib/opengl/library/gluegen-rt-natives-macosx-ppc.jar b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-macosx-ppc.jar new file mode 100644 index 0000000..2dafc1d Binary files /dev/null and b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-macosx-ppc.jar differ diff --git a/java/Mekstension/lib/opengl/library/gluegen-rt-natives-macosx-universal.jar b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-macosx-universal.jar new file mode 100644 index 0000000..c961056 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-macosx-universal.jar differ diff --git a/java/Mekstension/lib/opengl/library/gluegen-rt-natives-windows-amd64.jar b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-windows-amd64.jar new file mode 100644 index 0000000..e162d94 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-windows-amd64.jar differ diff --git a/java/Mekstension/lib/opengl/library/gluegen-rt-natives-windows-i586.jar b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-windows-i586.jar new file mode 100644 index 0000000..5cc7f06 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/gluegen-rt-natives-windows-i586.jar differ diff --git a/java/Mekstension/lib/opengl/library/gluegen-rt.dll b/java/Mekstension/lib/opengl/library/gluegen-rt.dll new file mode 100644 index 0000000..281d389 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/gluegen-rt.dll differ diff --git a/java/Mekstension/lib/opengl/library/gluegen-rt.jar b/java/Mekstension/lib/opengl/library/gluegen-rt.jar new file mode 100644 index 0000000..d53b307 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/gluegen-rt.jar differ diff --git a/java/Mekstension/lib/opengl/library/gluegen-rt.jar.pack.gz b/java/Mekstension/lib/opengl/library/gluegen-rt.jar.pack.gz new file mode 100644 index 0000000..013671a Binary files /dev/null and b/java/Mekstension/lib/opengl/library/gluegen-rt.jar.pack.gz differ diff --git a/java/Mekstension/lib/opengl/library/jogl-natives-linux-amd64.jar b/java/Mekstension/lib/opengl/library/jogl-natives-linux-amd64.jar new file mode 100644 index 0000000..c852ec9 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/jogl-natives-linux-amd64.jar differ diff --git a/java/Mekstension/lib/opengl/library/jogl-natives-linux-i586.jar b/java/Mekstension/lib/opengl/library/jogl-natives-linux-i586.jar new file mode 100644 index 0000000..a44e741 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/jogl-natives-linux-i586.jar differ diff --git a/java/Mekstension/lib/opengl/library/jogl-natives-macosx-ppc.jar b/java/Mekstension/lib/opengl/library/jogl-natives-macosx-ppc.jar new file mode 100644 index 0000000..cfa8e72 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/jogl-natives-macosx-ppc.jar differ diff --git a/java/Mekstension/lib/opengl/library/jogl-natives-macosx-universal.jar b/java/Mekstension/lib/opengl/library/jogl-natives-macosx-universal.jar new file mode 100644 index 0000000..bfcbb2a Binary files /dev/null and b/java/Mekstension/lib/opengl/library/jogl-natives-macosx-universal.jar differ diff --git a/java/Mekstension/lib/opengl/library/jogl-natives-windows-amd64.jar b/java/Mekstension/lib/opengl/library/jogl-natives-windows-amd64.jar new file mode 100644 index 0000000..a5a0c52 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/jogl-natives-windows-amd64.jar differ diff --git a/java/Mekstension/lib/opengl/library/jogl-natives-windows-i586.jar b/java/Mekstension/lib/opengl/library/jogl-natives-windows-i586.jar new file mode 100644 index 0000000..a6c700d Binary files /dev/null and b/java/Mekstension/lib/opengl/library/jogl-natives-windows-i586.jar differ diff --git a/java/Mekstension/lib/opengl/library/jogl.dll b/java/Mekstension/lib/opengl/library/jogl.dll new file mode 100644 index 0000000..ee7a6a5 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/jogl.dll differ diff --git a/java/Mekstension/lib/opengl/library/jogl.jar b/java/Mekstension/lib/opengl/library/jogl.jar new file mode 100644 index 0000000..32b5421 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/jogl.jar differ diff --git a/java/Mekstension/lib/opengl/library/jogl.jar.pack.gz b/java/Mekstension/lib/opengl/library/jogl.jar.pack.gz new file mode 100644 index 0000000..0578c3d Binary files /dev/null and b/java/Mekstension/lib/opengl/library/jogl.jar.pack.gz differ diff --git a/java/Mekstension/lib/opengl/library/jogl_awt.dll b/java/Mekstension/lib/opengl/library/jogl_awt.dll new file mode 100644 index 0000000..2b47eee Binary files /dev/null and b/java/Mekstension/lib/opengl/library/jogl_awt.dll differ diff --git a/java/Mekstension/lib/opengl/library/jogl_cg.dll b/java/Mekstension/lib/opengl/library/jogl_cg.dll new file mode 100644 index 0000000..c64e2a2 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/jogl_cg.dll differ diff --git a/java/Mekstension/lib/opengl/library/libgluegen-rt.jnilib b/java/Mekstension/lib/opengl/library/libgluegen-rt.jnilib new file mode 100644 index 0000000..1956280 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/libgluegen-rt.jnilib differ diff --git a/java/Mekstension/lib/opengl/library/libjogl.jnilib b/java/Mekstension/lib/opengl/library/libjogl.jnilib new file mode 100644 index 0000000..eb8719a Binary files /dev/null and b/java/Mekstension/lib/opengl/library/libjogl.jnilib differ diff --git a/java/Mekstension/lib/opengl/library/libjogl_awt.jnilib b/java/Mekstension/lib/opengl/library/libjogl_awt.jnilib new file mode 100644 index 0000000..2f16fbf Binary files /dev/null and b/java/Mekstension/lib/opengl/library/libjogl_awt.jnilib differ diff --git a/java/Mekstension/lib/opengl/library/libjogl_cg.jnilib b/java/Mekstension/lib/opengl/library/libjogl_cg.jnilib new file mode 100644 index 0000000..5627121 Binary files /dev/null and b/java/Mekstension/lib/opengl/library/libjogl_cg.jnilib differ diff --git a/java/Mekstension/lib/opengl/library/opengl.jar b/java/Mekstension/lib/opengl/library/opengl.jar new file mode 100644 index 0000000..749c7fd Binary files /dev/null and b/java/Mekstension/lib/opengl/library/opengl.jar differ diff --git a/java/Mekstension/lib/opengl/src/processing/opengl/PGraphicsOpenGL.java b/java/Mekstension/lib/opengl/src/processing/opengl/PGraphicsOpenGL.java new file mode 100644 index 0000000..ce7995e --- /dev/null +++ b/java/Mekstension/lib/opengl/src/processing/opengl/PGraphicsOpenGL.java @@ -0,0 +1,3015 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-08 Ben Fry and Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.opengl; + +import processing.core.*; + +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.nio.*; + +import javax.media.opengl.*; +import javax.media.opengl.glu.*; +import com.sun.opengl.util.*; + + +/** + * Implementation of the PGraphics API that employs OpenGL rendering via JOGL. + *

+ * JOGL requires Java 1.4 or higher, so there are no restrictions on this + * code to be compatible with Java 1.1 or Java 1.3. + *

+ * This code relies on PGraphics3D for all lighting and transformations. + * Meaning that translate(), rotate(), and any lighting will be done in + * PGraphics3D, and OpenGL is only used to blit lines and triangles as fast + * as it possibly can. + *

+ * For this reason, OpenGL may not be accelerated as far as it could be, + * but I don't have the time to maintain two separate versions of the + * renderer. My development time must always be focused on implementation + * and covering features first, and optimizing later. + *

+ * Further, the difference may be negligible, as the primary slowdown + * in Java is moving pixels (i.e. a large frame buffer is nearly impossible + * because Java just can't do a MemoryImageSource at screen resolution) + * and the overhead from JNI tends to be significant. In the latter case, + * we may even save time in some cases where a large number of calls to + * OpenGL would otherwise be used, but that's probably a stretch. + *

+ * The code is also very messy, while features are being added and + * removed rapidly as we head towards 1.0. Things got particularly ugly + * as we approached beta while both Simon and I were working on it. + * Relax, we'll get it fixed up later. + *

+ * When exporting applets, the JOGL Applet Launcher is used. More information + * about the launcher can be found at its documentation page. + */ +public class PGraphicsOpenGL extends PGraphics3D { + protected GLDrawable drawable; // the rendering 'surface' + protected GLContext context; // the rendering context (holds rendering state info) + + public GL gl; + public GLU glu; + //public GLCanvas canvas; + + //protected FloatBuffer projectionFloatBuffer; + protected float[] projectionFloats; + + protected GLUtessellator tobj; + protected TessCallback tessCallback; + + /// Buffer to hold light values before they're sent to OpenGL + //protected FloatBuffer lightBuffer; + protected float[] lightArray = new float[] { 1, 1, 1 }; + + static int maxTextureSize; + + int[] textureDeleteQueue = new int[10]; + int textureDeleteQueueCount = 0; + + /// Used to hold color values to be sent to OpenGL + //protected FloatBuffer colorBuffer; + protected float[] colorBuffer; + + /// Used to store empty values to be passed when a light has no ambient value + protected FloatBuffer zeroBuffer; + + /// IntBuffer to go with the pixels[] array + protected IntBuffer pixelBuffer; + + /** + * Set to true if the host system is big endian (PowerPC, MIPS, SPARC), + * false if little endian (x86 Intel for Mac or PC). + */ + static public boolean BIG_ENDIAN = + ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; + + + public PGraphicsOpenGL() { + glu = new GLU(); + + tobj = glu.gluNewTess(); + + // unfortunately glu.gluDeleteTess(tobj); is never called + //glu.gluTessProperty(tobj, GLU.GLU_TESS_WINDING_RULE, + // GLU.GLU_TESS_WINDING_NONZERO); + //glu.gluTessProperty(tobj, GLU.GLU_TESS_WINDING_RULE, + // GLU.GLU_TESS_WINDING_POSITIVE); + //GLU.GLU_TESS_WINDING_ODD); + //glu.gluTessProperty(tobj, GLU.GLU_TESS_BOUNDARY_ONLY, + // GL.GL_TRUE); + + tessCallback = new TessCallback(); + glu.gluTessCallback(tobj, GLU.GLU_TESS_BEGIN, tessCallback); + glu.gluTessCallback(tobj, GLU.GLU_TESS_END, tessCallback); + glu.gluTessCallback(tobj, GLU.GLU_TESS_VERTEX, tessCallback); + glu.gluTessCallback(tobj, GLU.GLU_TESS_COMBINE, tessCallback); + glu.gluTessCallback(tobj, GLU.GLU_TESS_ERROR, tessCallback); + +// lightBuffer = BufferUtil.newFloatBuffer(4); +// lightBuffer.put(3, 1.0f); +// lightBuffer.rewind(); + } + + + //public void setParent(PApplet parent) // PGraphics + + + //public void setPrimary(boolean primary) // PGraphics + + + //public void setPath(String path) // PGraphics + + + //public void setSize(int iwidth, int iheight) // PGraphics3D + + + /** + * Called by resize(), this handles creating the actual GLCanvas the + * first time around, or simply resizing it on subsequent calls. + * There is no pixel array to allocate for an OpenGL canvas + * because OpenGL's pixel buffer is all handled internally. + */ + protected void allocate() { + if (context == null) { +// System.out.println("PGraphicsOpenGL.allocate() for " + width + " " + height); +// new Exception().printStackTrace(System.out); + // If OpenGL 2X or 4X smoothing is enabled, setup caps object for them + GLCapabilities capabilities = new GLCapabilities(); + // Starting in release 0158, OpenGL smoothing is always enabled + if (!hints[DISABLE_OPENGL_2X_SMOOTH]) { + capabilities.setSampleBuffers(true); + capabilities.setNumSamples(2); + } else if (hints[ENABLE_OPENGL_4X_SMOOTH]) { + capabilities.setSampleBuffers(true); + capabilities.setNumSamples(4); + } + + // get a rendering surface and a context for this canvas + GLDrawableFactory factory = GLDrawableFactory.getFactory(); + + /* + if (PApplet.platform == PConstants.LINUX) { + GraphicsConfiguration pconfig = parent.getGraphicsConfiguration(); + System.out.println("parent config is " + pconfig); + + // GraphicsDevice device = config.getDevice(); + //AbstractGraphicsDevice agd = new AbstractGraphicsDevice(device); + //AbstractGraphicsConfiguration agc = factory.chooseGraphicsConfiguration(capabilities, null, null); + + AWTGraphicsConfiguration agc = (AWTGraphicsConfiguration) + factory.chooseGraphicsConfiguration(capabilities, null, null); + GraphicsConfiguration config = agc.getGraphicsConfiguration(); + System.out.println("agc config is " + config); + } + */ + + drawable = factory.getGLDrawable(parent, capabilities, null); + context = drawable.createContext(null); + + // need to get proper opengl context since will be needed below + gl = context.getGL(); + // Flag defaults to be reset on the next trip into beginDraw(). + settingsInited = false; + + } else { + // changing for 0100, need to resize rather than re-allocate + //System.out.println("PGraphicsOpenGL.allocate() again for " + width + " " + height); + reapplySettings(); + } + } + + + //public void dispose() // PGraphics + + + + //////////////////////////////////////////////////////////// + + + /** + * Get the current context, for use by libraries that need to talk to it. + */ + public GLContext getContext() { + return context; + } + + + /** + * Make the OpenGL rendering context current for this thread. + */ + protected void detainContext() { + try { + while (context.makeCurrent() == GLContext.CONTEXT_NOT_CURRENT) { +// System.out.println("Context not yet current..."); +// new Exception().printStackTrace(System.out); +// Thread.sleep(1000); + Thread.sleep(10); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + + /** + * Release the context, otherwise the AWT lock on X11 will not be released + */ + protected void releaseContext() { + context.release(); + } + + + /** + * OpenGL cannot draw until a proper native peer is available, so this + * returns the value of PApplet.isDisplayable() (inherited from Component). + */ + public boolean canDraw() { + return parent.isDisplayable(); + } + + + public void beginDraw() { + //if (!parent.isDisplayable()) return; + + // When using an offscreen buffer, the drawable instance will be null. + // The offscreen buffer uses the drawing context of the main PApplet. + if (drawable != null) { + // Call setRealized() after addNotify() has been called + drawable.setRealized(parent.isDisplayable()); + //System.out.println("OpenGL beginDraw() setting realized " + parent.isDisplayable()); + if (parent.isDisplayable()) { + //System.out.println(" we'll realize it alright"); + drawable.setRealized(true); + } else { + //System.out.println(" not yet ready to be realized"); + return; // Should have called canDraw() anyway + } + detainContext(); + } + + // On the first frame that's guaranteed to be on screen, + // and the component valid and all that, ask for focus. +// if ((parent != null) && parent.frameCount == 1) { +// canvas.requestFocus(); +// } + + super.beginDraw(); + + report("top beginDraw()"); + + gl.glDisable(GL.GL_LIGHTING); + for (int i = 0; i < MAX_LIGHTS; i++) { + gl.glDisable(GL.GL_LIGHT0 + i); + } + + gl.glMatrixMode(GL.GL_PROJECTION); + if (projectionFloats == null) { + projectionFloats = new float[] { + projection.m00, projection.m10, projection.m20, projection.m30, + projection.m01, projection.m11, projection.m21, projection.m31, + projection.m02, projection.m12, projection.m22, projection.m32, + projection.m03, projection.m13, projection.m23, projection.m33 + }; + } else { + projectionFloats[0] = projection.m00; + projectionFloats[1] = projection.m10; + projectionFloats[2] = projection.m20; + projectionFloats[3] = projection.m30; + + projectionFloats[4] = projection.m01; + projectionFloats[5] = projection.m11; + projectionFloats[6] = projection.m21; + projectionFloats[7] = projection.m31; + + projectionFloats[8] = projection.m02; + projectionFloats[9] = projection.m12; + projectionFloats[10] = projection.m22; + projectionFloats[11] = projection.m32; + + projectionFloats[12] = projection.m03; + projectionFloats[13] = projection.m13; + projectionFloats[14] = projection.m23; + projectionFloats[15] = projection.m33; + } + //projection.print(); + gl.glLoadMatrixf(projectionFloats, 0); + + gl.glMatrixMode(GL.GL_MODELVIEW); + gl.glLoadIdentity(); + // Flip Y-axis to make y count from 0 downwards + gl.glScalef(1, -1, 1); + + // these are necessary for alpha (i.e. fonts) to work + gl.glEnable(GL.GL_BLEND); + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); + + // this is necessary for 3D drawing + if (hints[DISABLE_DEPTH_TEST]) { + gl.glDisable(GL.GL_DEPTH_TEST); + } else { + gl.glEnable(GL.GL_DEPTH_TEST); + } + // use <= since that's what processing.core does + gl.glDepthFunc(GL.GL_LEQUAL); + + // because y is flipped + gl.glFrontFace(GL.GL_CW); + + // coloured stuff + gl.glEnable(GL.GL_COLOR_MATERIAL); + gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE); + gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR); + + // these tend to make life easier + // (but sometimes at the expense of a little speed) + // Not using them right now because we're doing our own lighting. + //gl.glEnable(GL.GL_NORMALIZE); + //gl.glEnable(GL.GL_AUTO_NORMAL); // I think this is OpenGL 1.2 only + //gl.glEnable(GL.GL_RESCALE_NORMAL); + //gl.GlLightModeli(GL.GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); + + report("bot beginDraw()"); + // are there other things to do here? + //System.out.println("beginDraw() stop error " + PApplet.hex(gl.glGetError())); + } + + + public void endDraw() { + //System.out.println("endDraw() error " + PApplet.hex(gl.glGetError())); + + report("top endDraw()"); + + if (hints[ENABLE_DEPTH_SORT]) { + flush(); + } + + if (drawable != null) { + drawable.swapBuffers(); + } + + //insideDraw = false; + report("bot endDraw()"); + + if (drawable != null) { + releaseContext(); + } + } + + + private float ctm[]; + + // this would also need to set up the lighting.. ? + public GL beginGL() { + //beginDraw(); // frame will have already started + gl.glPushMatrix(); + + // load p5 modelview into the opengl modelview + if (ctm == null) ctm = new float[16]; + + ctm[0] = modelview.m00; + ctm[1] = modelview.m10; + ctm[2] = modelview.m20; + ctm[3] = modelview.m30; + + ctm[4] = modelview.m01; + ctm[5] = modelview.m11; + ctm[6] = modelview.m21; + ctm[7] = modelview.m31; + + ctm[8] = modelview.m02; + ctm[9] = modelview.m12; + ctm[10] = modelview.m22; + ctm[11] = modelview.m32; + + ctm[12] = modelview.m03; + ctm[13] = modelview.m13; + ctm[14] = modelview.m23; + ctm[15] = modelview.m33; + + // apply this modelview and get to work + gl.glMultMatrixf(ctm, 0); + + return gl; + } + + + public void endGL() { + // remove the p5 modelview from opengl + gl.glPopMatrix(); + } + + + + //////////////////////////////////////////////////////////// + + // SETTINGS + + // checkSettings, defaultSettings, reapplySettings in PGraphics + + + + //////////////////////////////////////////////////////////// + + // HINTS + + + public void hint(int which) { + // make note of whether these are set, if they are, + // then will prevent the new renderer exception from being thrown. + boolean opengl2X = !hints[DISABLE_OPENGL_2X_SMOOTH]; + boolean opengl4X = hints[ENABLE_OPENGL_4X_SMOOTH]; + super.hint(which); + + if (which == DISABLE_DEPTH_TEST) { + gl.glDisable(GL.GL_DEPTH_TEST); + gl.glClear(GL.GL_DEPTH_BUFFER_BIT); + + } else if (which == ENABLE_DEPTH_TEST) { + gl.glEnable(GL.GL_DEPTH_TEST); + + } else if (which == DISABLE_OPENGL_2X_SMOOTH) { + if (opengl2X) { + releaseContext(); + context.destroy(); + context = null; + allocate(); + throw new PApplet.RendererChangeException(); + } + + } else if (which == ENABLE_OPENGL_2X_SMOOTH) { + // do nothing, this is the default in release 0158 and later + + } else if (which == ENABLE_OPENGL_4X_SMOOTH) { + if (!opengl4X) { + releaseContext(); + context.destroy(); + context = null; + allocate(); + throw new PApplet.RendererChangeException(); + } + } + } + + + + ////////////////////////////////////////////////////////////// + + // VERTEX SHAPES + + // All picked up from either PGraphics or PGraphics3D + + + //public void beginShape() + //public void beginShape(int kind) + //public void edge(boolean e) + //public void normal(float nx, float ny, float nz) + //public void textureMode(int mode) + //public void texture(PImage image) + //public void vertex(float x, float y) + //public void vertex(float x, float y, float z) + //public void vertex(float x, float y, float u, float v) + //public void vertex(float x, float y, float z, float u, float v) + //protected void vertexTexture(float u, float v); + //public void breakShape() + //public void endShape() + //public void endShape(int mode) + + + protected void endShapeLighting(boolean lights) { + super.endShapeLighting(lights); + + // For now do our own lighting--sum the specular and diffuse light colors + if (lights) { + for (int i = shapeFirst; i < shapeLast; i++) { + float v[] = vertices[i]; + v[R] = clamp(v[R] + v[SPR]); + v[G] = clamp(v[G] + v[SPG]); + v[B] = clamp(v[B] + v[SPB]); + } + } + } + + + + ////////////////////////////////////////////////////////////// + + // BEZIER CURVE VERTICES + + // All picked up from either PGraphics or PGraphics3D, however + // a faster version that made use of OpenGL's evaluator methods + // would be a nice improvement. + + + //protected void bezierVertexCheck(); + //public void bezierVertex(float x2, float y2, + // float x3, float y3, + // float x4, float y4) + //public void bezierVertex(float x2, float y2, float z2, + // float x3, float y3, float z3, + // float x4, float y4, float z4) + + + + ////////////////////////////////////////////////////////////// + + // CATMULL-ROM CURVE VERTICES + + // Like bezier, these could be implemented using an OpenGL evaluator. + + + //protected void curveVertexCheck(); + //public void curveVertex(float x, float y) + //public void curveVertex(float x, float y, float z) + //protected void curveVertexSegment(float x1, float y1, + // float x2, float y2, + // float x3, float y3, + // float x4, float y4) + //protected void curveVertexSegment(float x1, float y1, float z1, + // float x2, float y2, float z2, + // float x3, float y3, float z3, + // float x4, float y4, float z4) + + + + ////////////////////////////////////////////////////////////// + + // POINTS (override from P3D) + + + protected void renderPoints(int start, int stop) { + gl.glBegin(GL.GL_POINTS); + float sw = vertices[lines[start][VERTEX1]][SW]; + if (sw > 0) { + gl.glPointSize(sw); // can only be set outside glBegin/glEnd + for (int i = start; i < stop; i++) { + float[] a = vertices[points[i][VERTEX1]]; + + gl.glColor4f(a[SR], a[SG], a[SB], a[SA]); + gl.glVertex3f(a[VX], a[VY], a[VZ]); + } + } + gl.glEnd(); + } + + + //protected void rawPoints(int start, int stop) // PGraphics3D + + + + ////////////////////////////////////////////////////////////// + + // LINES (override from P3D) + + + //protected final void addLineBreak() // PGraphics3D + + + /** + * Add this line, but disable clipping because GL will handle it. + */ + protected void addLine(int a, int b) { + addLineWithoutClip(a, b); + } + + + //protected final void addLineWithClip(int a, int b) + + + //protected final void addLineWithoutClip(int a, int b) + + + /** + * In the current implementation, start and stop are ignored (in OpenGL). + * This will obviously have to be revisited if/when proper depth sorting + * is implemented. + */ + protected void renderLines(int start, int stop) { + report("render_lines in"); + + //int i = 0; + for (int j = 0; j < pathCount; j++) { + int i = pathOffset[j]; + float sw = vertices[lines[i][VERTEX1]][SW]; + //report("render_lines 1"); + // stroke weight zero will cause a gl error + if (sw > 0) { + // glLineWidth has to occur outside glBegin/glEnd + gl.glLineWidth(sw); + gl.glBegin(GL.GL_LINE_STRIP); + + // always draw a first point + float a[] = vertices[lines[i][VERTEX1]]; + gl.glColor4f(a[SR], a[SG], a[SB], a[SA]); + gl.glVertex3f(a[VX], a[VY], a[VZ]); + + // on this and subsequent lines, only draw the second point + //System.out.println(pathLength[j]); + for (int k = 0; k < pathLength[j]; k++) { + float b[] = vertices[lines[i][VERTEX2]]; + gl.glColor4f(b[SR], b[SG], b[SB], b[SA]); + //gl.glEdgeFlag(a[EDGE] == 1); + gl.glVertex3f(b[VX], b[VY], b[VZ]); + i++; + } + gl.glEnd(); + } + } + report("render_lines out"); + } + + + //protected void rawLines(int start, int stop) + + + + ////////////////////////////////////////////////////////////// + + // TRIANGLES + + + /** + * Add the triangle, but disable clipping because GL will handle it. + */ + protected void addTriangle(int a, int b, int c) { + addTriangleWithoutClip(a, b, c); + } + + + protected void renderTriangles(int start, int stop) { + report("render_triangles in"); + + for (int i = start; i < stop; i++) { + float a[] = vertices[triangles[i][VERTEX1]]; + float b[] = vertices[triangles[i][VERTEX2]]; + float c[] = vertices[triangles[i][VERTEX3]]; + + // This is only true when not textured. + // We really should pass specular straight through to triangle rendering. + float ar = clamp(triangleColors[i][0][TRI_DIFFUSE_R] + triangleColors[i][0][TRI_SPECULAR_R]); + float ag = clamp(triangleColors[i][0][TRI_DIFFUSE_G] + triangleColors[i][0][TRI_SPECULAR_G]); + float ab = clamp(triangleColors[i][0][TRI_DIFFUSE_B] + triangleColors[i][0][TRI_SPECULAR_B]); + float br = clamp(triangleColors[i][1][TRI_DIFFUSE_R] + triangleColors[i][1][TRI_SPECULAR_R]); + float bg = clamp(triangleColors[i][1][TRI_DIFFUSE_G] + triangleColors[i][1][TRI_SPECULAR_G]); + float bb = clamp(triangleColors[i][1][TRI_DIFFUSE_B] + triangleColors[i][1][TRI_SPECULAR_B]); + float cr = clamp(triangleColors[i][2][TRI_DIFFUSE_R] + triangleColors[i][2][TRI_SPECULAR_R]); + float cg = clamp(triangleColors[i][2][TRI_DIFFUSE_G] + triangleColors[i][2][TRI_SPECULAR_G]); + float cb = clamp(triangleColors[i][2][TRI_DIFFUSE_B] + triangleColors[i][2][TRI_SPECULAR_B]); + + int textureIndex = triangles[i][TEXTURE_INDEX]; + if (textureIndex != -1) { + report("before enable"); + gl.glEnable(GL.GL_TEXTURE_2D); + report("after enable"); + + report("before bind"); + PImage texture = textures[textureIndex]; + bindTexture(texture); + report("after bind"); + + ImageCache cash = (ImageCache) texture.getCache(this); + float uscale = (float) texture.width / (float) cash.twidth; + float vscale = (float) texture.height / (float) cash.theight; + + gl.glBegin(GL.GL_TRIANGLES); + + //System.out.println(a[U] + " " + a[V] + " " + uscale + " " + vscale); + //System.out.println(ar + " " + ag + " " + ab + " " + a[A]); + //ar = ag = ab = 1; + gl.glColor4f(ar, ag, ab, a[A]); + gl.glTexCoord2f(a[U] * uscale, a[V] * vscale); + gl.glNormal3f(a[NX], a[NY], a[NZ]); + gl.glEdgeFlag(a[EDGE] == 1); + gl.glVertex3f(a[VX], a[VY], a[VZ]); + + gl.glColor4f(br, bg, bb, b[A]); + gl.glTexCoord2f(b[U] * uscale, b[V] * vscale); + gl.glNormal3f(b[NX], b[NY], b[NZ]); + gl.glEdgeFlag(a[EDGE] == 1); + gl.glVertex3f(b[VX], b[VY], b[VZ]); + + gl.glColor4f(cr, cg, cb, c[A]); + gl.glTexCoord2f(c[U] * uscale, c[V] * vscale); + gl.glNormal3f(c[NX], c[NY], c[NZ]); + gl.glEdgeFlag(a[EDGE] == 1); + gl.glVertex3f(c[VX], c[VY], c[VZ]); + + gl.glEnd(); + + report("non-binding 6"); + + gl.glDisable(GL.GL_TEXTURE_2D); + + } else { // no texture + gl.glBegin(GL.GL_TRIANGLES); + + gl.glColor4f(ar, ag, ab, a[A]); + gl.glNormal3f(a[NX], a[NY], a[NZ]); + gl.glEdgeFlag(a[EDGE] == 1); + gl.glVertex3f(a[VX], a[VY], a[VZ]); + + gl.glColor4f(br, bg, bb, b[A]); + gl.glNormal3f(b[NX], b[NY], b[NZ]); + gl.glEdgeFlag(a[EDGE] == 1); + gl.glVertex3f(b[VX], b[VY], b[VZ]); + + gl.glColor4f(cr, cg, cb, c[A]); + gl.glNormal3f(c[NX], c[NY], c[NZ]); + gl.glEdgeFlag(a[EDGE] == 1); + gl.glVertex3f(c[VX], c[VY], c[VZ]); + + gl.glEnd(); + } + } + triangleCount = 0; + report("render_triangles out"); + } + + + //protected void rawTriangles(int start, int stop) // PGraphics3D + + + protected void bindTexture(PImage texture) { + ImageCache cash = (ImageCache) texture.getCache(this); // as in johnny + if (cash == null) { + cash = new ImageCache(); + texture.setCache(this, cash); + texture.setModified(true); + } + + if (texture.isModified()) { + //System.out.println("texture modified"); + // TODO make this more efficient and just update a sub-part + // based on mx1 et al, also use gl function to update + // only a sub-portion of the image. + cash.rebind(texture); + // clear the modified flag + texture.setModified(false); + + } else { + gl.glBindTexture(GL.GL_TEXTURE_2D, cash.tindex); + } + } + + + protected class ImageCache { + int tindex = -1; // not yet ready + int tpixels[]; + IntBuffer tbuffer; + public int twidth, theight; + + int[] tp; + + + /** + * Delete any texture memory that had been allocated. + * Added for 0125 to deal with memory problems reported in Bug #150. + */ + protected void finalize() { + if (textureDeleteQueue.length == textureDeleteQueueCount) { + textureDeleteQueue = (int[]) PApplet.expand(textureDeleteQueue); + } + if (tindex != -1) { + textureDeleteQueue[textureDeleteQueueCount++] = tindex; + } + } + + + /** + * Generate a texture ID and do the necessary bitshifting for the image. + */ + public void rebind(PImage source) { + if (textureDeleteQueueCount != 0) { + //gl.glDeleteTextures(1, new int[] { tindex }, 0); + gl.glDeleteTextures(textureDeleteQueueCount, textureDeleteQueue, 0); + textureDeleteQueueCount = 0; + } + + //System.out.println("rebinding texture for " + source); + if (tindex != -1) { + // free up the old memory + gl.glDeleteTextures(1, new int[] { tindex }, 0); + } + // generate a new texture number to bind to + int[] tmp = new int[1]; + gl.glGenTextures(1, tmp, 0); + tindex = tmp[0]; + //System.out.println("got index " + tindex); + + // bit shifting this might be more efficient + int width2 = nextPowerOfTwo(source.width); + //(int) Math.pow(2, Math.ceil(Math.log(source.width) / Math.log(2))); + int height2 = nextPowerOfTwo(source.height); + //(int) Math.pow(2, Math.ceil(Math.log(source.height) / Math.log(2))); + + // use glGetIntegerv with the argument GL_MAX_TEXTURE_SIZE + // to figure out min/max texture sizes + if (maxTextureSize == 0) { + int maxSize[] = new int[1]; + gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, maxSize, 0); + maxTextureSize = maxSize[0]; + //System.out.println("max texture size is " + maxTextureSize); + } + if ((width2 > maxTextureSize) || (height2 > maxTextureSize)) { + throw new RuntimeException("Image width and height cannot be" + + " larger than " + maxTextureSize + + " with your graphics card."); + } + + if ((width2 > twidth) || (height2 > theight)) { + // either twidth/theight are zero, or size has changed + tpixels = null; + } + if (tpixels == null) { + twidth = width2; + theight = height2; + tpixels = new int[twidth * theight]; + tbuffer = BufferUtil.newIntBuffer(twidth * theight); + } + + // copy image data into the texture + int p = 0; + int t = 0; + + if (BIG_ENDIAN) { + switch (source.format) { + case ALPHA: + for (int y = 0; y < source.height; y++) { + for (int x = 0; x < source.width; x++) { + tpixels[t++] = 0xFFFFFF00 | source.pixels[p++]; + } + t += twidth - source.width; + } + break; + + case RGB: + for (int y = 0; y < source.height; y++) { + for (int x = 0; x < source.width; x++) { + int pixel = source.pixels[p++]; + tpixels[t++] = (pixel << 8) | 0xff; + } + t += twidth - source.width; + } + break; + + case ARGB: + for (int y = 0; y < source.height; y++) { + for (int x = 0; x < source.width; x++) { + int pixel = source.pixels[p++]; + tpixels[t++] = (pixel << 8) | ((pixel >> 24) & 0xff); + } + t += twidth - source.width; + } + break; + } + + } else { // LITTLE_ENDIAN + // ARGB native, and RGBA opengl means ABGR on windows + // for the most part just need to swap two components here + // the sun.cpu.endian here might be "false", oddly enough.. + // (that's why just using an "else", rather than check for "little") + + switch (source.format) { + case ALPHA: + for (int y = 0; y < source.height; y++) { + for (int x = 0; x < source.width; x++) { + tpixels[t++] = (source.pixels[p++] << 24) | 0x00FFFFFF; + } + t += twidth - source.width; + } + break; + + case RGB: + for (int y = 0; y < source.height; y++) { + for (int x = 0; x < source.width; x++) { + int pixel = source.pixels[p++]; + // needs to be ABGR, stored in memory xRGB + // so R and B must be swapped, and the x just made FF + tpixels[t++] = + 0xff000000 | // force opacity for good measure + ((pixel & 0xFF) << 16) | + ((pixel & 0xFF0000) >> 16) | + (pixel & 0x0000FF00); + } + t += twidth - source.width; + } + break; + + case ARGB: + for (int y = 0; y < source.height; y++) { + for (int x = 0; x < source.width; x++) { + int pixel = source.pixels[p++]; + // needs to be ABGR stored in memory ARGB + // so R and B must be swapped, A and G just brought back in + tpixels[t++] = + ((pixel & 0xFF) << 16) | + ((pixel & 0xFF0000) >> 16) | + (pixel & 0xFF00FF00); + } + t += twidth - source.width; + } + break; + } + } + tbuffer.put(tpixels); + tbuffer.rewind(); + + // + + gl.glBindTexture(GL.GL_TEXTURE_2D, tindex); + + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); + //gl.glPixelStorei(GL.GL_UNPACK_SWAP_BYTES, 0); + + gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, 4, twidth, theight, + //0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, cash.tpixels); + 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, tbuffer); + + gl.glTexParameterf(GL.GL_TEXTURE_2D, + //GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); + GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); + gl.glTexParameterf(GL.GL_TEXTURE_2D, + //GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); + + // + + /*int err =*/ glu.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, 4, + twidth, theight, + GL.GL_RGBA, + GL.GL_UNSIGNED_BYTE, tbuffer); + //System.out.println("mipmap: " + err); + + // The MAG_FILTER should only be GL_LINEAR or GL_NEAREST. + // Some cards are OK with LINEAR_MIPMAP_LINEAR, but not the + // Radeon 9700, which is in all the PB G4s.. Not sure if this + // is an OpenGL version thing, tho it makes sense MIN_FILTER + // is the only one that uses mipmapping. + gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, + GL.GL_LINEAR); + gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, + GL.GL_LINEAR_MIPMAP_LINEAR); + +// gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP); +// gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP); + gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); + gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); + + // + + gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); + } + + + private int nextPowerOfTwo(int val) { + int ret = 1; + while (ret < val) { + ret <<= 1; + } + return ret; + } + } + + + ////////////////////////////////////////////////////////////// + + // RENDERING + + + //public void flush() + + + //protected void render() + + + //protected void sort() + + + + ////////////////////////////////////////////////////////////// + + // POINT, LINE, TRIANGLE, QUAD + + // Because vertex(x, y) is mapped to vertex(x, y, 0), none of these commands + // need to be overridden from their default implementation in PGraphics. + + + //public void point(float x, float y) + + + //public void point(float x, float y, float z) + + + //public void line(float x1, float y1, float x2, float y2) + + + //public void line(float x1, float y1, float z1, + // float x2, float y2, float z2) + + + //public void triangle(float x1, float y1, float x2, float y2, + // float x3, float y3) + + + //public void quad(float x1, float y1, float x2, float y2, + // float x3, float y3, float x4, float y4) + + + + ////////////////////////////////////////////////////////////// + + // RECT + + + //public void rectMode(int mode) + + + //public void rect(float a, float b, float c, float d) + + + //protected void rectImpl(float x1, float y1, float x2, float y2) + + + + ////////////////////////////////////////////////////////////// + + // ELLIPSE + + + //public void ellipseMode(int mode) + + + //public void ellipse(float a, float b, float c, float d) + + /* + boolean ellipseInited; + int ellipseFillList; + int ellipseStrokeList; + + protected void ellipseImpl(float x1, float y1, float w, float h) { + float hradius = w / 2f; + float vradius = h / 2f; + + float centerX = x1 + hradius; + float centerY = y1 + vradius; + + // adapt accuracy to radii used w/ a minimum of 4 segments [toxi] + // now uses current scale factors to determine "real" transformed radius + + //int cAccuracy = (int)(4+Math.sqrt(hradius*abs(m00)+vradius*abs(m11))*2); + //int cAccuracy = (int)(4+Math.sqrt(hradius+vradius)*2); + + // notched this up to *3 instead of *2 because things were + // looking a little rough, i.e. the calculate->arctangent example [fry] + + // also removed the m00 and m11 because those were causing weirdness + // need an actual measure of magnitude in there [fry] + + int accuracy = (int)(4+Math.sqrt(hradius+vradius)*3); + //System.out.println("accuracy is " + accuracy); + //accuracy = 5; + + // [toxi031031] adapted to use new lookup tables + float inc = (float)SINCOS_LENGTH / accuracy; + + float val = 0; + + if (fill) { + boolean savedStroke = stroke; + stroke = false; + + beginShape(TRIANGLE_FAN); + normal(0, 0, 1); + vertex(centerX, centerY); + for (int i = 0; i < accuracy; i++) { + vertex(centerX + cosLUT[(int) val] * hradius, + centerY + sinLUT[(int) val] * vradius); + val += inc; + } + // back to the beginning + vertex(centerX + cosLUT[0] * hradius, + centerY + sinLUT[0] * vradius); + endShape(); + + stroke = savedStroke; + } + + if (stroke) { + boolean savedFill = fill; + fill = false; + + val = 0; + beginShape(); //LINE_LOOP); + for (int i = 0; i < accuracy; i++) { + vertex(centerX + cosLUT[(int) val] * hradius, + centerY + sinLUT[(int) val] * vradius); + val += inc; + } + endShape(CLOSE); + + fill = savedFill; + } + } + */ + + /* + pgl.beginGL(); + //PGraphics gr = PApplet.this.g; + //GL gl = ((PGraphicsOpenGL).gr).beginGL(); + if (!ellipseInited) { + ellipseList = gl.glGenLists(1); + gl.glNewList(ellipseList, GL.GL_COMPILE); + gl.glBegin(GL.GL_LINE_LOOP); + int seg = 15; + float segf = 15; + for (int i = 0; i < seg; i++) { + float theta = TWO_PI * (float)i / segf; + gl.glVertex2f(cos(theta), sin(theta)); + } + gl.glEnd(); + gl.glEndList(); + ellipseInited = true; + } + + for (int i=1; i + * Also gets called by textFont, so the metrics + * will get recorded properly. + */ +// public void textSize(float size) { + // can't cancel on textMode(SHAPE) because textMode() must happen + // after textFont() and textFont() calls textSize() + //if ((textMode != SHAPE) || (textFontNative == null)) { + + // call this anyway to set the base variables for cases + // where textMode(SHAPE) will not be used +// super.textSize(size); + + /* + // derive the font just in case the user is gonna call + // textMode(SHAPE) afterwards + if (textFontNative != null) { + textFontNative = textFontNative.deriveFont(size); + Graphics2D graphics = (Graphics2D) parent.getGraphics(); + graphics.setFont(textFontNative); + + // get the metrics info + textFontNativeMetrics = graphics.getFontMetrics(textFontNative); + } + */ +// } + + + //public float textWidth(char c) + + + //public float textWidth(String str) + + + protected float textWidthImpl(char buffer[], int start, int stop) { + Font font = textFont.getFont(); + if ((textMode != SHAPE) || (font == null)) { + return super.textWidthImpl(buffer, start, stop); + } + + /* + // maybe should use one of the newer/fancier functions for this? + int length = stop - start; + return textFontNativeMetrics.charsWidth(buffer, start, length); + */ + Graphics2D graphics = (Graphics2D) parent.getGraphics(); + // otherwise smaller sizes will be totally crapped up + // seems to need to be before the getFRC, but after the canvas.getGraphics + // (placing this inside textSize(), even though it was called, wasn't working) + graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + + FontRenderContext frc = graphics.getFontRenderContext(); + GlyphVector gv; + + /* + if (start == 0 && stop == buffer.length) { + gv = textFontNative.createGlyphVector(frc, buffer); + } else { + char[] fellas = PApplet.subset(buffer, start, length); + gv = textFontNative.createGlyphVector(frc, fellas); + } + */ + gv = font.createGlyphVector(frc, buffer); + float sum = 0; + for (int i = start; i < stop; i++) { + GlyphMetrics gm = gv.getGlyphMetrics(i); + sum += gm.getAdvance(); + } + return sum; + } + + + + ////////////////////////////////////////////////////////////// + + // TEXT + + // None of the variations of text() are overridden from PGraphics. + + + + ////////////////////////////////////////////////////////////// + + // TEXT IMPL + + + //protected void textLineAlignImpl(char buffer[], int start, int stop, + // float x, float y) + + + //protected void textLineImpl(char buffer[], int start, int stop, + // float x, float y) + + + /** + * Override to handle rendering characters with textMode(SHAPE). + */ + protected void textCharImpl(char ch, float x, float y) { + if (textMode == SHAPE) { + if (textFont.getFont() == null) { + PGraphics.showWarning("textMode(SHAPE) is disabled because the font " + + "\"" + textFont.name + "\" is not available."); + } else { + textCharShapeImpl(ch, x, y); + } + } else { + super.textCharImpl(ch, x, y); + } + } + + + /** + * This uses the tesselation functions from GLU to handle triangulation + * to convert the character into a series of shapes. + *

+ * No attempt has been made to optimize this code + *

+ * TODO: Should instead override textPlacedImpl() because createGlyphVector + * takes a char array. Or better yet, cache the font on a per-char basis, + * so that it's not being re-tessellated each time, could make it into + * a display list which would be nice and speedy. + *

+ * Also a problem where some fonts seem to be a bit slight, as if the + * control points aren't being mapped quite correctly. Probably doing + * something dumb that the control points don't map to P5's control + * points. Perhaps it's returning b-spline data from the TrueType font? + * Though it seems like that would make a lot of garbage rather than + * just a little flattening. + *

+ * There also seems to be a bug that is causing a line (but not a filled + * triangle) back to the origin on some letters (i.e. a capital L when + * tested with Akzidenz Grotesk Light). But this won't be visible + * with the stroke shut off, so tabling that bug for now. + */ + protected void textCharShapeImpl(char ch, float x, float y) { + // save the current stroke because it needs to be disabled + // while the text is being drawn + boolean strokeSaved = stroke; + stroke = false; + + // six element array received from the Java2D path iterator + float textPoints[] = new float[6]; + + // array passed to createGylphVector + char textArray[] = new char[] { ch }; + + Graphics2D graphics = (Graphics2D) parent.getGraphics(); + FontRenderContext frc = graphics.getFontRenderContext(); + Font font = textFont.getFont(); + GlyphVector gv = font.createGlyphVector(frc, textArray); + Shape shp = gv.getOutline(); + //PathIterator iter = shp.getPathIterator(null, 0.05); + PathIterator iter = shp.getPathIterator(null); + + glu.gluTessBeginPolygon(tobj, null); + // second param to gluTessVertex is for a user defined object that contains + // additional info about this point, but that's not needed for anything + + float lastX = 0; + float lastY = 0; + + // unfortunately the tesselator won't work properly unless a + // new array of doubles is allocated for each point. that bites ass, + // but also just reaffirms that in order to make things fast, + // display lists will be the way to go. + double vertex[]; + + final boolean DEBUG_OPCODES = false; //true; + + while (!iter.isDone()) { + int type = iter.currentSegment(textPoints); + switch (type) { + case PathIterator.SEG_MOVETO: // 1 point (2 vars) in textPoints + case PathIterator.SEG_LINETO: // 1 point + if (type == PathIterator.SEG_MOVETO) { + if (DEBUG_OPCODES) { + System.out.println("moveto\t" + + textPoints[0] + "\t" + textPoints[1]); + } + glu.gluTessBeginContour(tobj); + } else { + if (DEBUG_OPCODES) { + System.out.println("lineto\t" + + textPoints[0] + "\t" + textPoints[1]); + } + } + vertex = new double[] { + x + textPoints[0], y + textPoints[1], 0 + }; + glu.gluTessVertex(tobj, vertex, 0, vertex); + lastX = textPoints[0]; + lastY = textPoints[1]; + break; + + case PathIterator.SEG_QUADTO: // 2 points + if (DEBUG_OPCODES) { + System.out.println("quadto\t" + + textPoints[0] + "\t" + textPoints[1] + "\t" + + textPoints[2] + "\t" + textPoints[3]); + } + + for (int i = 1; i < bezierDetail; i++) { + float t = (float)i / (float)bezierDetail; + vertex = new double[] { + x + bezierPoint(lastX, textPoints[0], + textPoints[2], textPoints[2], t), + y + bezierPoint(lastY, textPoints[1], + textPoints[3], textPoints[3], t), 0 + }; + glu.gluTessVertex(tobj, vertex, 0, vertex); + } + + lastX = textPoints[2]; + lastY = textPoints[3]; + break; + + case PathIterator.SEG_CUBICTO: // 3 points + if (DEBUG_OPCODES) { + System.out.println("cubicto\t" + + textPoints[0] + "\t" + textPoints[1] + "\t" + + textPoints[2] + "\t" + textPoints[3] + "\t" + + textPoints[4] + "\t" + textPoints[5]); + } + + for (int i = 1; i < bezierDetail; i++) { + float t = (float)i / (float)bezierDetail; + vertex = new double[] { + x + bezierPoint(lastX, textPoints[0], + textPoints[2], textPoints[4], t), + y + bezierPoint(lastY, textPoints[1], + textPoints[3], textPoints[5], t), 0 + }; + glu.gluTessVertex(tobj, vertex, 0, vertex); + } + + lastX = textPoints[4]; + lastY = textPoints[5]; + break; + + case PathIterator.SEG_CLOSE: + if (DEBUG_OPCODES) { + System.out.println("close"); + System.out.println(); + } + glu.gluTessEndContour(tobj); + break; + } + iter.next(); + } + glu.gluTessEndPolygon(tobj); + + // re-enable stroke if it was in use before + stroke = strokeSaved; + } + + + /** + * There must be a better way to do this, but I'm having a brain fart + * with all the inner class crap. Fix it later once this stuff is debugged. + *

+ * The method "void vertex(float $1, float $2, float $3);" contained in + * the enclosing type "processing.core.PGraphics3" is a perfect match for + * this method call. However, it is not visible in this nested class because + * a method with the same name in an intervening class is hiding it. + */ + /* + public void vertexRedirect(float x, float y, float z) { + vertex(x, y, z); + } + */ + + + public class TessCallback extends GLUtessellatorCallbackAdapter { + public void begin(int type) { + switch (type) { + case GL.GL_TRIANGLE_FAN: beginShape(TRIANGLE_FAN); break; + case GL.GL_TRIANGLE_STRIP: beginShape(TRIANGLE_STRIP); break; + case GL.GL_TRIANGLES: beginShape(TRIANGLES); break; + } + } + + public void end() { + //gl.glEnd(); + endShape(); + } + + public void edge(boolean e) { + PGraphicsOpenGL.this.edge(e); + } + + public void vertex(Object data) { + if (data instanceof double[]) { + double[] d = (double[]) data; + if (d.length != 3) { + throw new RuntimeException("TessCallback vertex() data " + + "isn't length 3"); + } + //System.out.println("tess callback vertex " + + // d[0] + " " + d[1] + " " + d[2]); + //vertexRedirect((float) d[0], (float) d[1], (float) d[2]); + PGraphicsOpenGL.this.vertex((float) d[0], (float) d[1], (float) d[2]); + /* + if (d.length == 6) { + double[] d2 = {d[0], d[1], d[2]}; + gl.glVertex3dv(d2); + d2 = new double[]{d[3], d[4], d[5]}; + gl.glColor3dv(d2); + } else if (d.length == 3) { + gl.glVertex3dv(d); + } + */ + } else { + throw new RuntimeException("TessCallback vertex() data not understood"); + } + } + + public void error(int errnum) { + String estring = glu.gluErrorString(errnum); + PGraphics.showWarning("Tessellation Error: " + estring); + } + + /** + * Implementation of the GLU_TESS_COMBINE callback. + * @param coords is the 3-vector of the new vertex + * @param data is the vertex data to be combined, up to four elements. + * This is useful when mixing colors together or any other + * user data that was passed in to gluTessVertex. + * @param weight is an array of weights, one for each element of "data" + * that should be linearly combined for new values. + * @param outData is the set of new values of "data" after being + * put back together based on the weights. it's passed back as a + * single element Object[] array because that's the closest + * that Java gets to a pointer. + */ + public void combine(double[] coords, Object[] data, + float[] weight, Object[] outData) { + //System.out.println("coords.length = " + coords.length); + //System.out.println("data.length = " + data.length); + //System.out.println("weight.length = " + weight.length); + //for (int i = 0; i < data.length; i++) { + //System.out.println(i + " " + data[i].getClass().getName() + " " + weight[i]); + //} + + double[] vertex = new double[coords.length]; + vertex[0] = coords[0]; + vertex[1] = coords[1]; + vertex[2] = coords[2]; + //System.out.println("combine " + + // vertex[0] + " " + vertex[1] + " " + vertex[2]); + + // this is just 3, so nothing interesting to bother combining + //System.out.println("data length " + ((double[]) data[0]).length); + + // not gonna bother doing any combining, + // since no user data is being passed in. + /* + for (int i = 3; i < 6; i++) { + vertex[i] = + weight[0] * ((double[]) data[0])[i] + + weight[1] * ((double[]) data[1])[i] + + weight[2] * ((double[]) data[2])[i] + + weight[3] * ((double[]) data[3])[i]; + } + */ + outData[0] = vertex; + } + } + + + + ////////////////////////////////////////////////////////////// + + // MATRIX MATH + + //public void pushMatrix() + //public void popMatrix() + + //public void translate(float tx, float ty) + //public void translate(float tx, float ty, float tz) + //public void rotate(float angle) + //public void rotateX(float angle) + //public void rotateY(float angle) + //public void rotateZ(float angle) + //public void rotate(float angle, float vx, float vy, float vz) + //public void scale(float s) + //public void scale(float sx, float sy) + //public void scale(float x, float y, float z) + + //public void resetMatrix() + //public void applyMatrix(PMatrix2D source) + //public void applyMatrix(float n00, float n01, float n02, + // float n10, float n11, float n12) + //public void applyMatrix(PMatrix3D source) + //public void applyMatrix(float n00, float n01, float n02, float n03, + // float n10, float n11, float n12, float n13, + // float n20, float n21, float n22, float n23, + // float n30, float n31, float n32, float n33) + + //public getMatrix(PMatrix2D target) + //public getMatrix(PMatrix3D target) + //public void setMatrix(PMatrix2D source) + //public void setMatrix(PMatrix3D source) + //public void printMatrix() + + //public void beginCamera() + //public void endCamera() + //public void camera() + //public void camera(float eyeX, float eyeY, float eyeZ, + // float centerX, float centerY, float centerZ, + // float upX, float upY, float upZ) + //public void printCamera() + + //public void ortho() + //public void ortho(float left, float right, + // float bottom, float top, + // float near, float far) + //public void perspective() + //public void perspective(float fov, float aspect, float near, float far) + //public void frustum(float left, float right, + // float bottom, float top, + // float near, float far) + //public void printProjection() + + //public float screenX(float x, float y) + //public float screenY(float x, float y) + //public float screenX(float x, float y, float z) + //public float screenY(float x, float y, float z) + //public float screenZ(float x, float y, float z) + //public float modelX(float x, float y, float z) + //public float modelY(float x, float y, float z) + //public float modelZ(float x, float y, float z) + + + + ////////////////////////////////////////////////////////////// + + // STYLES + + + //public void pushStyle() + //public void popStyle() + //public void style(PStyle) + //public PStyle getStyle() + //public void getStyle(PStyle) + + + + ////////////////////////////////////////////////////////////// + + // COLOR MODE + + + //public void colorMode(int mode) + //public void colorMode(int mode, float max) + //public void colorMode(int mode, float mx, float my, float mz); + //public void colorMode(int mode, float mx, float my, float mz, float ma); + + + + ////////////////////////////////////////////////////////////// + + // COLOR CALC + + + //protected void colorCalc(int rgb) + //protected void colorCalc(int rgb, float alpha) + //protected void colorCalc(float gray) + //protected void colorCalc(float gray, float alpha) + //protected void colorCalc(float x, float y, float z) + //protected void colorCalc(float x, float y, float z, float a) + //protected void colorCalcARGB(int argb, float alpha) + + + + ////////////////////////////////////////////////////////////// + + // STROKE CAP/JOIN/WEIGHT + + + public void strokeWeight(float weight) { + this.strokeWeight = weight; + } + + + public void strokeJoin(int join) { + if (join != DEFAULT_STROKE_JOIN) { + showMethodWarning("strokeJoin"); + } + } + + + public void strokeCap(int cap) { + if (cap != DEFAULT_STROKE_CAP) { + showMethodWarning("strokeCap"); + } + } + + + + ////////////////////////////////////////////////////////////// + + // STROKE, TINT, FILL + + + //public void noStroke() + //public void stroke(int rgb) + //public void stroke(int rgb, float alpha) + //public void stroke(float gray) + //public void stroke(float gray, float alpha) + //public void stroke(float x, float y, float z) + //public void stroke(float x, float y, float z, float a) + //protected void strokeFromCalc() + + //public void noTint() + //public void tint(int rgb) + //public void tint(int rgb, float alpha) + //public void tint(float gray) + //public void tint(float gray, float alpha) + //public void tint(float x, float y, float z) + //public void tint(float x, float y, float z, float a) + //protected void tintFromCalc() + + //public void noFill() + //public void fill(int rgb) + //public void fill(int rgb, float alpha) + //public void fill(float gray) + //public void fill(float gray, float alpha) + //public void fill(float x, float y, float z) + //public void fill(float x, float y, float z, float a) + + + protected void fillFromCalc() { + super.fillFromCalc(); + calcColorBuffer(); + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE, + colorBuffer, 0); + } + + + + ////////////////////////////////////////////////////////////// + + // MATERIAL PROPERTIES + + +// public void ambient(int rgb) { +// super.ambient(rgb); +// calcColorBuffer(); +// gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, colorBuffer, 0); +// } + + +// public void ambient(float gray) { +// super.ambient(gray); +// calcColorBuffer(); +// gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, colorBuffer, 0); +// } + + +// public void ambient(float x, float y, float z) { +// super.ambient(x, y, z); +// calcColorBuffer(); +// gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, colorBuffer, 0); +// } + + + protected void ambientFromCalc() { + calcColorBuffer(); + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, colorBuffer, 0); + } + + +// public void specular(int rgb) { +// super.specular(rgb); +// calcColorBuffer(); +// gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, colorBuffer, 0); +// } + + +// public void specular(float gray) { +// super.specular(gray); +// calcColorBuffer(); +// gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, colorBuffer, 0); +// } + + +// public void specular(float x, float y, float z) { +// super.specular(x, y, z); +// calcColorBuffer(); +// gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, colorBuffer, 0); +// } + + + protected void specularFromCalc() { + calcColorBuffer(); + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, colorBuffer, 0); + } + + + public void shininess(float shine) { + super.shininess(shine); + gl.glMaterialf(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, shine); + } + + +// public void emissive(int rgb) { +// super.emissive(rgb); +// calcColorBuffer(); +// gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_EMISSION, colorBuffer, 0); +// } + + +// public void emissive(float gray) { +// super.emissive(gray); +// calcColorBuffer(); +// gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_EMISSION, colorBuffer, 0); +// } + + +// public void emissive(float x, float y, float z) { +// super.emissive(x, y, z); +// calcColorBuffer(); +// gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_EMISSION, colorBuffer, 0); +// } + + + protected void emissiveFromCalc() { + calcColorBuffer(); + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_EMISSION, colorBuffer, 0); + } + + + + ////////////////////////////////////////////////////////////// + + // LIGHTING + + // We're not actually turning on GL lights right now + // because our home-grown ones work better for now. + + +// public void lights() { +// super.lights(); +// gl.glEnable(GL.GL_LIGHTING); +// } + + +// public void noLights() { +// super.noLights(); +// gl.glDisable(GL.GL_LIGHTING); +// } + + + public void ambientLight(float r, float g, float b) { + super.ambientLight(r, g, b); + glLightEnable(lightCount - 1); + glLightAmbient(lightCount - 1); + glLightPosition(lightCount - 1); + glLightFalloff(lightCount - 1); + } + + public void ambientLight(float r, float g, float b, + float x, float y, float z) { + super.ambientLight(r, g, b, x, y, z); + glLightEnable(lightCount - 1); + glLightAmbient(lightCount - 1); + glLightPosition(lightCount - 1); + glLightFalloff(lightCount - 1); + } + + + public void directionalLight(float r, float g, float b, + float nx, float ny, float nz) { + super.directionalLight(r, g, b, nx, ny, nz); + glLightEnable(lightCount - 1); + glLightNoAmbient(lightCount - 1); + glLightDirection(lightCount - 1); + glLightDiffuse(lightCount - 1); + glLightSpecular(lightCount - 1); + glLightFalloff(lightCount - 1); + } + + + public void pointLight(float r, float g, float b, + float x, float y, float z) { + super.pointLight(r, g, b, x, y, z); + glLightEnable(lightCount - 1); + glLightNoAmbient(lightCount - 1); + glLightPosition(lightCount - 1); + glLightDiffuse(lightCount - 1); + glLightSpecular(lightCount - 1); + glLightFalloff(lightCount - 1); + } + + + public void spotLight(float r, float g, float b, + float x, float y, float z, + float nx, float ny, float nz, + float angle, float concentration) { + super.spotLight(r, g, b, x, y, z, nx, ny, nz, angle, concentration); + glLightNoAmbient(lightCount - 1); + glLightPosition(lightCount - 1); + glLightDirection(lightCount - 1); + glLightDiffuse(lightCount - 1); + glLightSpecular(lightCount - 1); + glLightFalloff(lightCount - 1); + glLightSpotAngle(lightCount - 1); + glLightSpotConcentration(lightCount - 1); + } + + + public void lightFalloff(float constant, float linear, float quadratic) { + super.lightFalloff(constant, linear, quadratic); + glLightFalloff(lightCount); + } + + + public void lightSpecular(float x, float y, float z) { + super.lightSpecular(x, y, z); + glLightSpecular(lightCount); + } + + + protected void lightPosition(int num, float x, float y, float z) { + super.lightPosition(num, x, y, z); + glLightPosition(num); + } + + + protected void lightDirection(int num, float x, float y, float z) { + super.lightDirection(num, x, y, z); + glLightDirection(num); + } + + + private void glLightAmbient(int num) { +// lightBuffer.put(lightDiffuse[num]); +// lightBuffer.rewind(); +// gl.glLightfv(GL.GL_LIGHT0 + num, +// GL.GL_AMBIENT, lightBuffer); + gl.glLightfv(GL.GL_LIGHT0 + num, + GL.GL_AMBIENT, lightDiffuse[num], 0); + } + + + private void glLightNoAmbient(int num) { + if (zeroBuffer == null) { + // hopefully buffers are filled with zeroes.. + zeroBuffer = BufferUtil.newFloatBuffer(3); + } + gl.glLightfv(GL.GL_LIGHT0 + num, + GL.GL_AMBIENT, zeroBuffer); + } + + + private void glLightDiffuse(int num) { +// lightBuffer.put(lightDiffuse[num]); +// lightBuffer.rewind(); +// gl.glLightfv(GL.GL_LIGHT0 + num, +// GL.GL_DIFFUSE, lightBuffer); + gl.glLightfv(GL.GL_LIGHT0 + num, + GL.GL_DIFFUSE, lightDiffuse[num], 0); + } + + + private void glLightDirection(int num) { +// lightBuffer.put(lightNormal[num]); +// lightBuffer.rewind(); + + if (lightType[num] == DIRECTIONAL) { + // TODO this expects a fourth arg that will be set to 1 + // this is why lightBuffer is length 4, + // and the [3] element set to 1 in the constructor. + // however this may be a source of problems since + // it seems a bit "hack" + gl.glLightfv(GL.GL_LIGHT0 + num, GL.GL_POSITION, + lightNormal[num].array(), 0); + } else { // spotlight + // this one only needs the 3 arg version + gl.glLightfv(GL.GL_LIGHT0 + num, GL.GL_SPOT_DIRECTION, + lightNormal[num].array(), 0); + } + } + + + private void glLightEnable(int num) { + gl.glEnable(GL.GL_LIGHT0 + num); + } + + + private void glLightFalloff(int num) { + gl.glLightf(GL.GL_LIGHT0 + num, + GL.GL_CONSTANT_ATTENUATION, lightFalloffConstant[num]); + gl.glLightf(GL.GL_LIGHT0 + num, + GL.GL_LINEAR_ATTENUATION, lightFalloffLinear[num]); + gl.glLightf(GL.GL_LIGHT0 + num, + GL.GL_QUADRATIC_ATTENUATION, lightFalloffQuadratic[num]); + } + + + private void glLightPosition(int num) { + gl.glLightfv(GL.GL_LIGHT0 + num, GL.GL_POSITION, lightPosition[num].array(), 0); + } + + + private void glLightSpecular(int num) { + gl.glLightfv(GL.GL_LIGHT0 + num, GL.GL_SPECULAR, lightSpecular[num], 0); + } + + + private void glLightSpotAngle(int num) { + gl.glLightf(GL.GL_LIGHT0 + num, + GL.GL_SPOT_CUTOFF, lightSpotAngle[num]); + } + + + private void glLightSpotConcentration(int num) { + gl.glLightf(GL.GL_LIGHT0 + num, + GL.GL_SPOT_EXPONENT, lightSpotConcentration[num]); + } + + + + ////////////////////////////////////////////////////////////// + + // BACKGROUND + + + protected void backgroundImpl(PImage image) { + gl.glClearColor(backgroundR, backgroundG, backgroundB, 1); + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + set(0, 0, image); + } + + + protected void backgroundImpl() { + gl.glClearColor(backgroundR, backgroundG, backgroundB, 1); + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + } + + + + ////////////////////////////////////////////////////////////// + + // COLOR MODE + + // colorMode() is inherited from PGraphics. + + + + ////////////////////////////////////////////////////////////// + + // COLOR CALC + + // This is the OpenGL complement to the colorCalc() methods. + + + /** + * Load the calculated color into a pre-allocated array so that + * it can be quickly passed over to OpenGL. + */ + private final void calcColorBuffer() { + if (colorBuffer == null) { +// colorBuffer = BufferUtil.newFloatBuffer(4); + colorBuffer = new float[4]; + } + colorBuffer[0] = calcR; + colorBuffer[1] = calcG; + colorBuffer[2] = calcB; + colorBuffer[3] = calcA; +// colorBuffer.put(0, calcR); +// colorBuffer.put(1, calcG); +// colorBuffer.put(2, calcB); +// colorBuffer.put(3, calcA); +// colorBuffer.rewind(); + } + + + + ////////////////////////////////////////////////////////////// + + // COLOR METHODS + + //public final int color(int gray) + //public final int color(int gray, int alpha) + //public final int color(int rgb, float alpha) + //public final int color(int x, int y, int z) + + //public final float alpha(int what) + //public final float red(int what) + //public final float green(int what) + //public final float blue(int what) + //public final float hue(int what) + //public final float saturation(int what) + //public final float brightness(int what) + + //public int lerpColor(int c1, int c2, float amt) + //static public int lerpColor(int c1, int c2, float amt, int mode) + + + + ////////////////////////////////////////////////////////////// + + // BEGINRAW/ENDRAW + + // beginRaw, endRaw() both inherited. + + + + ////////////////////////////////////////////////////////////// + + // WARNINGS and EXCEPTIONS + + // showWarning() and showException() available from PGraphics. + + + /** + * Report on anything from glError(). + * Don't use this inside glBegin/glEnd otherwise it'll + * throw an GL_INVALID_OPERATION error. + */ + public void report(String where) { + if (!hints[DISABLE_OPENGL_ERROR_REPORT]) { + int err = gl.glGetError(); + if (err != 0) { + String errString = glu.gluErrorString(err); + String msg = "OpenGL error " + err + " at " + where + ": " + errString; + PGraphics.showWarning(msg); + } + } + } + + + + ////////////////////////////////////////////////////////////// + + // RENDERER SUPPORT QUERIES + + + //public boolean displayable() + + + //public boolean dimensional() // from P3D + + + + ////////////////////////////////////////////////////////////// + + // PIMAGE METHODS + + // getImage + // setCache, getCache, removeCache + // isModified, setModified + + + + ////////////////////////////////////////////////////////////// + + // LOAD/UPDATE PIXELS + + + public void loadPixels() { + if ((pixels == null) || (pixels.length != width*height)) { + pixels = new int[width * height]; + pixelBuffer = BufferUtil.newIntBuffer(pixels.length); + } + + /* + for (int y = 0; y < height; y++) { + // or SKIP_PIXELS with y*width + //gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, (height-1) - y); + gl.glReadPixels(0, y, width, y + 1, + GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pixels); + } + gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, 0); + */ + + gl.glReadPixels(0, 0, width, height, + GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pixelBuffer); + pixelBuffer.get(pixels); + pixelBuffer.rewind(); + + //for (int i = 0; i < 5; i++) { + //System.out.println(PApplet.hex(pixels[i])); + //} + + /* + int temp[] = new int[width]; + // 3 rows, skips the middle + + for (int y = 0; y < height/2; y++) { + int yy = (height - 1) - y; + System.arraycopy(pixels, y*width, temp, 0, width); + System.arraycopy(pixels, yy*width, pixels, y*width, width); + System.arraycopy(temp, 0, pixels, yy*width, width); + } + */ + + /* + // now need to swap the RGBA components to ARGB (big endian) + for (int i = 0; i < pixels.length; i++) { + //pixels[i] = ((pixels[i] & 0xff) << 24) | + pixels[i] = ((pixels[i] << 24) & 0xff) | // safer? + ((pixels[i] >> 8) & 0xffffff); + } + */ + + // flip vertically (opengl stores images upside down), + // and swap RGBA components to ARGB (big endian) + int index = 0; + int yindex = (height - 1) * width; + for (int y = 0; y < height/2; y++) { + if (BIG_ENDIAN) { + for (int x = 0; x < width; x++) { + int temp = pixels[index]; + // ignores alpha component, just sets it opaque + pixels[index] = 0xff000000 | ((pixels[yindex] >> 8) & 0x00ffffff); + pixels[yindex] = 0xff000000 | ((temp >> 8) & 0x00ffffff); + + index++; + yindex++; + } + } else { // LITTLE_ENDIAN, convert ABGR to ARGB + for (int x = 0; x < width; x++) { + int temp = pixels[index]; + + // identical to endPixels because only two + // components are being swapped + pixels[index] = 0xff000000 | + ((pixels[yindex] << 16) & 0xff0000) | + (pixels[yindex] & 0xff00) | + ((pixels[yindex] >> 16) & 0xff); + + pixels[yindex] = 0xff000000 | + ((temp << 16) & 0xff0000) | + (temp & 0xff00) | + ((temp >> 16) & 0xff); + + index++; + yindex++; + } + } + yindex -= width*2; + } + + // When height is an odd number, the middle line needs to be + // endian swapped, but not y-swapped. + // http://dev.processing.org/bugs/show_bug.cgi?id=944 + if ((height % 2) == 1) { + index = (height / 2) * width; + if (BIG_ENDIAN) { + for (int x = 0; x < width; x++) { + // ignores alpha component, just sets it opaque + pixels[index] = 0xff000000 | ((pixels[index] >> 8) & 0x00ffffff); + } + } else { + for (int x = 0; x < width; x++) { + pixels[index] = 0xff000000 | + ((pixels[index] << 16) & 0xff0000) | + (pixels[index] & 0xff00) | + ((pixels[index] >> 16) & 0xff); + } + } + } + } + + + /** + * Convert native OpenGL format into palatable ARGB format. + * This function leaves alone (ignores) the alpha component. + * Also flips the image vertically, since images are upside-down in GL. + */ + static void nativeToJavaRGB(PImage image) { + int index = 0; + int yindex = (image.height - 1) * image.width; + for (int y = 0; y < image.height/2; y++) { + if (BIG_ENDIAN) { + for (int x = 0; x < image.width; x++) { + int temp = image.pixels[index]; + // ignores alpha component, just sets it opaque + image.pixels[index] = + 0xff000000 | ((image.pixels[yindex] >> 8) & 0x00ffffff); + image.pixels[yindex] = + 0xff000000 | ((temp >> 8) & 0x00ffffff); + index++; + yindex++; + } + } else { // LITTLE_ENDIAN, convert ABGR to ARGB + for (int x = 0; x < image.width; x++) { + int temp = image.pixels[index]; + + // identical to endPixels because only two + // components are being swapped + image.pixels[index] = 0xff000000 | + ((image.pixels[yindex] << 16) & 0xff0000) | + (image.pixels[yindex] & 0xff00) | + ((image.pixels[yindex] >> 16) & 0xff); + + image.pixels[yindex] = 0xff000000 | + ((temp << 16) & 0xff0000) | + (temp & 0xff00) | + ((temp >> 16) & 0xff); + + index++; + yindex++; + } + } + yindex -= image.width*2; + } + } + + + /** + * Convert native OpenGL format into palatable ARGB format. + * This function leaves alone (ignores) the alpha component. + * Also flips the image vertically, since images are upside-down in GL. + */ + static void nativeToJavaARGB(PImage image) { + int index = 0; + int yindex = (image.height - 1) * image.width; + for (int y = 0; y < image.height/2; y++) { + if (BIG_ENDIAN) { + for (int x = 0; x < image.width; x++) { + int temp = image.pixels[index]; + // ignores alpha component, just sets it opaque + image.pixels[index] = + (image.pixels[yindex] & 0xff000000) | + ((image.pixels[yindex] >> 8) & 0x00ffffff); + image.pixels[yindex] = + (temp & 0xff000000) | + ((temp >> 8) & 0x00ffffff); + index++; + yindex++; + } + } else { // LITTLE_ENDIAN, convert ABGR to ARGB + for (int x = 0; x < image.width; x++) { + int temp = image.pixels[index]; + + // identical to endPixels because only two + // components are being swapped + image.pixels[index] = + (image.pixels[yindex] & 0xff000000) | + ((image.pixels[yindex] << 16) & 0xff0000) | + (image.pixels[yindex] & 0xff00) | + ((image.pixels[yindex] >> 16) & 0xff); + + image.pixels[yindex] = + (temp & 0xff000000) | + ((temp << 16) & 0xff0000) | + (temp & 0xff00) | + ((temp >> 16) & 0xff); + + index++; + yindex++; + } + } + yindex -= image.width*2; + } + } + + + /** + * Convert ARGB (Java/Processing) data to native OpenGL format. + * This function leaves alone (ignores) the alpha component. + * Also flips the image vertically, since images are upside-down in GL. + */ + static void javaToNativeRGB(PImage image) { + int width = image.width; + int height = image.height; + int pixels[] = image.pixels; + + int index = 0; + int yindex = (height - 1) * width; + for (int y = 0; y < height/2; y++) { + if (BIG_ENDIAN) { + // and convert ARGB back to opengl RGBA components (big endian) + for (int x = 0; x < image.width; x++) { + int temp = pixels[index]; + /* + pixels[index] = + ((pixels[yindex] >> 24) & 0xff) | + ((pixels[yindex] << 8) & 0xffffff00); + pixels[yindex] = + ((temp >> 24) & 0xff) | + ((temp << 8) & 0xffffff00); + */ + pixels[index] = ((pixels[yindex] << 8) & 0xffffff00) | 0xff; + pixels[yindex] = ((temp << 8) & 0xffffff00) | 0xff; + + index++; + yindex++; + } + + } else { + // convert ARGB back to native little endian ABGR + for (int x = 0; x < width; x++) { + int temp = pixels[index]; + + pixels[index] = 0xff000000 | + ((pixels[yindex] << 16) & 0xff0000) | + (pixels[yindex] & 0xff00) | + ((pixels[yindex] >> 16) & 0xff); + + pixels[yindex] = 0xff000000 | + ((temp << 16) & 0xff0000) | + (temp & 0xff00) | + ((temp >> 16) & 0xff); + + index++; + yindex++; + } + } + yindex -= width*2; + } + } + + + /** + * Convert Java ARGB to native OpenGL format. + * Also flips the image vertically, since images are upside-down in GL. + */ + static void javaToNativeARGB(PImage image) { + int width = image.width; + int height = image.height; + int pixels[] = image.pixels; + + int index = 0; + int yindex = (height - 1) * width; + for (int y = 0; y < height/2; y++) { + if (BIG_ENDIAN) { + // and convert ARGB back to opengl RGBA components (big endian) + for (int x = 0; x < image.width; x++) { + int temp = pixels[index]; + pixels[index] = + ((pixels[yindex] >> 24) & 0xff) | + ((pixels[yindex] << 8) & 0xffffff00); + pixels[yindex] = + ((temp >> 24) & 0xff) | + ((temp << 8) & 0xffffff00); + + index++; + yindex++; + } + + } else { + // convert ARGB back to native little endian ABGR + for (int x = 0; x < width; x++) { + int temp = pixels[index]; + + pixels[index] = + (pixels[yindex] & 0xff000000) | + ((pixels[yindex] << 16) & 0xff0000) | + (pixels[yindex] & 0xff00) | + ((pixels[yindex] >> 16) & 0xff); + + pixels[yindex] = + (pixels[yindex] & 0xff000000) | + ((temp << 16) & 0xff0000) | + (temp & 0xff00) | + ((temp >> 16) & 0xff); + + index++; + yindex++; + } + } + yindex -= width*2; + } + } + + + public void updatePixels() { + // flip vertically (opengl stores images upside down), + + int index = 0; + int yindex = (height - 1) * width; + for (int y = 0; y < height/2; y++) { + if (BIG_ENDIAN) { + // and convert ARGB back to opengl RGBA components (big endian) + for (int x = 0; x < width; x++) { + int temp = pixels[index]; + + pixels[index] = ((pixels[yindex] << 8) & 0xffffff00) | 0xff; + pixels[yindex] = ((temp << 8) & 0xffffff00) | 0xff; + + index++; + yindex++; + } + + } else { + // convert ARGB back to native little endian ABGR + for (int x = 0; x < width; x++) { + int temp = pixels[index]; + + pixels[index] = 0xff000000 | + ((pixels[yindex] << 16) & 0xff0000) | + (pixels[yindex] & 0xff00) | + ((pixels[yindex] >> 16) & 0xff); + + pixels[yindex] = 0xff000000 | + ((temp << 16) & 0xff0000) | + (temp & 0xff00) | + ((temp >> 16) & 0xff); + + index++; + yindex++; + } + } + yindex -= width*2; + } + + // re-pack ARGB data into RGBA for opengl (big endian) + //for (int i = 0; i < pixels.length; i++) { + //pixels[i] = ((pixels[i] >> 24) & 0xff) | + //((pixels[i] << 8) & 0xffffff00); + //} + + setRasterPos(0, 0); // lower-left corner + + pixelBuffer.put(pixels); + pixelBuffer.rewind(); + gl.glDrawPixels(width, height, + GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pixelBuffer); + } + + + + ////////////////////////////////////////////////////////////// + + // RESIZE + + + public void resize(int wide, int high) { + PGraphics.showMethodWarning("resize"); + } + + + + ////////////////////////////////////////////////////////////// + + // GET/SET + + + IntBuffer getsetBuffer = BufferUtil.newIntBuffer(1); +// int getset[] = new int[1]; + + public int get(int x, int y) { + gl.glReadPixels(x, y, 1, 1, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, getsetBuffer); +// gl.glReadPixels(x, y, 1, 1, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, getset, 0); + int getset = getsetBuffer.get(0); + + if (BIG_ENDIAN) { + return 0xff000000 | ((getset >> 8) & 0x00ffffff); + + } else { + return 0xff000000 | + ((getset << 16) & 0xff0000) | + (getset & 0xff00) | + ((getset >> 16) & 0xff); + } + } + + + //public PImage get(int x, int y, int w, int h) + + + protected PImage getImpl(int x, int y, int w, int h) { + PImage newbie = new PImage(w, h); //new int[w*h], w, h, ARGB); + + IntBuffer newbieBuffer = BufferUtil.newIntBuffer(w*h); + gl.glReadPixels(x, y, w, h, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, newbieBuffer); + newbieBuffer.get(newbie.pixels); + + nativeToJavaARGB(newbie); + return newbie; + } + + + public PImage get() { + return get(0, 0, width, height); + } + + + public void set(int x, int y, int argb) { + int getset = 0; + + if (BIG_ENDIAN) { + // convert ARGB to RGBA + getset = (argb << 8) | 0xff; + + } else { + // convert ARGB to ABGR + getset = + (argb & 0xff00ff00) | + ((argb << 16) & 0xff0000) | + ((argb >> 16) & 0xff); + } + getsetBuffer.put(0, getset); + getsetBuffer.rewind(); + //gl.glRasterPos2f(x + EPSILON, y + EPSILON); + setRasterPos(x, (height-y) - 1); + gl.glDrawPixels(1, 1, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, getsetBuffer); + } + + + /** + * Set an image directly to the screen. + *

+ * TODO not optimized properly, creates multiple temporary buffers + * the size of the image. Needs to instead use image cache, but that + * requires two types of image cache. One for power of 2 textures + * and another for glReadPixels/glDrawPixels data that's flipped + * vertically. Both have their components all swapped to native. + */ + public void set(int x, int y, PImage source) { + int[] backup = new int[source.pixels.length]; + System.arraycopy(source.pixels, 0, backup, 0, source.pixels.length); + javaToNativeARGB(source); + + // TODO is this possible without intbuffer? + IntBuffer setBuffer = BufferUtil.newIntBuffer(source.pixels.length); + setBuffer.put(source.pixels); + setBuffer.rewind(); + + setRasterPos(x, (height-y) - source.height); //+source.height); + gl.glDrawPixels(source.width, source.height, + GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, setBuffer); + source.pixels = backup; + } + + + // TODO remove the implementation above and use setImpl instead, + // since it'll be more efficient + // http://dev.processing.org/bugs/show_bug.cgi?id=943 + //protected void setImpl(int dx, int dy, int sx, int sy, int sw, int sh, + // PImage src) + + + /** + * Definitive method for setting raster pos, including offscreen locations. + * The raster position is tricky because it's affected by the modelview and + * projection matrices. Further, offscreen coords won't properly set the + * raster position. This code gets around both issues. + * http://www.mesa3d.org/brianp/sig97/gotchas.htm + * @param y the Y-coordinate, which is flipped upside down in OpenGL + */ + protected void setRasterPos(float x, float y) { + float z = 0; + float w = 1; + + float fx, fy; + + // Push current matrix mode and viewport attributes + gl.glPushAttrib(GL.GL_TRANSFORM_BIT | GL.GL_VIEWPORT_BIT); + + // Setup projection parameters + gl.glMatrixMode(GL.GL_PROJECTION); + gl.glPushMatrix(); + gl.glLoadIdentity(); + gl.glMatrixMode(GL.GL_MODELVIEW); + gl.glPushMatrix(); + gl.glLoadIdentity(); + + gl.glDepthRange(z, z); + gl.glViewport((int) x - 1, (int) y - 1, 2, 2); + + // set the raster (window) position + fx = x - (int) x; + fy = y - (int) y; + gl.glRasterPos4f(fx, fy, 0, w); + + // restore matrices, viewport and matrix mode + gl.glPopMatrix(); + gl.glMatrixMode(GL.GL_PROJECTION); + gl.glPopMatrix(); + + gl.glPopAttrib(); + } + + + + ////////////////////////////////////////////////////////////// + + // MASK + + + public void mask(int alpha[]) { + PGraphics.showMethodWarning("mask"); + } + + + public void mask(PImage alpha) { + PGraphics.showMethodWarning("mask"); + } + + + + ////////////////////////////////////////////////////////////// + + // FILTER + + + /** + * This is really inefficient and not a good idea in OpenGL. + * Use get() and set() with a smaller image area, or call the + * filter on an image instead, and then draw that. + */ + public void filter(int kind) { + PImage temp = get(); + temp.filter(kind); + set(0, 0, temp); + } + + + /** + * This is really inefficient and not a good idea in OpenGL. + * Use get() and set() with a smaller image area, or call the + * filter on an image instead, and then draw that. + */ + public void filter(int kind, float param) { + PImage temp = get(); + temp.filter(kind, param); + set(0, 0, temp); + } + + + ////////////////////////////////////////////////////////////// + + + /** + * Extremely slow and not optimized, should use GL methods instead. + * Currently calls a beginPixels() on the whole canvas, then does the copy, + * then it calls endPixels(). + */ + //public void copy(int sx1, int sy1, int sx2, int sy2, + // int dx1, int dy1, int dx2, int dy2) + + + /** + * TODO - extremely slow and not optimized. + * Currently calls a beginPixels() on the whole canvas, + * then does the copy, then it calls endPixels(). + */ + //public void copy(PImage src, + // int sx1, int sy1, int sx2, int sy2, + // int dx1, int dy1, int dx2, int dy2) + + + + ////////////////////////////////////////////////////////////// + + // BLEND + + + //static public int blendColor(int c1, int c2, int mode) + + + public void blend(PImage src, + int sx, int sy, int dx, int dy, int mode) { + set(dx, dy, PImage.blendColor(src.get(sx, sy), get(dx, dy), mode)); + } + + + /** + * Extremely slow and not optimized, should use GL methods instead. + * Currently calls a beginPixels() on the whole canvas, then does the copy, + * then it calls endPixels(). Please help fix: + * Bug 941, + * Bug 942. + */ + public void blend(int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2, int mode) { + loadPixels(); + super.blend(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); + updatePixels(); + } + + + public void blend(PImage src, + int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2, int mode) { + loadPixels(); + super.blend(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); + updatePixels(); + } + + + + ////////////////////////////////////////////////////////////// + + // SAVE + + + //public void save(String filename) // PImage calls loadPixels() + + + + ////////////////////////////////////////////////////////////// + + // INTERNAL MATH + + + protected final float clamp(float a) { + return (a < 1) ? a : 1; + } +} diff --git a/java/Mekstension/src/mekstension/Hud.java b/java/Mekstension/src/mekstension/Hud.java new file mode 100644 index 0000000..381b370 --- /dev/null +++ b/java/Mekstension/src/mekstension/Hud.java @@ -0,0 +1,62 @@ +package mekstension; + + + +import processing.core.PFont; + +public class Hud +{ + private Mekstension p; + private PFont font; + private String mssg = ""; + private float alpha = 255; + + private boolean toggle = false; + + /** + * constructor + * @param p_ Mekstension parent application + */ + public Hud(Mekstension p_) + { + p = p_; + font = p.loadFont( "data/HelveticaNeue-Light-48.vlw" ); + p.textFont( font, 18); + } + + + /** + * draw loop + */ + public void draw() + { + p.fill(255, alpha); + p.text(mssg, 20, p.height - 100); + + if( toggle ) + alpha = alpha - 5 > 0 ? alpha - 5 : 0; + else + alpha = 255; + } + + + /** + * log + * @param mssg_ the message + */ + public void log(String mssg_) + { + mssg = mssg_; + alpha = 255; + } + + + + /** + * toggle hud on/off + */ + public void toggle() + { + toggle = toggle ? false : true; + } +} diff --git a/java/Mekstension/src/mekstension/Mekstension.java b/java/Mekstension/src/mekstension/Mekstension.java new file mode 100644 index 0000000..db97cb3 --- /dev/null +++ b/java/Mekstension/src/mekstension/Mekstension.java @@ -0,0 +1,297 @@ +package mekstension; + +import gdl.MinimalCamera; + +import javax.media.opengl.GL; +import javax.media.opengl.glu.GLU; + +import processing.core.PApplet; +import processing.opengl.PGraphicsOpenGL; +import shapes.ShapeManager; +import shapes.ShapeRenderer; +import toxi.geom.Vec3D; + + +public class Mekstension extends PApplet +{ + public static final String VERSION = "Mekstension v.0.1"; + public static boolean FULLSCREEN = false; + + private boolean debug = false; + private boolean hideCursor = false; + + public PGraphicsOpenGL pgl; + public GL gl; + public GLU glu; + + private ShapeManager shapeManager; + private ShapeRenderer shapeRenderer; + private Hud hud; + + private MinimalCamera cam; + + + /** + * setup + */ + public void setup() + { + if( FULLSCREEN ) + size(screen.width, screen.height, OPENGL); + else + size(1200, 800, OPENGL); + + hint(ENABLE_OPENGL_4X_SMOOTH); + frameRate(60); + + pgl = (PGraphicsOpenGL) g; + gl = pgl.beginGL(); + glu = new GLU(); + + shapeManager = new ShapeManager(this); + shapeRenderer = new ShapeRenderer(this); + hud = new Hud(this); + hud.log( VERSION ); + hud.toggle(); + + cam = new MinimalCamera(width, height); + cam.farClip = 10; + } + + + /** + * runner + * + * @param args java applet/application arguments + */ + public static void main(String[] args) + { + String className = "mekstension.Mekstension"; + if(FULLSCREEN) { PApplet.main( new String[] { "--present", className } ); } + else { PApplet.main( new String[] { className } ); } + } + + + /** + * Draw loop + */ + public void draw() + { + cam.update(); + cam.apply(g); + background(0); + shapeManager.update(); + shapeRenderer.draw( shapeManager.getShapelist() ); + hud.draw(); + } + + + /** + * mouse button down and mouse moving + */ + public void mouseDragged() + { + shapeManager.mouseEdit(mouseX, mouseY, pmouseX, pmouseY); + } + + + /** + * Key pressed + */ + public void keyPressed() + { + switch(keyCode) + { + // global controls + case 81 : // q + shapeManager.setEditMode(ShapeManager.GLOBAL_SET_ANGLE); + hud.log("Angle: " + ((shapeManager.globalAngle * (2 * Math.PI) * 9))); + break; + case 87 : // w + shapeManager.setEditMode(ShapeManager.GLOBAL_SET_FOCAL_LENGTH); + hud.log("Focal Length: " + shapeManager.globalFocalLength); + break; + case 69 : // e + shapeManager.setEditMode(ShapeManager.GLOBAL_SET_DEPTH); + hud.log("Depth"); + break; + case 82 : // r + shapeManager.setEditMode(ShapeManager.GLOBAL_SET_SEGMENTS); + hud.log("Segments: " + shapeManager.globalSegments); + break; + case 84 : // t + shapeManager.setEditMode(ShapeManager.GLOBAL_CYCLE_FG); + hud.log("Cycle Foreground Color: " + + parseInt(shapeManager.globalFG.red() * 255) + ", " + + parseInt(shapeManager.globalFG.green() * 255) + ", " + + parseInt(shapeManager.globalFG.blue() * 255)); + break; + case 89 : // y + shapeManager.setEditMode(ShapeManager.GLOBAL_CYCLE_BG); + hud.log("Cycle Background Color: " + + parseInt(shapeManager.globalBG.red() * 255) + ", " + + parseInt(shapeManager.globalBG.green() * 255) + ", " + + parseInt(shapeManager.globalBG.blue() * 255)); + break; + case 85 : // u + shapeManager.setEditMode(ShapeManager.GLOBAL_CYCLE_STROKE); + hud.log("Cycle Stroke Color: " + + parseInt(shapeManager.globalStroke.red() * 255) + ", " + + parseInt(shapeManager.globalStroke.green() * 255) + ", " + + parseInt(shapeManager.globalStroke.blue() * 255)); + break; + case 73 : // i + shapeManager.setEditMode(ShapeManager.GLOBAL_SET_FG_ALPHA); + hud.log("Set Foreground Alpha:" + shapeManager.globalFG.alpha); + break; + case 79 : // o + shapeManager.setEditMode(ShapeManager.GLOBAL_SET_BG_ALPHA); + hud.log("Set Background Alpha:" + shapeManager.globalBG.alpha); + break; + case 80 : // p + shapeManager.setEditMode(ShapeManager.GLOBAL_SET_STROKE_ALPHA); + hud.log("Set Stroke Alpha: " + shapeManager.globalStroke.alpha); + break; + + + // individual object controls + case 65 : // a + shapeManager.setEditMode(ShapeManager.OBJ_SET_POSITION); + Vec3D pos = shapeManager.currentShape.getPos(); + hud.log(shapeManager.currentShape.label + " position: " + pos.x + ", " + pos.y ); + break; + case 83 : // s + shapeManager.setEditMode(ShapeManager.OBJ_SET_ROTATION); + hud.log(shapeManager.currentShape.label + " rotation: " + shapeManager.currentShape.rot); + break; + case 68 : // d + shapeManager.setEditMode(ShapeManager.OBJ_SET_SCALE); + hud.log(shapeManager.currentShape.label + " scale: " + shapeManager.currentShape.scale); + break; + case 70 : // f + shapeManager.setEditMode(ShapeManager.OBJ_SET_DEPTH); + hud.log(shapeManager.currentShape.label + " depth: " + shapeManager.currentShape.depth); + break; + case 71 : // g + shapeManager.setEditMode(ShapeManager.OBJ_SET_MODX_AMP); + hud.log(shapeManager.currentShape.label + " modulation x amplitude: " + shapeManager.currentShape.ampx); + break; + case 72 : // h + shapeManager.setEditMode(ShapeManager.OBJ_SET_MODX_FREQ); + hud.log(shapeManager.currentShape.label + " modulation x freq: " + shapeManager.currentShape.modXFreq); + break; + case 74 : // j + shapeManager.setEditMode(ShapeManager.OBJ_SET_MODY_AMP); + hud.log(shapeManager.currentShape.label + " modulation y amplitude: " + shapeManager.currentShape.ampy); + break; + case 75 :// k + shapeManager.setEditMode(ShapeManager.OBJ_SET_MODY_FREQ); + hud.log(shapeManager.currentShape.label + " modulation y freq: " + shapeManager.currentShape.modYFreq); + break; + case 76 : // l + shapeManager.setEditMode(ShapeManager.OBJ_SET_SEGMENTS); + hud.log(shapeManager.currentShape.label + " segments: " + shapeManager.currentShape.numSegments); + break; + + + case 61 : // = (+) + if(shapeManager.currentShape != null) { + shapeManager.currentShape.addSide(); + hud.log(shapeManager.currentShape.label + " sides: " + shapeManager.currentShape.sides); + } + break; + case 45 : // - + if(shapeManager.currentShape != null) { + shapeManager.currentShape.subtractSide(); + hud.log(shapeManager.currentShape.label + " sides: " + shapeManager.currentShape.sides); + } + break; + + + case 47 : // "/" + shapeManager.addShape(); + hud.log("Added shape " + shapeManager.currentShape.getID()); + break; + + + case 9 : // TAB + hud.toggle(); + hideCursor = hideCursor ? false : true; + if (hideCursor) noCursor(); + else cursor(); + break; + + + case 93 : // ] + shapeRenderer.nextBlendModeL(); + hud.log("GL Blend Function:\n" + shapeRenderer.GLblendR.getCurrentName() + "\n" + shapeRenderer.GLblendL.getCurrentName()); + break; + + case 91 : // [ + shapeRenderer.nextBlendModeR(); + hud.log("GL Blend Function:\n" + shapeRenderer.GLblendR.getCurrentName() + "\n" + shapeRenderer.GLblendL.getCurrentName()); + break; + + case 92 : // \ + shapeRenderer.toggleBlend(); + String blendEnabled = shapeRenderer.enableBlend ? "ON" : "OFF"; + hud.log("GL Blend Function: " + blendEnabled); + break; + + + + case 38 : // UP + shapeManager.mouseEdit(0, -10, 0, 0); + break; + + case 40 : // DN + shapeManager.mouseEdit(0, 10, 0, 0); + break; + + case 39 : // RT + shapeManager.mouseEdit(10, 0, 0, 0); + break; + + case 37 : // LT + shapeManager.mouseEdit(-10, 0, 0, 0); + break; + + + case 90 : // z + shapeManager.toggleFGCycle(); + String cycleFG = shapeManager.cycleFG ? "ON" : "OFF"; + hud.log("Toggle Foreground Color Cycle : " + cycleFG); + break; + + case 88 : // x + shapeManager.toggleBGCycle(); + String cycleBG = shapeManager.cycleBG ? "ON" : "OFF"; + hud.log("Toggle Background Color Cycle : " + cycleBG); + break; + + case 67 : // c + shapeManager.toggleStrokeCycle(); + String cycleStroke = shapeManager.cycleStroke ? "ON" : "OFF"; + hud.log("Toggle Stroke Color Cycle : " + cycleStroke); + break; + + case 86 : // v + shapeManager.setEditMode(ShapeManager.GLOBAL_SET_COLORCYCLE_SPEED); + hud.log("Color Cycle Speed:" + shapeManager.colorCycleSpeed); + break; + + + case 32 : // SPACEBAR + if(shapeManager.currentShape != null) { + hud.log("Edit shape " + shapeManager.currentShape.getID()); + shapeManager.nextShape(); + } + break; + + + default : + if(debug) System.out.print("\n" + keyCode); + break; + } + } +} \ No newline at end of file diff --git a/java/Mekstension/src/shapes/Shape.java b/java/Mekstension/src/shapes/Shape.java new file mode 100644 index 0000000..f39656c --- /dev/null +++ b/java/Mekstension/src/shapes/Shape.java @@ -0,0 +1,441 @@ +package shapes; + +import java.util.ArrayList; +import processing.core.PApplet; +import mekstension.Mekstension; + +import toxi.color.TColor; +import toxi.geom.Vec3D; +import toxi.math.noise.SimplexNoise; + + +public class Shape +{ + protected Mekstension p; + public String label; + + private int id; + private float inc = 0; + + + // color, draw style + public TColor fg, bg, stroke; + public boolean enableFill = true; + public boolean enableStroke = true; + public float strokeWeight = 2; + + // position, shape + private Vec3D pos, dest; + private float objScale = 0; + public float scale = 50, destScale = 50; + public float depth = -500, destDepth = -500; + public float angle = 0, destAngle = 0; + public float rot = 0, destRot = 0; + public float zScale = 1, destZScale = 0; + + // mesh + ArrayList> shapeSegments = new ArrayList>(); + ArrayList segments = new ArrayList (); + final public static int MAX_SIDES = 64; + final public static int MAX_SEGMENTS = 1000; + public int numSegments = 20; + public int sides = 4; + + + // animation + private boolean lerpVars = true; + private float lerpSpeed = 0.15f; + + private float noiseOffset = 0; + + public float modX = 0.5f, modXFreq = 1f, destModXFreq = 0.05f, ampx = 100f, destxAmp = 100f; + public float modY = 0.5f, modYFreq = 1f, destModYFreq = 0.05f, ampy = 100f, destyAmp = 100f; + + + /** + * Constructor + * + * @param p_ Parent Mekstension class. Extended from PApplet + * @param sides_ Number of sides + * @param label_ Label + * @see PApplet + */ + public Shape(Mekstension p_, int sides_, String label_, int id_) + { + p = p_; + id = id_; + label = label_; + pos = new Vec3D(p.width / 2, p.height / 2, 0); + dest = new Vec3D(pos); + sides = sides_; + + setSegments(numSegments); + } + + + /** + * update mesh + */ + private void updateMesh() + { + inc = (float) (2 * Math.PI) / sides; + + shapeSegments.clear(); + + for ( int j = 1; j <= numSegments; j++ ) { + ArrayList seg = new ArrayList(); + float depthMul = PApplet.map(j, 1, numSegments, 0, 1); + for ( int k = 0; k < sides; k ++ ) { + float x = (float) ( scale * Math.cos( inc * k + rot ) + ( depth * depthMul ) * Math.cos( angle ) ); + float y = (float) ( scale * Math.sin( inc * k + rot ) + ( depth * depthMul ) * Math.sin( angle ) ); + float z = depth * depthMul; + seg.add(new Vec3D(x, y, z)); + } + shapeSegments.add( seg ); + } + } + + + /** + * Update Loop + */ + public void update() + { + objScale = PApplet.lerp(objScale, 1.0f, lerpSpeed); + + if( lerpVars ) { + pos.x = PApplet.lerp(pos.x, dest.x, lerpSpeed); + pos.y = PApplet.lerp(pos.y, dest.y, lerpSpeed); + pos.z = PApplet.lerp(pos.z, dest.z, lerpSpeed); + depth = PApplet.lerp(depth, destDepth, lerpSpeed); + angle = PApplet.lerp(angle, destAngle, lerpSpeed); + rot = PApplet.lerp(rot, destRot, lerpSpeed); + zScale = PApplet.lerp(zScale, destZScale, lerpSpeed); + scale = PApplet.lerp(scale, destScale, lerpSpeed); + ampx = PApplet.lerp(ampx, destxAmp, lerpSpeed); + modXFreq = PApplet.lerp(ampx, destModXFreq, lerpSpeed); + ampy = PApplet.lerp(ampy, destyAmp, lerpSpeed); + modYFreq = PApplet.lerp(ampy, destModYFreq, lerpSpeed); + } + else { + pos.x = dest.x; + pos.y = dest.y; + pos.z = dest.z; + depth = destDepth; + angle = destAngle; + rot = destRot; + zScale = destZScale; + scale = destScale; + ampx = destxAmp; + modXFreq = destModXFreq; + ampy = destyAmp; + modYFreq = destModYFreq; + } + + updateSegments(); + updateMesh(); + } + + + /** + * Draw loop + */ + public void draw() + { + p.pushMatrix(); + + p.translate(pos.x, pos.y, pos.z); + p.scale(objScale, objScale, zScale); + + drawSegments(); + //drawMesh(); + + p.popMatrix(); + } + + + /** + * update segments with 3D noisefield + */ + private void updateSegments() + { + Vec3D change = new Vec3D(); + float freq = 100f; + float NS = 0.1f; + + for ( int i = 1; i < segments.size(); i++) + { + float noiseValX = (float) SimplexNoise.noise(NS + noiseOffset + (-1 * i), NS + noiseOffset + (-1 * i)); + float noiseValY = (float) SimplexNoise.noise(NS + noiseOffset + 100 + (-1 * i), NS + noiseOffset + 100 + (-1 * i)); + float scalar = i * 0.1f; + + change.x = (float) ( noiseValX * ampx * scalar ); + change.y = (float) ( noiseValY * ampy * scalar ); + + Vec3D seg = segments.get(i); + Vec3D pseg = new Vec3D(segments.get(i - 1)); + + pseg.z = ( depth / numSegments ) * i; + seg.addSelf( change ); + seg.interpolateToSelf( pseg, 0.8f ); + } + noiseOffset += NS / freq; + } + + + /** + * draw mesh + */ + void drawMesh() + { + p.stroke(stroke.toARGB()); + + if(enableStroke && stroke.alpha > 0) + p.strokeWeight( strokeWeight ); + else + p.noStroke(); + + p.beginShape(PApplet.QUAD_STRIP); + + for( int j = 1; j < numSegments; j++) { + + TColor c1 = new TColor(fg); + TColor c2 = new TColor(bg); + + float colMul1 = PApplet.map(j, 1, numSegments, 0, 1); + float colMul2 = PApplet.map(j, 0, numSegments - 1, 1, 0); + c1.blend( bg, colMul1 ); + c2.blend( fg, colMul2 ); + + ArrayList seg = shapeSegments.get(j); + ArrayList seg2 = shapeSegments.get(j - 1); + + for ( int i = 0; i < seg.size(); i++ ) { + + Vec3D v = (Vec3D) seg.get(i); + Vec3D v2 = (Vec3D) seg2.get(i); + p.fill(c2.toARGB()); + p.vertex(v.x, v.y, v.z); + p.fill(c1.toARGB()); + p.vertex(v2.x, v2.y, v2.z); + if(i == seg.size() - 1) { + v = (Vec3D) seg.get(0); + v2 = (Vec3D) seg2.get(0); + p.fill(c2.toARGB()); + p.vertex(v.x, v.y, v.z); + p.fill(c1.toARGB()); + p.vertex(v2.x, v2.y, v2.z); + } + } + } + p.endShape(PApplet.CLOSE); + } + + + + /** + * draw all segments + * @param segs the segment vectors + */ + void drawSegments() + { + p.strokeWeight(5); + p.stroke(255,0,255); + + p.fill(255, 0, 0); + p.ellipse(0, 0, 10, 10); + + p.beginShape(PApplet.LINES); + + p.vertex(0, 0, 0); + p.vertex(segments.get(1).x, segments.get(1).y, segments.get(1).z); + + for ( int i = 1; i < numSegments; i++) { + // color blending + TColor c1 = new TColor(fg); + TColor c2 = new TColor(bg); + + float colMul1 = PApplet.map(i, 1, numSegments, 0, 1); + float colMul2 = PApplet.map(i, 0, numSegments - 1, 1, 0); + c1.blend( bg, colMul1 ); + c2.blend( fg, colMul2 ); + + Vec3D s = segments.get( i ); + Vec3D prev = segments.get( i - 1 ); + + p.fill(c1.toARGB()); + p.stroke(c1.toARGB()); + p.vertex(s.x, s.y, s.z); + + p.fill(c2.toARGB()); + p.stroke(c2.toARGB()); + p.vertex(prev.x, prev.y, prev.z); + } + p.endShape(); + } + + + /** + * set number of segments + * @param segments number of segments + */ + public void setSegments ( int segments_ ) + { + numSegments = PApplet.constrain(segments_, 2, Shape.MAX_SEGMENTS); + segments.clear(); + for ( int i = 0; i < numSegments; i++) + segments.add(new Vec3D( 0, 0, ( depth / numSegments ) * i )); + } + + + /** + * set zscale, which simulates focal length + * @param zScale z scale + */ + public void setZScale(float zScale_) + { + destZScale = zScale_; + } + + + /** + * set new shape depth, and build new mesh + * @param depth_ depth + */ + public void setDepth(float depth_) + { + destDepth = depth_; + } + + + /** + * set shape rotation + * @param newRot the rotation amount + */ + public void setRot(float rot_) + { + destRot = rot_; + } + + + /** + * the new position + * @param newX x position + * @param newY y position + */ + public void setPos(float x_, float y_, float z_) + { + dest.x = x_; + dest.y = y_; + dest.z = z_; + } + + + /** + * get position + * @return + */ + public Vec3D getPos() + { + return dest; + } + + + /** + * add side + */ + public void addSide() + { + sides = sides < Shape.MAX_SIDES ? sides += 1 : Shape.MAX_SIDES; + } + + + /** + * subtract side + */ + public void subtractSide() + { + sides = sides > 2 ? sides -= 1 : 2; + } + + + /** + * set shape scale + * @param scale_ new scale + */ + public void setScale(float scale_) + { + destScale = scale_; + } + + + /** + * set mod x freq + * @param freq_ + */ + public void setModFreqX(float freq_) + { + destModXFreq = freq_; + } + + + /** + * set mod y freq + * @param freq_ + */ + public void setModFreqY(float freq_) + { + destModYFreq = freq_; + } + + + /** + * set mod x amp + * @param amp_ + */ + public void setModAmpX(float amp_) + { + destxAmp = amp_; + } + + + /** + * set mod Y amp + * @param amp_ + */ + public void setModAmpY(float amp_) + { + destyAmp = amp_; + } + + + /** + * Set angle + * @param ang the angle in Radians + */ + public void setAngle(float ang) + { + destAngle = ang; + } + + + /** + * set overall alpha + */ + public void setAlpha (float alpha_) + { + fg.alpha = alpha_; + bg.alpha = alpha_; + stroke.alpha = alpha_; + } + + + /** + * get id + * @return the id + */ + public int getID() + { + return id; + } +} diff --git a/java/Mekstension/src/shapes/ShapeList.java b/java/Mekstension/src/shapes/ShapeList.java new file mode 100644 index 0000000..7ef50ce --- /dev/null +++ b/java/Mekstension/src/shapes/ShapeList.java @@ -0,0 +1,18 @@ +package shapes; + +import java.util.ArrayList; + +public class ShapeList extends ArrayList +{ + + private int currentIndex = 0; + + public Shape next() + { + if (size() == 0) return null; + if (currentIndex++ >= size() - 1) { + currentIndex = 0; + } + return get(currentIndex); + } +} diff --git a/java/Mekstension/src/shapes/ShapeManager.java b/java/Mekstension/src/shapes/ShapeManager.java new file mode 100644 index 0000000..2c4cea0 --- /dev/null +++ b/java/Mekstension/src/shapes/ShapeManager.java @@ -0,0 +1,304 @@ +package shapes; + +import processing.core.PApplet; +import toxi.color.TColor; +import toxi.geom.Vec3D; +import mekstension.Mekstension; + +public class ShapeManager +{ + public final static int OBJ_SET_POSITION = 2; + public final static int OBJ_SET_ROTATION = 3; + public final static int OBJ_SET_SCALE = 4; + public final static int OBJ_SET_DEPTH = 5; + public final static int OBJ_SET_MODX_FREQ = 6; + public final static int OBJ_SET_MODX_AMP = 7; + public final static int OBJ_SET_MODY_FREQ = 8; + public final static int OBJ_SET_MODY_AMP = 9; + public final static int OBJ_SET_SEGMENTS = 10; + + + public final static int GLOBAL_SET_DEPTH = 99; + public final static int GLOBAL_SET_ANGLE = 100; + public final static int GLOBAL_SET_FOCAL_LENGTH = 101; + public final static int GLOBAL_SET_SEGMENTS = 108; + public final static int GLOBAL_CYCLE_FG = 102; + public final static int GLOBAL_CYCLE_BG = 103; + public final static int GLOBAL_CYCLE_STROKE = 104; + public final static int GLOBAL_SET_FG_ALPHA = 105; + public final static int GLOBAL_SET_BG_ALPHA = 106; + public final static int GLOBAL_SET_STROKE_ALPHA = 107; + public final static int GLOBAL_SET_COLORCYCLE_SPEED = 110; + + + + + private int editmode = OBJ_SET_POSITION; + + private Mekstension p; + private ShapeList shapelist; + public Shape currentShape; + + public int globalSegments = 2; + public float globalAngle = 0f, globalFocalLength = 0.5f; + public TColor globalFG, globalBG, globalStroke; + + public boolean cycleFG, cycleBG, cycleStroke; + + public float colorCycleSpeed = 0.05f; + + + + /** + * Constructor + * @param p_ Mekstension application, extended from PApplet + * @see PApplet + */ + public ShapeManager(Mekstension p_) + { + this.p = p_; + shapelist = new ShapeList(); + currentShape = null; + + globalFG = new TColor( TColor.newRandom() ); + globalBG = new TColor( TColor.newRandom() ); + globalStroke = new TColor( TColor.newRandom() ); + globalFG.setBrightness(1); + globalBG.setBrightness(1); + globalStroke.setBrightness(1); + + addShape(); + } + + + /** + * + * @param mx current mouse x + * @param my current mouse y + * @param pmx previous mouse x + * @param pmy previous mouse y + */ + public void mouseEdit(int mx, int my, int pmx, int pmy) + { + float vX = mx - pmx; + float vY = my - pmy; + + switch( editmode ) + { + case ShapeManager.GLOBAL_SET_ANGLE : + for( int i = 0; i < shapelist.size(); i++ ) { + globalAngle += vX / 200f; + shapelist.get(i).setAngle(globalAngle); + } + break; + + case ShapeManager.GLOBAL_SET_FOCAL_LENGTH : + for( int i = 0; i < shapelist.size(); i++ ) { + globalFocalLength += vX / 100f; + shapelist.get(i).setZScale(globalFocalLength); + } + break; + + case ShapeManager.GLOBAL_SET_DEPTH : + for( int i = 0; i < shapelist.size(); i++ ) + shapelist.get(i).setDepth(shapelist.get(i).destDepth += vX); + break; + + case ShapeManager.GLOBAL_SET_SEGMENTS : + int segments = vX > 0 ? 1 : -1; + globalSegments = segments; + for( int i = 0; i < shapelist.size(); i++ ) { + shapelist.get(i).setSegments(shapelist.get(i).numSegments += segments); + globalSegments = shapelist.get(i).numSegments; + } + break; + + case ShapeManager.GLOBAL_CYCLE_FG : + globalFG.rotateRYB( vX / 100f ); + globalFG.saturate( vY / 100f); + for( int i = 0; i < shapelist.size(); i++ ) + shapelist.get(i).fg = globalFG; + break; + + case ShapeManager.GLOBAL_CYCLE_BG: + globalBG.rotateRYB( vX / 100f ); + globalBG.saturate( vY / 100f ); + for( int i = 0; i < shapelist.size(); i++ ) + shapelist.get(i).bg = globalBG; + break; + + case ShapeManager.GLOBAL_CYCLE_STROKE: + globalStroke.rotateRYB( vX / 100f ); + globalStroke.saturate( vY / 100f); + for( int i = 0; i < shapelist.size(); i++ ) + shapelist.get(i).stroke = globalStroke; + break; + + case ShapeManager.GLOBAL_SET_FG_ALPHA : + float new_fga = globalFG.alpha(); + new_fga += (vX / 600f); + globalFG.alpha = PApplet.constrain(new_fga, 0, 1); + for( int i = 0; i < shapelist.size(); i++ ) + shapelist.get(i).fg = globalFG; + break; + + case ShapeManager.GLOBAL_SET_BG_ALPHA: + float new_bga = globalBG.alpha(); + new_bga += (vX / 600f); + globalBG.alpha = PApplet.constrain(new_bga, 0, 1); + for( int i = 0; i < shapelist.size(); i++ ) + shapelist.get(i).bg = globalBG; + break; + + case ShapeManager.GLOBAL_SET_STROKE_ALPHA: + float new_stra = globalStroke.alpha(); + new_stra += (vX / 600f); + globalStroke.alpha = PApplet.constrain(new_stra, 0, 1); + for( int i = 0; i < shapelist.size(); i++ ) + shapelist.get(i).stroke = globalStroke; + break; + + case ShapeManager.GLOBAL_SET_COLORCYCLE_SPEED : + colorCycleSpeed += (vX / 5000f); + break; + + case ShapeManager.OBJ_SET_SCALE : + currentShape.setScale(currentShape.destScale += (my - pmy)); + break; + + case ShapeManager.OBJ_SET_DEPTH : + currentShape.setDepth(currentShape.destDepth += (my - pmy)); + break; + + case ShapeManager.OBJ_SET_ROTATION : + currentShape.setRot(currentShape.destRot += ((mx - pmx) / 100f)); + break; + + case ShapeManager.OBJ_SET_POSITION : + Vec3D pos = currentShape.getPos(); + currentShape.setPos(pos.x += ((mx - pmx)), pos.y += ((my - pmy)), 0); + break; + + case ShapeManager.OBJ_SET_MODX_AMP : + float modx = currentShape.destxAmp += (vX / 10f); + currentShape.setModAmpX(modx); + break; + + case ShapeManager.OBJ_SET_MODX_FREQ : + float freqx = currentShape.destModXFreq += (vX / 600f); + currentShape.setModFreqX(freqx); + break; + + case ShapeManager.OBJ_SET_MODY_AMP : + float mody = currentShape.destyAmp += (vY / 10f); + currentShape.setModAmpY(mody); + break; + + case ShapeManager.OBJ_SET_MODY_FREQ : + float freqy = currentShape.destModYFreq += (vY / 600f); + currentShape.setModFreqY(freqy); + break; + + case ShapeManager.OBJ_SET_SEGMENTS : + int segs = vX > 0 ? 1 : -1; + currentShape.setSegments(currentShape.numSegments += segs); + break; + + default : + break; + } + } + + + /** + * toggle foreground audo color cycle + */ + public void toggleFGCycle() + { + cycleFG = cycleFG ? false : true; + } + + + /** + * toggle background audo color cycle + */ + public void toggleBGCycle() + { + cycleBG = cycleBG ? false : true; + } + + + /** + * toggle stroke audo color cycle + */ + public void toggleStrokeCycle() + { + cycleStroke = cycleStroke ? false : true; + } + + + /** + * Set edit mode + * @param mode + */ + public void setEditMode(int mode) + { + editmode = mode; + } + + + /** + * Create new shape and to shapelist ArrayList + */ + public void addShape() + { + Shape newShape = new Shape(p, 4, "Shape " + shapelist.size(), (shapelist.size()+1)); + + newShape.fg = globalFG; + newShape.bg = globalBG; + newShape.stroke = globalStroke; + newShape.angle = globalAngle; + newShape.setZScale( globalFocalLength ); + // newShape.setSegments( globalSegments ); + newShape.setAngle(globalAngle); + shapelist.add(newShape); + + nextShape(); + } + + + /** + * update loop + */ + public void update() + { + if(cycleFG || cycleBG || cycleStroke) { + for( int i = 0; i < shapelist.size(); i++ ) { + if(cycleFG) + shapelist.get(i).fg.rotateRYB(colorCycleSpeed); + if(cycleBG) + shapelist.get(i).bg.rotateRYB(colorCycleSpeed); + if(cycleStroke) + shapelist.get(i).stroke.rotateRYB(colorCycleSpeed); + } + } + } + + + /** + * step to the next shape and assign it to the current shape + */ + public void nextShape() + { + currentShape = shapelist.next(); + } + + + /** + * get shapelist + */ + public ShapeList getShapelist() + { + return shapelist; + } +} diff --git a/java/Mekstension/src/shapes/ShapeRenderer.java b/java/Mekstension/src/shapes/ShapeRenderer.java new file mode 100644 index 0000000..7379308 --- /dev/null +++ b/java/Mekstension/src/shapes/ShapeRenderer.java @@ -0,0 +1,161 @@ +package shapes; + +import java.util.ArrayList; + +import javax.media.opengl.GL; + +import processing.core.PApplet; +import mekstension.Mekstension; + +public class ShapeRenderer +{ + Mekstension p; + + public nvArray GLblendL = new nvArray(); + public nvArray GLblendR = new nvArray(); + private boolean blnDisableZTest = true; + public boolean enableBlend = false; + + /** + * Constructor + * @param p_ {@link Mekstension} parent, extended from {@link PApplet} + * @see PApplet + */ + public ShapeRenderer(Mekstension p_) + { + p = p_; + + GLblendL.add("GL_ONE",GL.GL_ONE); + GLblendL.add("GL_SRC_COLOR",GL.GL_SRC_COLOR); + GLblendL.add("GL_ONE_MINUS_SRC_COLOR",GL.GL_ONE_MINUS_SRC_COLOR); + GLblendL.add("GL_DST_COLOR",GL.GL_DST_COLOR); + GLblendL.add("GL_ONE_MINUS_DST_COLOR",GL.GL_ONE_MINUS_DST_COLOR); + GLblendL.add("GL_SRC_ALPHA",GL.GL_SRC_ALPHA); + GLblendL.add("GL_ONE_MINUS_SRC_ALPHA",GL.GL_ONE_MINUS_SRC_ALPHA); + GLblendL.add("GL_DST_ALPHA",GL.GL_DST_ALPHA); + GLblendL.add("GL_ONE_MINUS_DST_ALPHA",GL.GL_ONE_MINUS_DST_ALPHA); + GLblendL.add("GL_SRC_ALPHA_SATURATE",GL.GL_SRC_ALPHA_SATURATE); + GLblendL.add("GL_CONSTANT_COLOR",GL.GL_CONSTANT_COLOR ); + GLblendL.add("GL_ONE_MINUS_CONSTANT_COLOR",GL.GL_ONE_MINUS_CONSTANT_COLOR); + GLblendL.add("GL_CONSTANT_ALPHA",GL.GL_CONSTANT_ALPHA ); + GLblendL.add("GL_ONE_MINUS_CONSTANT_ALPHA",GL.GL_ONE_MINUS_CONSTANT_ALPHA); + GLblendL.add("GL_ZERO",GL.GL_ZERO); + + GLblendR.add("GL_ONE",GL.GL_ONE); + GLblendR.add("GL_SRC_COLOR",GL.GL_SRC_COLOR); + GLblendR.add("GL_ONE_MINUS_SRC_COLOR",GL.GL_ONE_MINUS_SRC_COLOR); + GLblendR.add("GL_DST_COLOR",GL.GL_DST_COLOR); + GLblendR.add("GL_ONE_MINUS_DST_COLOR",GL.GL_ONE_MINUS_DST_COLOR); + GLblendR.add("GL_SRC_ALPHA",GL.GL_SRC_ALPHA); + GLblendR.add("GL_ONE_MINUS_SRC_ALPHA",GL.GL_ONE_MINUS_SRC_ALPHA); + GLblendR.add("GL_DST_ALPHA",GL.GL_DST_ALPHA); + GLblendR.add("GL_ONE_MINUS_DST_ALPHA",GL.GL_ONE_MINUS_DST_ALPHA); + GLblendR.add("GL_CONSTANT_COLOR",GL.GL_CONSTANT_COLOR ); + GLblendR.add("GL_ONE_MINUS_CONSTANT_COLOR",GL.GL_ONE_MINUS_CONSTANT_COLOR); + GLblendR.add("GL_CONSTANT_ALPHA",GL.GL_CONSTANT_ALPHA ); + GLblendR.add("GL_ONE_MINUS_CONSTANT_ALPHA",GL.GL_ONE_MINUS_CONSTANT_ALPHA); + GLblendR.add("GL_ZERO",GL.GL_ZERO); + } + + + /** + * draw loop + */ + public void draw(ShapeList shapelist) + { + if ( enableBlend ) { + if( blnDisableZTest ) + p.gl.glDisable(GL.GL_DEPTH_TEST); + p.gl.glEnable(GL.GL_BLEND); + p.gl.glBlendFunc( GLblendL.getCurrent(), GLblendR.getCurrent() ); + } + + for(int i = 0; i < shapelist.size(); i++) { + shapelist.get(i).update(); + shapelist.get(i).draw(); + } + + if( enableBlend ) { + p.gl.glBlendFunc( GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA ); + } + } + + + public void nextBlendModeR() + { + GLblendR.next(); + } + + + public void nextBlendModeL() + { + GLblendL.next(); + } + + + public void toggleDepthTest() + { + blnDisableZTest = blnDisableZTest ? false : true; + } + + + public void toggleBlend() + { + enableBlend = enableBlend ? false : true; + } + + + + + /** + * name/value pair array + * @author Ira Greenberg + */ + public class nvArray + { + ArrayList name = new ArrayList (); + ArrayList val = new ArrayList (); + + int length; + int pos; + + + nvArray() + { + length = 0; + pos = 0; + } + + + void add(String l_name, int l_val) + { + length++; + name.add( l_name ); + val.add( new Integer(l_val) ); + } + + + void next() + { + pos = (pos+1 == length)? 0 : pos+1; + } + + + void prev() + { + pos = (pos-1 <0)? length-1 : pos-1; + } + + + public int getCurrent() + { + return ( (Integer) val.get( pos ) ).intValue(); + } + + + public String getCurrentName() + { + return( (String) name.get( pos ) ); + } + } +} diff --git a/java/Mekstension/tools/gdl/MinimalCamera.java b/java/Mekstension/tools/gdl/MinimalCamera.java new file mode 100644 index 0000000..814f910 --- /dev/null +++ b/java/Mekstension/tools/gdl/MinimalCamera.java @@ -0,0 +1,210 @@ +package gdl; + +import processing.core.PGraphics; +import toxi.geom.Vec3D; + +public class MinimalCamera extends Vec3D +{ + public Vec3D target, up; + + public float fov, aspect, nearClip, farClip; + public float mag; + + public boolean autoClip = false; + public float autoClip_nearMul = 0.1f; + public float autoClip_farMul = 10; + + public Vec3D pos = new Vec3D(), ppos = new Vec3D(), vel = new Vec3D(); + + + public MinimalCamera() + { + this( + 0.0f, 0.0f, whOffsetZ(2.0f, 2.0f), + 0.0f, 0.0f, 0.0f, + 2.0f, 2.0f + ); + } + + public MinimalCamera(float width, float height) + { + this( + width / 2.0f, height / 2.0f, whOffsetZ(width, height), + width / 2.0f, height / 2.0f, 0.0f, + width, height + ); + } + + public MinimalCamera(float x, float y, float width, float height) + { + this( + x, y, whOffsetZ(width, height), + x, y, 0.0f, + width, height + ); + } + + public MinimalCamera(float x, float y, float z, float aimx, float aimy, float aimz, float width, float height) + { + this( + x, y, z, + aimx, aimy, aimz, + 0, -1, 0, + (float)(Math.PI / 3.0), width / height, 0, 0 + ); + autoClip = true; + updateClip(); + } + + public MinimalCamera(float x, float y, float z, float aimx, float aimy, float aimz, float upx, float upy, float upz, float _fov, float _aspect, float _nearClip, float _farClip) + { + super(); + + pos = new Vec3D(x, y, z); + target = new Vec3D(aimx, aimy, aimz); + vel = new Vec3D(); + up = new Vec3D(upx, upy, upz); + + fov = _fov; + aspect = _aspect; + nearClip = _nearClip; + farClip = _farClip; + + updateMag(); + } + + + public void setPos(float x, float y, float z) + { + pos.set(x,y,z); + updateMag(); + } + + public void setPos(Vec3D _pos) + { + pos.set(_pos); + updateMag(); + } + + public void setTarget(float x, float y, float z) + { + target.set(x,y,z); + updateMag(); + } + + public void setUp(float x, float y, float z) + { + up.set(x,y,z); + } + + public void setOrbit(float rx, float ry, float distance) + { + pos.set(0,0,-distance); + pos.rotateX(rx); + pos.rotateY(ry); + + up.set(0,-1,0); + up.rotateY(-ry); + up.rotateX(-rx); + + updateMag(); + } + + public void translate(float x, float y, float z) + { + pos.add(x,y,z); + target.add(x,y,z); + updateMag(); + } + + + +// /** Move the camera and target simultaneously along the camera's X axis */ +// public void truck(float anAmount) +// { +// // Calculate the camera's X axis in world space +// Vector3 direction = new Vector3( +// theDeltaY * up.z - theDeltaZ * up.y, +// theDeltaX * up.z - theDeltaZ * up.x, +// theDeltaX * up.y - theDeltaY * up.x +// ); +// direction.normalize(); +// +// // Perform the truck, if any +// translate( +// pos.x -anAmount * direction.x, +// pos.y -anAmount * direction.y, +// pos.z -anAmount * direction.z +// ); +// } +// +// /** Move the camera and target simultaneously along the camera's Y axis */ +// public void boom(float anAmount) +// { +// // Perform the boom, if any +// pos.x += anAmount * up.x; +// pos.y += anAmount * up.y; +// pos.z += anAmount * up.z; +// target.x += anAmount * up.x; +// target.y += anAmount * up.y; +// target.z += anAmount * up.z; +// } +// +// /** Move the camera and target along the view vector */ +// public void dolly(float anAmount) +// { +// // Normalize the view vector +// float directionX = theDeltaX / mag; +// float directionY = theDeltaY / mag; +// float directionZ = theDeltaZ / mag; +// +// // Perform the dolly, if any +// pos.x += anAmount * directionX; +// pos.y += anAmount * directionY; +// pos.z += anAmount * directionZ; +// target.x += anAmount * directionX; +// target.y += anAmount * directionY; +// target.z += anAmount * directionZ; +// +// +// } + + + + public void update() + { + ppos.set(pos); + setPos( + pos.x + vel.x, + pos.y + vel.y, + pos.z + vel.z + ); + } + + + public void apply(PGraphics g) + { + g.perspective(fov, aspect, nearClip, farClip); + g.camera(pos.x,pos.y,pos.z, target.x,target.y,target.z, up.x,-up.y,up.z); + } + + + public void updateMag() + { + mag = pos.distanceTo(target); + + if(autoClip) updateClip(); + } + + public void updateClip() + { + nearClip = mag * autoClip_nearMul; + farClip = mag * autoClip_farMul; + } + + + public static float whOffsetZ(float width, float height) + { + return (height / 2.0f) / (float) Math.tan(Math.PI * 60.0 / 360.0); + } +} \ No newline at end of file diff --git a/java/Mekstension/tools/toxi/color/AccessCriteria.java b/java/Mekstension/tools/toxi/color/AccessCriteria.java new file mode 100644 index 0000000..e445e19 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/AccessCriteria.java @@ -0,0 +1,112 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.color; + +import java.util.Comparator; + +/** + * Defines color component access criterias and associated {@link Comparator}s + * used to sort colors based on component values. + * + * @author toxi + * + */ +public class AccessCriteria { + + public static final AccessCriteria HUE = new AccessCriteria(AccessMode.HSV, + 0); + public static final AccessCriteria SATURATION = new AccessCriteria( + AccessMode.HSV, 1); + public static final AccessCriteria BRIGHTNESS = new AccessCriteria( + AccessMode.HSV, 2); + public static final AccessCriteria RED = new AccessCriteria(AccessMode.RGB, + 0); + public static final AccessCriteria GREEN = new AccessCriteria( + AccessMode.RGB, 1); + public static final AccessCriteria BLUE = new AccessCriteria( + AccessMode.RGB, 2); + public static final AccessCriteria CYAN = new AccessCriteria( + AccessMode.CMYK, 0); + public static final AccessCriteria MAGENTA = new AccessCriteria( + AccessMode.CMYK, 1); + public static final AccessCriteria YELLOW = new AccessCriteria( + AccessMode.CMYK, 2); + public static final AccessCriteria BLACK = new AccessCriteria( + AccessMode.CMYK, 3); + + public static final AccessCriteria ALPHA = new AccessCriteria( + AccessMode.ALPHA, 0); + + public static final AccessCriteria LUMINANCE = new AccessCriteria( + AccessMode.DIRECT, 0, new LuminanceComparator()); + + protected final AccessMode mode; + protected final int component; + protected Comparator comparator; + + protected AccessCriteria(AccessMode mode, int compID) { + this(mode, compID, null); + } + + protected AccessCriteria(AccessMode mode, int compID, + Comparator comparator) { + this.mode = mode; + this.component = compID; + this.comparator = comparator; + } + + /** + * @return the comparator associated with the criteria. + */ + public Comparator getComparator() { + if (comparator == null) { + switch (mode) { + case HSV: + comparator = new HSVComparator(component); + break; + case RGB: + comparator = new RGBComparator(component); + break; + case CMYK: + comparator = new CMYKComparator(component); + break; + case ALPHA: + comparator = new AlphaComparator(); + break; + } + } + return comparator; + } + + public int getComponent() { + return component; + } + + public AccessMode getMode() { + return mode; + } +} diff --git a/java/Mekstension/tools/toxi/color/AccessMode.java b/java/Mekstension/tools/toxi/color/AccessMode.java new file mode 100644 index 0000000..f6763ea --- /dev/null +++ b/java/Mekstension/tools/toxi/color/AccessMode.java @@ -0,0 +1,30 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +public enum AccessMode { + HSV, RGB, CMYK, ALPHA, DIRECT; +} diff --git a/java/Mekstension/tools/toxi/color/AlphaComparator.java b/java/Mekstension/tools/toxi/color/AlphaComparator.java new file mode 100644 index 0000000..22d3283 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/AlphaComparator.java @@ -0,0 +1,41 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +import java.util.Comparator; + +/** + * Compares 2 colors by their alpha value. + * + * @author toxi + * + */ +public class AlphaComparator implements Comparator { + + public int compare(TColor a, TColor b) { + return a.alpha < b.alpha ? -1 : a.alpha > b.alpha ? 1 : 0; + } +} diff --git a/java/Mekstension/tools/toxi/color/CMYKComparator.java b/java/Mekstension/tools/toxi/color/CMYKComparator.java new file mode 100644 index 0000000..7aa6e80 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/CMYKComparator.java @@ -0,0 +1,48 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +import java.util.Comparator; + +/** + * Compares 2 colors by one of their CMYK component values. + * + * @author toxi + * + */ +public class CMYKComparator implements Comparator { + + private final int component; + + public CMYKComparator(int comp) { + component = comp; + } + + public int compare(TColor a, TColor b) { + return Float.compare(a.cmyk[component], b.cmyk[component]); + } + +} diff --git a/java/Mekstension/tools/toxi/color/CMYKDistanceProxy.java b/java/Mekstension/tools/toxi/color/CMYKDistanceProxy.java new file mode 100644 index 0000000..2b42b38 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/CMYKDistanceProxy.java @@ -0,0 +1,42 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.color; + +/** + * Implements the {@link DistanceProxy} interface to sort colors by CMYK + * distance (used by {@link ColorList#sortByDistance(DistanceProxy, boolean)}). + * + * @author toxi + * + */ +public class CMYKDistanceProxy implements DistanceProxy { + + public float distanceBetween(TColor a, TColor b) { + return a.distanceToCMYK(b); + } + +} diff --git a/java/Mekstension/tools/toxi/color/ColorGradient.java b/java/Mekstension/tools/toxi/color/ColorGradient.java new file mode 100644 index 0000000..241715e --- /dev/null +++ b/java/Mekstension/tools/toxi/color/ColorGradient.java @@ -0,0 +1,178 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.color; + +import java.util.Iterator; +import java.util.TreeSet; + +import toxi.math.MathUtils; + +/** + * This class can be used to calculate multi-color gradients with colors + * positioned along an imaginary line. + * + * @author toxi + * + */ +public class ColorGradient { + + protected class GradPoint implements Comparable { + float pos; + TColor color; + + GradPoint(float p, TColor c) { + pos = p; + color = c; + } + + public int compareTo(GradPoint p) { + if (Float.compare(p.pos, pos) == 0) { + return 0; + } else { + return pos < p.pos ? -1 : 1; + } + } + } + + protected TreeSet gradient; + + protected float maxDither; + + /** + * Constructs a new empty gradient. + */ + public ColorGradient() { + gradient = new TreeSet(); + } + + /** + * Adds a new color at specified position. + * + * @param p + * @param c + */ + public void addColorAt(float p, TColor c) { + gradient.add(new GradPoint(p, c)); + } + + /** + * Calculates the gradient from specified position. + * + * @param pos + * @param width + * @return list of interpolated gradient colors + */ + public ColorList calcGradient(float pos, int width) { + ColorList result = new ColorList(); + + if (gradient.size() == 0) { + return result; + } + + float frac = 0; + GradPoint currPoint = null; + GradPoint nextPoint = null; + float endPos = pos + width; + // find 1st color needed, clamp start position to positive values only + for (GradPoint gp : gradient) { + if (gp.pos < pos) { + currPoint = gp; + } + } + boolean isPremature = currPoint == null; + TreeSet activeGradient = null; + if (!isPremature) { + activeGradient = (TreeSet) gradient.tailSet(currPoint); + } else { + // start position is before 1st gradient color, so use whole + // gradient + activeGradient = gradient; + currPoint = activeGradient.first(); + } + float currWidth = 0; + Iterator iter = activeGradient.iterator(); + if (currPoint != activeGradient.last()) { + nextPoint = iter.next(); + if (isPremature) { + currWidth = 1f / (currPoint.pos - pos); + } else { + if (nextPoint.pos - currPoint.pos > 0) { + currWidth = 1f / (nextPoint.pos - currPoint.pos); + } + } + } + while (pos < endPos) { + if (isPremature) { + frac = 1 - (currPoint.pos - pos) * currWidth; + } else { + frac = (pos - currPoint.pos) * currWidth; + } + // switch to next color? + if (frac > 1.0) { + currPoint = nextPoint; + isPremature = false; + if (iter.hasNext()) { + nextPoint = iter.next(); + if (currPoint != activeGradient.last()) { + currWidth = 1f / (nextPoint.pos - currPoint.pos); + } else { + currWidth = 0; + } + frac = (pos - currPoint.pos) * currWidth; + } + } + if (currPoint != activeGradient.last()) { + float ditheredFrac = MathUtils.clip(frac + + MathUtils.normalizedRandom() * maxDither, 0f, 1f); + result.add(currPoint.color.getBlended(nextPoint.color, + ditheredFrac)); + } else { + result.add(currPoint.color.copy()); + } + pos++; + } + return result; + } + + /** + * @return the maximum dither amount. + */ + public float getMaxDither() { + return maxDither; + } + + /** + * Sets the maximum dither amount. Setting this to values >0 will jitter the + * interpolated colors in the calculated gradient. The value range for this + * parameter is 0.0 (off) to 1.0 (100%). + * + * @param maxDither + */ + public void setMaxDither(float maxDither) { + this.maxDither = MathUtils.clip(maxDither, 0f, 1f); + } +} diff --git a/java/Mekstension/tools/toxi/color/ColorList.java b/java/Mekstension/tools/toxi/color/ColorList.java new file mode 100644 index 0000000..93274a7 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/ColorList.java @@ -0,0 +1,616 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.color; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; + +import toxi.color.theory.ColorTheoryRegistry; +import toxi.color.theory.ColorTheoryStrategy; +import toxi.math.MathUtils; + +/** + * A container class of concrete colors. ColorLists can be built manually and + * are also created when working with {@link ColorRange}s. The class has various + * methods to manipulate all colors in the list in parallel, as well as sort + * them by various criteria. + * + * @see ColorRange + * @see AccessCriteria + * + * @author toxi + * + */ +public class ColorList implements Iterable { + + protected ArrayList colors = new ArrayList(); + + /** + * Factory method. Creates a new ColorList of colors sampled from the given + * ARGB image array. + * + * @param pixels + * int array of ARGB pixels + * @param num + * number of colors samples (clipped automatically to number of + * pixels in the image) + * @param uniqueOnly + * flag if only unique samples are to be taken (doesn't guarantee + * unique colors though) + * @return new color list + */ + public static final ColorList createFromARGBArray(int[] pixels, int num, + boolean uniqueOnly) { + num = MathUtils.min(num, pixels.length); + int[] colors = new int[num]; + int[] index = new int[num]; + for (int i = 0; i < num; i++) { + int idx; + if (uniqueOnly) { + boolean isUnique = true; + do { + idx = MathUtils.random(pixels.length); + for (int j = 0; j < i; j++) { + if (index[j] == idx) { + isUnique = false; + break; + } + } + } while (!isUnique); + } else { + idx = MathUtils.random(pixels.length); + } + index[i] = idx; + colors[i] = pixels[idx]; + } + return new ColorList(colors); + } + + /** + * Factory method. Creates a new ColorList based on the given + * {@link ColorTheoryStrategy} instance and the given source color. + * + * @param strategy + * @param c + * @return new list + */ + public static final ColorList createUsingStrategy( + ColorTheoryStrategy strategy, TColor c) { + return strategy.createListFromColor(c); + } + + /** + * Factory method. Creates a ColorList based on the + * {@link ColorTheoryStrategy} name and the given source color. + * + * @param name + * @param c + * @return new color list or null, if the supplied strategy name is not + * mapped to a registered implementation. + */ + public static final ColorList createUsingStrategy(String name, TColor c) { + ColorTheoryStrategy strategy = ColorTheoryRegistry + .getStrategyForName(name); + ColorList list = null; + if (strategy != null) { + list = strategy.createListFromColor(c); + } + return list; + } + + /** + * Creates an empty list. + */ + public ColorList() { + + } + + /** + * Creates a ColorList by wrapping the given ArrayList of colors. No copies + * of the given colors are created (shallow copy only). + * + * @param colors + */ + public ColorList(ArrayList colors) { + this.colors.addAll(colors); + } + + /** + * Creates a deep copy of the given ColorList. Manipulating the new list + * does NOT change the colors of the original. + * + * @param list + * source list + */ + public ColorList(ColorList list) { + for (TColor c : list) { + this.colors.add(c.copy()); + } + } + + /** + * Creates a new color list from the array of ARGB int values. + * + * @param argbArray + */ + public ColorList(int[] argbArray) { + for (int c : argbArray) { + colors.add(TColor.newARGB(c)); + } + } + + /** + * Creates a color list with the supplied color as first entry. + * + * @param c + * color + */ + public ColorList(TColor c) { + this.colors.add(c.copy()); + } + + /** + * Creates new ColorList from the given array of colors No copies of the + * given colors are created (shallow copy only). + * + * @param colorArray + */ + public ColorList(TColor[] colorArray) { + for (TColor c : colorArray) { + colors.add(c); + } + } + + /** + * Adds the given color to the list + * + * @param c + * @return itself + */ + public ColorList add(TColor c) { + colors.add(c); + return this; + } + + /** + * Adds all entries of the TColor collection to the list (shallow copy only, + * manipulating the new list will modify the original colors). + * + * @param collection + * @return itself + */ + public ColorList addAll(Collection collection) { + colors.addAll(collection); + return this; + } + + /** + * Adjusts the brightness component of all list colors by the given amount. + * + * @param step + * adjustment value + * @return itself + */ + public ColorList adjustBrightness(float step) { + for (TColor c : colors) { + c.lighten(step); + } + return this; + } + + /** + * Adjusts the saturation component of all list colors by the given amount. + * + * @param step + * adjustment value + * @return itself + */ + public ColorList adjustSaturation(float step) { + for (TColor c : colors) { + c.saturate(step); + } + return this; + } + + /** + * Sorts the list based on two criteria to create clusters/segments within + * the list. + * + * @param clusterCriteria + * main sort criteria + * @param subClusterCriteria + * secondary sort criteria + * @param numClusters + * number of clusters + * @param isReversed + * true, if reversed sort + * @return itself + */ + public ColorList clusterSort(AccessCriteria clusterCriteria, + AccessCriteria subClusterCriteria, int numClusters, + boolean isReversed) { + ArrayList sorted = new ArrayList(colors); + Collections.sort(sorted, clusterCriteria.getComparator()); + Collections.reverse(sorted); + ArrayList clusters = new ArrayList(); + + float d = 1; + int i = 0; + int num = sorted.size(); + for (int j = 0; j < num; j++) { + TColor c = sorted.get(j); + if (c.getComponentValue(clusterCriteria) < d) { + ArrayList slice = new ArrayList(); + slice.addAll(sorted.subList(i, j)); + Collections.sort(slice, subClusterCriteria.getComparator()); + clusters.addAll(slice); + d -= 1.0f / numClusters; + i = j; + } + } + ArrayList slice = new ArrayList(); + slice.addAll(sorted.subList(i, sorted.size())); + Collections.sort(slice, subClusterCriteria.getComparator()); + clusters.addAll(slice); + if (isReversed) { + Collections.reverse(clusters); + } + colors = clusters; + return this; + } + + /** + * Switches all list colors to their complementary color. + * + * @return itself + */ + public ColorList complement() { + for (TColor c : colors) { + c.complement(); + } + return this; + } + + /** + * Checks if the given color is part of the list. Check is done by value, + * not instance. + * + * @param color + * @return true, if the color is present. + */ + public boolean contains(TColor color) { + for (TColor c : colors) { + if (c.equals(color)) { + return true; + } + } + return false; + } + + /** + * Returns the color at the given index. + * + * @param i + * @return color + */ + public TColor get(int i) { + return colors.get(i); + } + + /** + * Calculates and returns the average color of the list. + * + * @return average color or null, if there're no entries yet. + */ + public TColor getAverage() { + float r = 0; + float g = 0; + float b = 0; + float a = 0; + for (TColor c : colors) { + r += c.rgb[0]; + g += c.rgb[1]; + b += c.rgb[2]; + a += c.alpha; + } + int num = colors.size(); + if (num > 0) { + return TColor.newRGBA(r / num, g / num, b / num, a / num); + } else { + return null; + } + } + + /** + * Creates a new ColorList by blending all colors in the list with each + * other (successive indices only) + * + * @param amount + * blend amount + * @return new color list + */ + public ColorList getBlended(float amount) { + TColor[] clrs = new TColor[colors.size()]; + for (int i = 0; i < clrs.length; i++) { + TColor c = colors.get(i > 0 ? i - 1 : clrs.length - 1); + clrs[i] = colors.get(i).getBlended(c, amount); + } + return new ColorList(clrs); + } + + /** + * Finds and returns the darkest color of the list. + * + * @return darkest color or null if there're no entries yet. + */ + public TColor getDarkest() { + TColor darkest = null; + float minBrightness = Float.MAX_VALUE; + for (TColor c : colors) { + float luma = c.luminance(); + if (luma < minBrightness) { + darkest = c; + minBrightness = luma; + } + } + return darkest; + } + + /** + * Finds and returns the lightest (luminance) color of the list. + * + * @return lightest color or null, if there're no entries yet. + */ + public TColor getLightest() { + TColor lightest = null; + float maxBrightness = Float.MIN_VALUE; + for (TColor c : colors) { + float luma = c.luminance(); + if (luma > maxBrightness) { + lightest = c; + maxBrightness = luma; + } + } + return lightest; + } + + /** + * Returns a reversed copy of the current list. + * + * @return reversed copy of the list + */ + public ColorList getReverse() { + return new ColorList(colors).reverse(); + } + + /** + * Inverts all colors in the list. + * + * @return itself + */ + public ColorList invert() { + for (TColor c : colors) { + c.invert(); + } + return this; + } + + /** + * Returns an iterator over the internal list. This means the list can be + * accessed via standard Iterator loops. + * + * @return list iterator + */ + public Iterator iterator() { + return colors.iterator(); + } + + /** + * Reverses the current order of the list. + * + * @return itself + */ + public ColorList reverse() { + Collections.reverse(colors); + return this; + } + + /** + * Rotates the hues of all colors in the list by the given amount. + * + * @param theta + * rotation angle in radians + * @return itself + */ + public ColorList rotateRYB(float theta) { + return rotateRYB(MathUtils.degrees(theta)); + } + + /** + * Rotates the hues of all colors in the list by the given amount. + * + * @param angle + * rotation angle in degrees + * @return itself + */ + public ColorList rotateRYB(int angle) { + for (TColor c : colors) { + c.rotateRYB(angle); + } + return this; + } + + /** + * @return the number of colors in the list + */ + public float size() { + return colors.size(); + } + + /** + * Convenience method. Sorts the list by hue. + * + * @return itself + */ + public ColorList sort() { + return sortByCriteria(AccessCriteria.HUE, false); + } + + /** + * Sorts the list using the given comparator. + * + * @param comp + * comparator + * @param isReversed + * true, if reversed sort + * @return itself + */ + public ColorList sortByComparator(Comparator comp, + boolean isReversed) { + Collections.sort(colors, comp); + if (isReversed) { + Collections.reverse(colors); + } + return this; + } + + /** + * Sorts the list using the given {@link AccessCriteria}. + * + * @param criteria + * sort criteria + * @param isReversed + * true, if reversed sort + * @return itself + */ + public ColorList sortByCriteria(AccessCriteria criteria, boolean isReversed) { + Comparator comparator = criteria.getComparator(); + if (comparator != null) { + return sortByComparator(comparator, isReversed); + } else { + return this; + } + } + + /** + * Sorts the list by relative distance to each predecessor, starting with + * the darkest color in the list. + * + * @param isReversed + * true, if list is to be sorted in reverse. + * @return itself + */ + public ColorList sortByDistance(boolean isReversed) { + return sortByDistance(new HSVDistanceProxy(), isReversed); + } + + /** + * Sorts the list by relative distance to each predecessor, starting with + * the darkest color in the list. + * + * @param isReversed + * true, if list is to be sorted in reverse. + * @return itself + */ + public ColorList sortByDistance(DistanceProxy proxy, boolean isReversed) { + if (colors.size() == 0) { + return this; + } + + TColor root = getDarkest(); + + // Remove the darkest color from the stack, + // put it in the sorted list as starting element. + ArrayList stack = new ArrayList(colors); + stack.remove(root); + ArrayList sorted = new ArrayList(colors.size()); + sorted.add(root); + + // Now find the color in the stack closest to that color. + // Take this color from the stack and add it to the sorted list. + // Now find the color closest to that color, etc. + int sortedCount = 0; + while (stack.size() > 1) { + TColor closest = stack.get(0); + TColor lastSorted = sorted.get(sortedCount); + float distance = proxy.distanceBetween(closest, lastSorted); + for (int i = stack.size() - 1; i >= 0; i--) { + TColor c = stack.get(i); + float d = proxy.distanceBetween(c, lastSorted); + if (d < distance) { + closest = c; + distance = d; + } + } + stack.remove(closest); + sorted.add(closest); + sortedCount++; + } + sorted.add(stack.get(0)); + if (isReversed) { + Collections.reverse(sorted); + } + colors = sorted; + return this; + } + + /** + * Sorts the list by proximity to the given target color (using RGB distance + * metrics). + * + * @see #sortByProximityTo(TColor, DistanceProxy, boolean) + * @param target + * color + * @param isReversed + * true, if reverse sorted + * @return sorted list + */ + public ColorList sortByProximityTo(TColor target, boolean isReversed) { + return sortByProximityTo(target, new RGBDistanceProxy(), isReversed); + } + + /** + * Sorts the list by proximity to the given target color using the given + * {@link DistanceProxy} implementation. + * + * @param target + * color + * @param proxy + * distance metrics + * @param isReversed + * true, if reverse sorted + * @return sorted list + */ + public ColorList sortByProximityTo(TColor target, DistanceProxy proxy, + boolean isReversed) { + return sortByComparator(new ProximityComparator(target, proxy), + isReversed); + } +} diff --git a/java/Mekstension/tools/toxi/color/ColorRange.java b/java/Mekstension/tools/toxi/color/ColorRange.java new file mode 100644 index 0000000..6cd9c79 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/ColorRange.java @@ -0,0 +1,645 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.color; + +import java.lang.reflect.Field; +import java.util.HashMap; + +import toxi.math.MathUtils; +import toxi.util.datatypes.FloatRange; +import toxi.util.datatypes.GenericSet; + +/** + * A ColorRange is a set of contraints to specify possible ranges for hue, + * saturation, brightness and alpha independently and use these as creation + * rules for new {@link TColor}s or {@link ColorList}s. The class comes with 11 + * preset ranges reflecting common demands and color characters. You can also + * construct new ranges and manually add additional constraints. Unless the + * constraints in a range are very narrow the class will always create random + * variations within the constraints. Please see the examples for further + * details. + * + * {@link ColorRange}s are a key ingredient for defining {@link ColorTheme}s + * but can also be used individually. + * + * @author toxi + */ +public class ColorRange { + + /** + * Default hue variance for {@link #getColor(TColor, float)}. + */ + public static final float DEFAULT_VARIANCE = 0.035f; + + /** + * Shade definition: saturation 30-70%, brightness: 90-100% + */ + public static final ColorRange LIGHT = new ColorRange(null, + new FloatRange(0.3f, 0.7f), new FloatRange(0.9f, 1.0f), null, + new FloatRange(0.15f, 0.30f), null, "light"); + + /** + * Shade definition: saturation 70-100%, brightness: 15-40% + */ + public static final ColorRange DARK = new ColorRange(null, + new FloatRange(0.7f, 1.0f), new FloatRange(0.15f, 0.4f), null, + null, new FloatRange(0.5f, 0.75f), "dark"); + + /** + * Shade definition: saturation 80-100%, brightness: 80-100% + */ + public static final ColorRange BRIGHT = new ColorRange(null, + new FloatRange(0.8f, 1.0f), new FloatRange(0.8f, 1.0f), "bright"); + + /** + * Shade definition: saturation 15-30%, brightness: 70-100% + */ + public static final ColorRange WEAK = new ColorRange(null, + new FloatRange(0.15f, 0.3f), new FloatRange(0.7f, 1.0f), null, + new FloatRange(0.2f, 0.2f), null, "weak"); + + /** + * Shade definition: saturation 25-35%, brightness: 30-70% + */ + public static final ColorRange NEUTRAL = new ColorRange(null, + new FloatRange(0.25f, 0.35f), new FloatRange(0.3f, 0.7f), null, + new FloatRange(0.15f, 0.15f), new FloatRange(0.9f, 1), "neutral"); + + /** + * Shade definition: saturation 40-80%, brightness: 80-100% + */ + public static final ColorRange FRESH = new ColorRange(null, + new FloatRange(0.4f, 0.8f), new FloatRange(0.8f, 1.0f), null, + new FloatRange(0.05f, 0.3f), new FloatRange(0.8f, 1.0f), "fresh"); + + /** + * Shade definition: saturation 20-30%, brightness: 60-90% + */ + public static final ColorRange SOFT = new ColorRange(null, + new FloatRange(0.2f, 0.3f), new FloatRange(0.6f, 0.9f), null, + new FloatRange(0.05f, 0.15f), new FloatRange(0.6f, 0.9f), "soft"); + + /** + * Shade definition: saturation 90-100%, brightness: 40-100% + */ + public static final ColorRange HARD = new ColorRange(null, + new FloatRange(0.9f, 1.0f), new FloatRange(0.4f, 1.0f), "hard"); + + /** + * Shade definition: saturation 60-90%, brightness: 40-90% + */ + public static final ColorRange WARM = new ColorRange(null, + new FloatRange(0.6f, 0.9f), new FloatRange(0.4f, 0.9f), null, + new FloatRange(0.2f, 0.2f), new FloatRange(0.8f, 1.0f), "warm"); + + /** + * Shade definition: saturation 5-20%, brightness: 90-100% + */ + public static final ColorRange COOL = new ColorRange(null, + new FloatRange(0.05f, 0.2f), new FloatRange(0.9f, 1.0f), null, + null, new FloatRange(0.95f, 1.0f), "cool"); + + /** + * Shade definition: saturation 90-100%, brightness: 20-35% or 80-100% + */ + public static final ColorRange INTENSE = new ColorRange(null, + new FloatRange(0.9f, 1.0f), new FloatRange(0.2f, 0.35f), "intense") + .addBrightnessRange(new FloatRange(0.8f, 1.0f)); + + /** + * List of ColorRange presets. + */ + public static final HashMap PRESETS = new HashMap(); + + private static int UNTITLED_ID = 1; + + protected GenericSet hueConstraint; + + protected GenericSet saturationConstraint; + protected GenericSet brightnessConstraint; + protected GenericSet alphaConstraint; + protected FloatRange white; + + protected FloatRange black; + protected String name; + + static { + Field[] fields = ColorRange.class.getDeclaredFields(); + try { + for (Field f : fields) { + if (f.getType() == ColorRange.class) { + String id = f.getName(); + PRESETS.put(id, (ColorRange) f.get(null)); + } + } + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + /** + * Retrieves one of the predefined ranges by name. + * + * @param name + * @return color range or null if name not registered + */ + public static ColorRange getPresetForName(String name) { + return PRESETS.get(name.toUpperCase()); + } + + /** + * Only used internally by {@link #copy()}, doesn't initialize anything. + */ + private ColorRange() { + + } + + /** + * Constructs a new range using the hue of the given color as hue + * constraint, but saturation and brightness are fully flexible. The + * resulting range will produce any shade of the given color. + * + * @param c + * base color + */ + public ColorRange(TColor c) { + this(new FloatRange(c.hue(), c.hue()), null, null, null, null, null, + null); + } + + /** + * Constructs a new range using the given colors as HSV constraints. + * + * @param list + * list base colors + */ + public ColorRange(ColorList list) { + this(list.get(0)); + hueConstraint.clear(); + for (TColor c : list) { + add(c); + } + } + + /** + * Constructs a new range with the supplied constraints (if an HSV argument + * is null, a range of 0.0 ... 1.0 is created automatically for that + * constraint). If alpha is left undefined, it'll be initialized to fully + * opaque only. You can also specify ranges for possible black and white + * points which are used if the range is later applied to a grayscale + * color. The default black point is at 0.0 and white at 1.0. + * + * @param hue + * @param sat + * @param bri + * @param alpha + * @param black + * @param white + * @param name + */ + public ColorRange(FloatRange hue, FloatRange sat, FloatRange bri, + FloatRange alpha, FloatRange black, FloatRange white, String name) { + super(); + hueConstraint = new GenericSet(hue != null ? hue + : new FloatRange(0, 1)); + saturationConstraint = new GenericSet(sat != null ? sat + : new FloatRange(0, 1)); + brightnessConstraint = new GenericSet(bri != null ? bri + : new FloatRange(0, 1)); + alphaConstraint = new GenericSet(alpha != null ? alpha + : new FloatRange(1, 1)); + if (black == null) { + this.black = new FloatRange(0, 0); + } else { + this.black = black; + } + if (white == null) { + this.white = new FloatRange(1, 1); + } else { + this.white = white; + } + this.name = name != null ? name : "untitled" + (UNTITLED_ID++); + } + + /** + * Constructs a new range with the supplied constraints (if an argument is + * null, a range of 0.0 ... 1.0 is created automatically for that + * constraint). + * + * @param hue + * min/max hue range + * @param sat + * min/max saturation range + * @param bri + * min/max brightness range + * @param alpha + * min/max alpha range (if null, initialized to 100% only) + * @param name + */ + public ColorRange(FloatRange hue, FloatRange sat, FloatRange bri, + FloatRange alpha, String name) { + this(hue, sat, bri, alpha, null, null, name); + } + + /** + * Constructs a new range with the supplied constraints (if an argument is + * null, a range of 0.0 ... 1.0 is created automatically for that + * constraint). Alpha constraint will be set to 100%. + * + * @param hue + * min/max hue range + * @param sat + * min/max saturation range + * @param bri + * min/max brightness range + * @param name + */ + public ColorRange(FloatRange hue, FloatRange sat, FloatRange bri, + String name) { + this(hue, sat, bri, null, null, null, name); + } + + /** + * Constructs a new range using the given hue as constraint, but saturation + * and brightness are fully flexible. The resulting range will produce any + * shade of the given hue. + * + * @param hue + * base hue + */ + public ColorRange(Hue hue) { + this(new FloatRange(hue.getHue(), hue.getHue()), null, null, null, + null, null, null); + } + + /** + * Adds the HSV color components as constraints. + * + * @param c + * color to use as constraint + * @return itself + */ + public ColorRange add(TColor c) { + hueConstraint.add(new FloatRange(c.hue(), c.hue())); + saturationConstraint + .add(new FloatRange(c.saturation(), c.saturation())); + brightnessConstraint + .add(new FloatRange(c.brightness(), c.brightness())); + alphaConstraint.add(new FloatRange(c.alpha, c.alpha)); + return this; + } + + /** + * Adds the contraints of the given range to this range and forms unions for + * the black and white point ranges. + * + * @param range + * color range to add + * @return itself + */ + public ColorRange add(ColorRange range) { + hueConstraint.addAll(range.hueConstraint.getItems()); + saturationConstraint.addAll(range.saturationConstraint.getItems()); + brightnessConstraint.addAll(range.brightnessConstraint.getItems()); + alphaConstraint.addAll(range.alphaConstraint.getItems()); + black.min = MathUtils.min(black.min, range.black.min); + black.max = MathUtils.max(black.max, range.black.max); + white.min = MathUtils.min(white.min, range.white.min); + white.max = MathUtils.max(white.max, range.white.max); + return this; + } + + /** + * Adds the range between min-max as possible alpha values for this range. + * + * @param min + * @param max + * @return itself + */ + public ColorRange addAlphaRange(float min, float max) { + return addAlphaRange(new FloatRange(min, max)); + } + + /** + * Adds an additional alpha constraint. + * + * @param alpha + * min/max alpha values + * @return itself + */ + public ColorRange addAlphaRange(FloatRange alpha) { + alphaConstraint.add(alpha); + return this; + } + + /** + * Adds the range between min-max as possible brightness values for this + * range. + * + * @param min + * @param max + * @return itself + */ + public ColorRange addBrightnessRange(float min, float max) { + return addBrightnessRange(new FloatRange(min, max)); + } + + /** + * Adds an additional brightness constraint. + * + * @param bri + * min/max brightness values + * @return itself + */ + public ColorRange addBrightnessRange(FloatRange bri) { + brightnessConstraint.add(bri); + return this; + } + + /** + * Add the given hue as hue constraint. + * + * @param hue + * @return itself + */ + public ColorRange addHue(Hue hue) { + hueConstraint.add(new FloatRange(hue.getHue(), hue.getHue())); + return this; + } + + /** + * Adds the range between min-max as possible hue values for this range. If + * max < min then two intervals are added: {min ... 1.0} and {0.0 ... max} + * + * @param min + * @param max + * @return itself + */ + public ColorRange addHueRange(float min, float max) { + if (max >= min) { + addHueRange(new FloatRange(min, max)); + } else { + addHueRange(new FloatRange(min, 1)); + addHueRange(new FloatRange(0, max)); + } + return this; + } + + /** + * Adds an additional hue constraint. + * + * @param hue + * min/max hue values + * @return itself + */ + public ColorRange addHueRange(FloatRange hue) { + hueConstraint.add(hue); + return this; + } + + /** + * Adds the range between min-max as possible saturation values for this + * range. + * + * @param min + * @param max + * @return itself + */ + public ColorRange addSaturationRange(float min, float max) { + return addAlphaRange(new FloatRange(min, max)); + } + + /** + * Adds an additional saturation constraint. + * + * @param sat + * min/max saturation values + * @return itself + */ + public ColorRange addSaturationRange(FloatRange sat) { + saturationConstraint.add(sat); + return this; + } + + /** + * Checks if all HSVA components of the given color are within the + * constraints defined for this range. + * + * @param c + * color to check + * @return true, if color is contained + */ + public boolean contains(TColor c) { + boolean isInRange = isValueInConstraint(c.hue(), hueConstraint); + isInRange &= isValueInConstraint(c.saturation(), saturationConstraint); + isInRange &= isValueInConstraint(c.brightness(), brightnessConstraint); + isInRange &= isValueInConstraint(c.alpha(), alphaConstraint); + return isInRange; + } + + /** + * Creates a shallow copy of the range. + * + * @return copy + */ + public ColorRange copy() { + return copy(null, 0); + } + + /** + * Creates a copy of the range but overrides the hue and alpha constraints + * taken from the given color (if specified). + * + * @param c + * color, if the new range is to be used to create specific + * shades of that color only + * @param variance + * hue variance (use {@link #DEFAULT_VARIANCE} for default) + * @return copy + */ + public ColorRange copy(TColor c, float variance) { + ColorRange range = new ColorRange(); + range.name = name; + + if (c != null) { + float hue = c.hue() + variance * MathUtils.normalizedRandom(); + range.hueConstraint = new GenericSet(new FloatRange( + hue, hue)); + range.alphaConstraint = new GenericSet(new FloatRange( + c.alpha, c.alpha)); + } else { + range.hueConstraint = hueConstraint.copy(); + range.alphaConstraint = alphaConstraint.copy(); + } + range.saturationConstraint = saturationConstraint.copy(); + range.brightnessConstraint = brightnessConstraint.copy(); + + range.black = black.copy(); + range.white = white.copy(); + return range; + } + + /** + * Creates a new color based on the flexible constraints of the range. + * + * @return color + */ + public TColor getColor() { + return getColor(null, 0); + } + + /** + * Creates a new color based on the constraints defined in the range. If an + * input color is specified, the method will use the hue of that color and + * the given variance to create a shade of a hue within the tolerance. + * + * @param c + * @param variance + * @return color + */ + public TColor getColor(TColor c, float variance) { + float h, s, b, a; + if (c != null) { + if (c.isBlack()) { + return TColor + .newHSVA(c.hue(), 0, black.pickRandom(), c.alpha()); + } else if (c.isWhite()) { + return TColor + .newHSVA(c.hue(), 0, white.pickRandom(), c.alpha()); + } + if (c.isGrey()) { + return TColor.newHSVA(c.hue(), 0, MathUtils.flipCoin() ? black + .pickRandom() : white.pickRandom(), c.alpha()); + } + h = c.hue() + variance * MathUtils.normalizedRandom(); + a = c.alpha(); + } else { + h = hueConstraint.pickRandom().pickRandom(); + a = alphaConstraint.pickRandom().pickRandom(); + } + s = saturationConstraint.pickRandom().pickRandom(); + b = brightnessConstraint.pickRandom().pickRandom(); + return TColor.newHSVA(h, s, b, a); + } + + /** + * Creates a new shade of the given hue based on the other constraints of + * the range. + * + * @param hue + * @return color + */ + public TColor getColor(Hue hue) { + return TColor.newHSVA(hue.getHue(), saturationConstraint.pickRandom() + .pickRandom(), brightnessConstraint.pickRandom().pickRandom(), + alphaConstraint.pickRandom().pickRandom()); + } + + /** + * Creates a new {@link ColorList} of shades of the given {@link TColor} + * based on the other constraints of the range. + * + * @see #getColor(TColor, float) + * @param c + * base color + * @param num + * number of colors to create + * @param variance + * hue variance + * @return color list + */ + public ColorList getColors(TColor c, int num, float variance) { + ColorList list = new ColorList(); + for (int i = 0; i < num; i++) { + list.add(getColor(c, variance)); + } + return list; + } + + /** + * Creates a new {@link ColorList} of colors based on the constraints of + * this range. + * + * @see #getColor() + * @param num + * number of colors to create + * @return color list + */ + public ColorList getColors(int num) { + return getColors(null, num, DEFAULT_VARIANCE); + } + + /** + * Creates a new shade of gray based on the input brightness and the black + * and white constraints of the range. + * + * @param brightness + * input brightness + * @param variance + * hue variance (this might seem irrevelant, but might be + * important if the created color is being saturated later on) + * @return color/shade of gray + */ + public TColor getGrayscale(float brightness, float variance) { + return getColor(TColor.newGray(brightness), variance); + } + + /** + * @return name of the range + */ + public String getName() { + return name; + } + + /** + * Creates a copy of the current range and adds the given one to it. + * + * @see #add(ColorRange) + * @param range + * range to add + * @return summed copy + */ + public ColorRange getSum(ColorRange range) { + return copy().add(range); + } + + /** + * @param val + * @param rangeSet + * @return true, if value is within range + */ + protected boolean isValueInConstraint(float val, + GenericSet rangeSet) { + boolean isValid = false; + for (FloatRange r : rangeSet) { + isValid |= r.isValueInRange(val); + } + return isValid; + } +} \ No newline at end of file diff --git a/java/Mekstension/tools/toxi/color/ColorTheme.java b/java/Mekstension/tools/toxi/color/ColorTheme.java new file mode 100644 index 0000000..6e92267 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/ColorTheme.java @@ -0,0 +1,132 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +import java.util.ArrayList; +import java.util.StringTokenizer; + +import toxi.math.MathUtils; + +/** + * A ColorTheme is a weighted collection of {@link ColorRange}s used to define + * custom palettes with a certain balance between individual colors/shades. New + * theme parts can be added via textual descriptors referring to one of the + * preset {@link ColorRange}s and/or {@link NamedColor}s: e.g. + * "warm springgreen". For each theme part a weight has to be specified. The + * magnitude of the weight value is irrelevant and is only important in relation + * to the weights of other theme parts. For example: Theme part A with a weight + * of 0.5 will only have 1/20 of the weight of theme part B with a weight of + * 5.0... + * + * @author toxi + * + */ +public class ColorTheme { + + class ThemePart { + ColorRange range; + TColor col; + float weight; + + ThemePart(ColorRange range, TColor col, float weight) { + this.range = range; + this.col = col; + this.weight = weight; + } + + public TColor getColor() { + return range.getColor(col, ColorRange.DEFAULT_VARIANCE); + } + } + + protected String name; + protected ArrayList parts = new ArrayList(); + + protected float weightedSum; + + public ColorTheme(String name) { + this.name = name; + } + + public ColorTheme addRange(ColorRange range, TColor col, float weight) { + parts.add(new ThemePart(range, col, weight)); + weightedSum += weight; + return this; + } + + public ColorTheme addRange(String descriptor, float weight) { + StringTokenizer st = new StringTokenizer(descriptor, " ,"); + TColor col = null; + ColorRange range = null; + while (st.hasMoreTokens()) { + String item = st.nextToken(); + if (ColorRange.getPresetForName(item) != null) { + range = ColorRange.getPresetForName(item); + } else if (NamedColor.getForName(item) != null) { + col = NamedColor.getForName(item); + } + } + if (range != null) { + addRange(range, col, weight); + } + return this; + } + + public TColor getColor() { + float rnd = MathUtils.random(1f); + for (ThemePart t : parts) { + float currWeight = t.weight / weightedSum; + if (currWeight >= rnd) { + return t.getColor(); + } + rnd -= currWeight; + } + return null; + } + + /** + * Creates a {@link ColorList} of {@link TColor}s based on the theme's + * ranges and balance defined by their weights + * + * @param num + * number of colors to create + * @return new list + */ + public ColorList getColors(int num) { + ColorList list = new ColorList(); + for (int i = 0; i < num; i++) { + list.add(getColor()); + } + return list; + } + + /** + * @return the theme's name + */ + public String getName() { + return name; + } +} diff --git a/java/Mekstension/tools/toxi/color/DistanceProxy.java b/java/Mekstension/tools/toxi/color/DistanceProxy.java new file mode 100644 index 0000000..12f50bb --- /dev/null +++ b/java/Mekstension/tools/toxi/color/DistanceProxy.java @@ -0,0 +1,44 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +/** + * A proxy interface to support the sorting of colors in different color + * spaces. Used by {@link ColorList#sortByDistance(DistanceProxy, boolean)}. + * + * @author toxi + * + */ +public interface DistanceProxy { + /** + * Computes the distance between 2 colors. + * + * @param a + * @param b + * @return distance + */ + float distanceBetween(TColor a, TColor b); +} diff --git a/java/Mekstension/tools/toxi/color/HSVComparator.java b/java/Mekstension/tools/toxi/color/HSVComparator.java new file mode 100644 index 0000000..98af010 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/HSVComparator.java @@ -0,0 +1,48 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +import java.util.Comparator; + +/** + * Compares 2 colors by one of their HSV component values. + * + * @author toxi + * + */ +public class HSVComparator implements Comparator { + + private final int component; + + public HSVComparator(int comp) { + component = comp; + } + + public int compare(TColor a, TColor b) { + return Float.compare(a.hsv[component], b.hsv[component]); + } + +} diff --git a/java/Mekstension/tools/toxi/color/HSVDistanceProxy.java b/java/Mekstension/tools/toxi/color/HSVDistanceProxy.java new file mode 100644 index 0000000..4aa2979 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/HSVDistanceProxy.java @@ -0,0 +1,40 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +/** + * Implements the {@link DistanceProxy} interface to sort colors by HSV + * distance (used by {@link ColorList#sortByDistance(DistanceProxy, boolean)}). + * + * @author toxi + * + */ +public class HSVDistanceProxy implements DistanceProxy { + + public float distanceBetween(TColor a, TColor b) { + return a.distanceToHSV(b); + } +} diff --git a/java/Mekstension/tools/toxi/color/Hue.java b/java/Mekstension/tools/toxi/color/Hue.java new file mode 100644 index 0000000..5399358 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/Hue.java @@ -0,0 +1,143 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +import java.util.ArrayList; +import java.util.HashMap; + +import toxi.math.MathUtils; + +/** + * This class defines color hues and allows them to be access by name. There + * are also methods to check if a hue is one of the 7 primary hues (rainbow) or + * to find the closest defined hue for a given color. + * + * @author toxi + * + */ +public class Hue { + + protected static final HashMap namedHues = new HashMap(); + protected static final ArrayList primaryHues = new ArrayList(); + + public static final Hue RED = new Hue("red", 0, true); + public static final Hue ORANGE = new Hue("orange", 30 / 360.0f, true); + public static final Hue YELLOW = new Hue("yellow", 60 / 360.0f, true); + public static final Hue LIME = new Hue("lime", 90 / 360.0f); + public static final Hue GREEN = new Hue("green", 120 / 360.0f, true); + public static final Hue TEAL = new Hue("teal", 150 / 360.0f); + public static final Hue CYAN = new Hue("cyan", 180 / 360.0f); + public static final Hue AZURE = new Hue("azure", 210 / 360.0f); + public static final Hue BLUE = new Hue("blue", 240 / 360.0f, true); + public static final Hue INDIGO = new Hue("indigo", 270 / 360.0f); + public static final Hue PURPLE = new Hue("purple", 300 / 360.0f, true); + public static final Hue PINK = new Hue("pink", 330 / 360.0f, true); + + /** + * Tolerance value for checking if a given hue is primary (default 0.01) + */ + public static float PRIMARY_VARIANCE = 0.01f; + + protected String name; + protected float hue; + protected boolean isPrimary; + + /** + * Finds the closest defined & named Hue for the given hue value. + * Optionally, the search can be limited to primary hues only. + * + * @param hue + * normalized hue (0.0 ... 1.0) will be automatically wrapped + * @param primaryOnly + * only consider the 7 primary hues + * @return closest Hue instance + */ + public static final Hue getClosest(float hue, boolean primaryOnly) { + hue %= 1; + float dist = Float.MAX_VALUE; + Hue closest = null; + Iterable hues = (primaryOnly ? primaryHues : namedHues.values()); + for (Hue h : hues) { + float d = MathUtils.min(MathUtils.abs(h.hue - hue), MathUtils.abs(1 + + h.hue - hue)); + if (d < dist) { + dist = d; + closest = h; + } + } + return closest; + } + + public static final Hue getForName(String name) { + return namedHues.get(name.toLowerCase()); + } + + public static boolean isPrimary(float hue) { + return isPrimary(hue, PRIMARY_VARIANCE); + } + + public static boolean isPrimary(float hue, float variance) { + boolean isPrimary = false; + for (Hue h : primaryHues) { + if (MathUtils.abs(hue - h.hue) < variance) { + isPrimary = true; + break; + } + } + return isPrimary; + } + + public Hue(String name, float hue) { + this(name, hue, false); + } + + public Hue(String name, float hue, boolean isPrimary) { + this.name = name; + this.hue = hue; + this.isPrimary = isPrimary; + namedHues.put(name, this); + if (isPrimary) { + primaryHues.add(this); + } + } + + public float getHue() { + return hue; + } + + public String getName() { + return name; + } + + public boolean isPrimary() { + return isPrimary; + } + + @Override + public String toString() { + return "Hue: ID:" + name + " @ " + (int) (hue * 360) + " degrees"; + } +} \ No newline at end of file diff --git a/java/Mekstension/tools/toxi/color/LuminanceComparator.java b/java/Mekstension/tools/toxi/color/LuminanceComparator.java new file mode 100644 index 0000000..f67bc8d --- /dev/null +++ b/java/Mekstension/tools/toxi/color/LuminanceComparator.java @@ -0,0 +1,49 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +import java.util.Comparator; + +/** + * Compares 2 colors by their luminance values. + * + * @author toxi + * + */ +public class LuminanceComparator implements Comparator { + + public int compare(TColor a, TColor b) { + float lumA = a.luminance(); + float lumB = b.luminance(); + if (lumA < lumB) + return -1; + if (lumA > lumB) + return 1; + else + return 0; + } + +} diff --git a/java/Mekstension/tools/toxi/color/NamedColor.java b/java/Mekstension/tools/toxi/color/NamedColor.java new file mode 100644 index 0000000..c4f35d0 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/NamedColor.java @@ -0,0 +1,251 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Defines a list of named colors across the spectrum and provides a means to + * access them by name (strings) dynamically (e.g. using config files or when + * building {@link ColorTheme}s) + * + * @author toxi + * + */ +public class NamedColor { + + public static final TColor ALICEBLUE = TColor.newRGB(0.94f, 0.97f, 1.00f); + public static final TColor ANTIQUEWHITE = TColor + .newRGB(0.98f, 0.92f, 0.84f); + public static final TColor AQUA = TColor.newRGB(0.00f, 1.00f, 1.00f); + public static final TColor AQUAMARINE = TColor.newRGB(0.50f, 1.00f, 0.83f); + public static final TColor AZURE = TColor.newRGB(0.94f, 1.00f, 1.00f); + public static final TColor BARK = TColor.newRGB(0.25f, 0.19f, 0.13f); + public static final TColor BEIGE = TColor.newRGB(0.96f, 0.96f, 0.86f); + public static final TColor BISQUE = TColor.newRGB(1.00f, 0.89f, 0.77f); + public static final TColor BLACK = TColor.newRGB(0.00f, 0.00f, 0.00f); + public static final TColor BLANCHEDALMOND = TColor.newRGB(1.00f, 0.92f, + 0.80f); + public static final TColor BLUE = TColor.newRGB(0.00f, 0.00f, 1.00f); + public static final TColor BLUEVIOLET = TColor.newRGB(0.54f, 0.17f, 0.89f); + public static final TColor BROWN = TColor.newRGB(0.65f, 0.16f, 0.16f); + public static final TColor BURLYWOOD = TColor.newRGB(0.87f, 0.72f, 0.53f); + public static final TColor CADETBLUE = TColor.newRGB(0.37f, 0.62f, 0.63f); + public static final TColor CHARTREUSE = TColor.newRGB(0.50f, 1.00f, 0.00f); + public static final TColor CHOCOLATE = TColor.newRGB(0.82f, 0.41f, 0.12f); + public static final TColor CORAL = TColor.newRGB(1.00f, 0.50f, 0.31f); + public static final TColor CORNFLOWERBLUE = TColor.newRGB(0.39f, 0.58f, + 0.93f); + public static final TColor CORNSILK = TColor.newRGB(1.00f, 0.97f, 0.86f); + public static final TColor CRIMSON = TColor.newRGB(0.86f, 0.08f, 0.24f); + public static final TColor CYAN = TColor.newRGB(0.00f, 0.68f, 0.94f); + public static final TColor DARKBLUE = TColor.newRGB(0.00f, 0.00f, 0.55f); + public static final TColor DARKCYAN = TColor.newRGB(0.00f, 0.55f, 0.55f); + public static final TColor DARKGOLDENROD = TColor.newRGB(0.72f, 0.53f, + 0.04f); + public static final TColor DARKGRAY = TColor.newRGB(0.66f, 0.66f, 0.66f); + public static final TColor DARKGREEN = TColor.newRGB(0.00f, 0.39f, 0.00f); + public static final TColor DARKKHAKI = TColor.newRGB(0.74f, 0.72f, 0.42f); + public static final TColor DARKMAGENTA = TColor.newRGB(0.55f, 0.00f, 0.55f); + public static final TColor DARKOLIVEGREEN = TColor.newRGB(0.33f, 0.42f, + 0.18f); + public static final TColor DARKORANGE = TColor.newRGB(1.00f, 0.55f, 0.00f); + public static final TColor DARKORCHID = TColor.newRGB(0.60f, 0.20f, 0.80f); + public static final TColor DARKRED = TColor.newRGB(0.55f, 0.00f, 0.00f); + public static final TColor DARKSALMON = TColor.newRGB(0.91f, 0.59f, 0.48f); + public static final TColor DARKSEAGREEN = TColor + .newRGB(0.56f, 0.74f, 0.56f); + public static final TColor DARKSLATEBLUE = TColor.newRGB(0.28f, 0.24f, + 0.55f); + public static final TColor DARKSLATEGRAY = TColor.newRGB(0.18f, 0.31f, + 0.31f); + public static final TColor DARKTURQUOISE = TColor.newRGB(0.00f, 0.81f, + 0.82f); + public static final TColor DARKVIOLET = TColor.newRGB(0.58f, 0.00f, 0.83f); + public static final TColor DEEPPINK = TColor.newRGB(1.00f, 0.08f, 0.58f); + public static final TColor DEEPSKYBLUE = TColor.newRGB(0.00f, 0.75f, 1.00f); + public static final TColor DIMGRAY = TColor.newRGB(0.41f, 0.41f, 0.41f); + public static final TColor DIMGREY = TColor.newRGB(0.41f, 0.41f, 0.41f); + public static final TColor DODGERBLUE = TColor.newRGB(0.12f, 0.56f, 1.00f); + public static final TColor FIREBRICK = TColor.newRGB(0.70f, 0.13f, 0.13f); + public static final TColor FLORALWHITE = TColor.newRGB(1.00f, 0.98f, 0.94f); + public static final TColor FORESTGREEN = TColor.newRGB(0.13f, 0.55f, 0.13f); + public static final TColor FUCHSIA = TColor.newRGB(1.00f, 0.00f, 1.00f); + public static final TColor GAINSBORO = TColor.newRGB(0.86f, 0.86f, 0.86f); + public static final TColor GHOSTWHITE = TColor.newRGB(0.97f, 0.97f, 1.00f); + public static final TColor GOLD = TColor.newRGB(1.00f, 0.84f, 0.00f); + public static final TColor GOLDENROD = TColor.newRGB(0.85f, 0.65f, 0.13f); + public static final TColor GRAY = TColor.newRGB(0.50f, 0.50f, 0.50f); + public static final TColor GREEN = TColor.newRGB(0.00f, 0.50f, 0.00f); + public static final TColor GREENYELLOW = TColor.newRGB(0.68f, 1.00f, 0.18f); + public static final TColor GREY = TColor.newRGB(0.50f, 0.50f, 0.50f); + public static final TColor HONEYDEW = TColor.newRGB(0.94f, 1.00f, 0.94f); + public static final TColor HOTPINK = TColor.newRGB(1.00f, 0.41f, 0.71f); + public static final TColor INDIANRED = TColor.newRGB(0.80f, 0.36f, 0.36f); + public static final TColor INDIGO = TColor.newRGB(0.29f, 0.00f, 0.51f); + public static final TColor IVORY = TColor.newRGB(1.00f, 1.00f, 0.94f); + public static final TColor KHAKI = TColor.newRGB(0.94f, 0.90f, 0.55f); + public static final TColor LAVENDER = TColor.newRGB(0.90f, 0.90f, 0.98f); + public static final TColor LAVENDERBLUSH = TColor.newRGB(1.00f, 0.94f, + 0.96f); + public static final TColor LAWNGREEN = TColor.newRGB(0.49f, 0.99f, 0.00f); + public static final TColor LEMONCHIFFON = TColor + .newRGB(1.00f, 0.98f, 0.80f); + public static final TColor LIGHTBLUE = TColor.newRGB(0.68f, 0.85f, 0.90f); + public static final TColor LIGHTCORAL = TColor.newRGB(0.94f, 0.50f, 0.50f); + public static final TColor LIGHTCYAN = TColor.newRGB(0.88f, 1.00f, 1.00f); + public static final TColor LIGHTGOLDENRODYELLOW = TColor.newRGB(0.98f, + 0.98f, 0.82f); + public static final TColor LIGHTGREEN = TColor.newRGB(0.56f, 0.93f, 0.56f); + public static final TColor LIGHTGREY = TColor.newRGB(0.83f, 0.83f, 0.83f); + public static final TColor LIGHTPINK = TColor.newRGB(1.00f, 0.71f, 0.76f); + public static final TColor LIGHTSALMON = TColor.newRGB(1.00f, 0.63f, 0.48f); + public static final TColor LIGHTSEAGREEN = TColor.newRGB(0.13f, 0.70f, + 0.67f); + public static final TColor LIGHTSKYBLUE = TColor + .newRGB(0.53f, 0.81f, 0.98f); + public static final TColor LIGHTSLATEGRAY = TColor.newRGB(0.47f, 0.53f, + 0.60f); + public static final TColor LIGHTSTEELBLUE = TColor.newRGB(0.69f, 0.77f, + 0.87f); + public static final TColor LIGHTYELLOW = TColor.newRGB(1.00f, 1.00f, 0.88f); + public static final TColor LIME = TColor.newRGB(0.00f, 1.00f, 0.00f); + public static final TColor LIMEGREEN = TColor.newRGB(0.20f, 0.80f, 0.20f); + public static final TColor LINEN = TColor.newRGB(0.98f, 0.94f, 0.90f); + public static final TColor MAROON = TColor.newRGB(0.50f, 0.00f, 0.00f); + public static final TColor MEDIUMAQUAMARINE = TColor.newRGB(0.40f, 0.80f, + 0.67f); + public static final TColor MEDIUMBLUE = TColor.newRGB(0.00f, 0.00f, 0.80f); + public static final TColor MEDIUMORCHID = TColor + .newRGB(0.73f, 0.33f, 0.83f); + public static final TColor MEDIUMPURPLE = TColor + .newRGB(0.58f, 0.44f, 0.86f); + public static final TColor MEDIUMSEAGREEN = TColor.newRGB(0.24f, 0.70f, + 0.44f); + public static final TColor MEDIUMSLATEBLUE = TColor.newRGB(0.48f, 0.41f, + 0.93f); + public static final TColor MEDIUMSPRINGGREEN = TColor.newRGB(0.00f, 0.98f, + 0.60f); + public static final TColor MEDIUMTURQUOISE = TColor.newRGB(0.28f, 0.82f, + 0.80f); + public static final TColor MEDIUMVIOLETRED = TColor.newRGB(0.78f, 0.08f, + 0.52f); + public static final TColor MIDNIGHTBLUE = TColor + .newRGB(0.10f, 0.10f, 0.44f); + public static final TColor MINTCREAM = TColor.newRGB(0.96f, 1.00f, 0.98f); + public static final TColor MISTYROSE = TColor.newRGB(1.00f, 0.89f, 0.88f); + public static final TColor MOCCASIN = TColor.newRGB(1.00f, 0.89f, 0.71f); + public static final TColor NAVAJOWHITE = TColor.newRGB(1.00f, 0.87f, 0.68f); + public static final TColor NAVY = TColor.newRGB(0.00f, 0.00f, 0.50f); + public static final TColor OLDLACE = TColor.newRGB(0.99f, 0.96f, 0.90f); + public static final TColor OLIVE = TColor.newRGB(0.50f, 0.50f, 0.00f); + public static final TColor OLIVEDRAB = TColor.newRGB(0.42f, 0.56f, 0.14f); + public static final TColor ORANGE = TColor.newRGB(1.00f, 0.65f, 0.00f); + public static final TColor ORANGERED = TColor.newRGB(1.00f, 0.27f, 0.00f); + public static final TColor ORCHID = TColor.newRGB(0.85f, 0.44f, 0.84f); + public static final TColor PALEGOLDENROD = TColor.newRGB(0.93f, 0.91f, + 0.67f); + public static final TColor PALEGREEN = TColor.newRGB(0.60f, 0.98f, 0.60f); + public static final TColor PALETURQUOISE = TColor.newRGB(0.69f, 0.93f, + 0.93f); + public static final TColor PALEVIOLETRED = TColor.newRGB(0.86f, 0.44f, + 0.58f); + public static final TColor PAPAYAWHIP = TColor.newRGB(1.00f, 0.94f, 0.84f); + public static final TColor PEACHPUFF = TColor.newRGB(1.00f, 0.85f, 0.73f); + public static final TColor PERU = TColor.newRGB(0.80f, 0.52f, 0.25f); + public static final TColor PINK = TColor.newRGB(1.00f, 0.75f, 0.80f); + public static final TColor PLUM = TColor.newRGB(0.87f, 0.63f, 0.87f); + public static final TColor POWDERBLUE = TColor.newRGB(0.69f, 0.88f, 0.90f); + public static final TColor PURPLE = TColor.newRGB(0.50f, 0.00f, 0.50f); + public static final TColor RED = TColor.newRGB(1.00f, 0.00f, 0.00f); + public static final TColor ROSYBROWN = TColor.newRGB(0.74f, 0.56f, 0.56f); + public static final TColor ROYALBLUE = TColor.newRGB(0.25f, 0.41f, 0.88f); + public static final TColor SADDLEBROWN = TColor.newRGB(0.55f, 0.27f, 0.07f); + public static final TColor SALMON = TColor.newRGB(0.98f, 0.50f, 0.45f); + public static final TColor SANDYBROWN = TColor.newRGB(0.96f, 0.64f, 0.38f); + public static final TColor SEAGREEN = TColor.newRGB(0.18f, 0.55f, 0.34f); + public static final TColor SEASHELL = TColor.newRGB(1.00f, 0.96f, 0.93f); + public static final TColor SIENNA = TColor.newRGB(0.63f, 0.32f, 0.18f); + public static final TColor SILVER = TColor.newRGB(0.75f, 0.75f, 0.75f); + public static final TColor SKYBLUE = TColor.newRGB(0.53f, 0.81f, 0.92f); + public static final TColor SLATEBLUE = TColor.newRGB(0.42f, 0.35f, 0.80f); + public static final TColor SLATEGRAY = TColor.newRGB(0.44f, 0.50f, 0.56f); + public static final TColor SNOW = TColor.newRGB(1.00f, 0.98f, 0.98f); + public static final TColor SPRINGGREEN = TColor.newRGB(0.00f, 1.00f, 0.50f); + public static final TColor STEELBLUE = TColor.newRGB(0.27f, 0.51f, 0.71f); + public static final TColor TAN = TColor.newRGB(0.82f, 0.71f, 0.55f); + public static final TColor TEAL = TColor.newRGB(0.00f, 0.50f, 0.50f); + public static final TColor THISTLE = TColor.newRGB(0.85f, 0.75f, 0.85f); + public static final TColor TOMATO = TColor.newRGB(1.00f, 0.39f, 0.28f); + public static final TColor TRANSPARENT = TColor.newRGBA(0.00f, 0.00f, + 0.00f, 0.00f); + public static final TColor TURQUOISE = TColor.newRGB(0.25f, 0.88f, 0.82f); + public static final TColor VIOLET = TColor.newRGB(0.93f, 0.51f, 0.93f); + public static final TColor WHEAT = TColor.newRGB(0.96f, 0.87f, 0.07f); + public static final TColor WHITE = TColor.newRGB(1.00f, 1.00f, 1.00f); + public static final TColor WHITESMOKE = TColor.newRGB(0.96f, 0.96f, 0.96f); + public static final TColor YELLOW = TColor.newRGB(1.00f, 1.00f, 0.00f); + public static final TColor YELLOWGREEN = TColor.newRGB(0.60f, 0.80f, 0.20f); + + protected static final HashMap namedColorMap = new HashMap(); + + static { + Field[] fields = NamedColor.class.getDeclaredFields(); + try { + for (Field f : fields) { + if (f.getType() == TColor.class) { + String id = f.getName(); + namedColorMap.put(id, (TColor) f.get(null)); + } + } + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + /** + * Returns a color for the given name. + * + * @param name + * @return color or null, if name not found. + */ + public static final TColor getForName(String name) { + return namedColorMap.get(name.toUpperCase()); + } + + /** + * Returns the names of all defined colors. + * + * @return list of name + */ + public static final ArrayList getNames() { + return new ArrayList(namedColorMap.keySet()); + } +} diff --git a/java/Mekstension/tools/toxi/color/ProximityComparator.java b/java/Mekstension/tools/toxi/color/ProximityComparator.java new file mode 100644 index 0000000..5872fac --- /dev/null +++ b/java/Mekstension/tools/toxi/color/ProximityComparator.java @@ -0,0 +1,52 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +import java.util.Comparator; + +/** + * Compares two colors by their distance to the given target color. + * + * @author toxi + * + */ +public class ProximityComparator implements Comparator { + + protected TColor col; + protected DistanceProxy proxy; + + public ProximityComparator(TColor col, DistanceProxy proxy) { + this.col = col; + this.proxy = proxy; + } + + public int compare(TColor a, TColor b) { + float da = proxy.distanceBetween(col, a); + float db = proxy.distanceBetween(col, b); + return da < db ? -1 : da > db ? 1 : 0; + } + +} diff --git a/java/Mekstension/tools/toxi/color/RGBComparator.java b/java/Mekstension/tools/toxi/color/RGBComparator.java new file mode 100644 index 0000000..f39d4d8 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/RGBComparator.java @@ -0,0 +1,48 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color; + +import java.util.Comparator; + +/** + * Compares 2 colors by one of their RGB component values. + * + * @author toxi + * + */ +public class RGBComparator implements Comparator { + + private final int component; + + public RGBComparator(int comp) { + component = comp; + } + + public int compare(TColor a, TColor b) { + return Float.compare(a.rgb[component], b.rgb[component]); + } + +} diff --git a/java/Mekstension/tools/toxi/color/RGBDistanceProxy.java b/java/Mekstension/tools/toxi/color/RGBDistanceProxy.java new file mode 100644 index 0000000..ad7a8f0 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/RGBDistanceProxy.java @@ -0,0 +1,42 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.color; + +/** + * Implements the {@link DistanceProxy} interface to sort colors by RGB + * distance (used by {@link ColorList#sortByDistance(DistanceProxy, boolean)}). + * + * @author toxi + * + */ +public class RGBDistanceProxy implements DistanceProxy { + + public float distanceBetween(TColor a, TColor b) { + return a.distanceToRGB(b); + } + +} diff --git a/java/Mekstension/tools/toxi/color/TColor.java b/java/Mekstension/tools/toxi/color/TColor.java new file mode 100644 index 0000000..58c6bb1 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/TColor.java @@ -0,0 +1,1053 @@ +/* + * Some classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.color; + +import toxi.geom.Vec2D; +import toxi.geom.Vec3D; +import toxi.math.MathUtils; + +/** + * Floating point color class with implicit RGB, HSV, CMYK access modes, + * conversion and color theory utils. Based on the Colors + * library for NodeBox + * + */ +public class TColor { + + protected static final float INV60DEGREES = 60.0f / 360; + protected static final float INV8BIT = 1f / 255; + protected static final double EPS = .001; + + protected static final Vec2D[] RYB_WHEEL = new Vec2D[] { new Vec2D(0, 0), + new Vec2D(15, 8), new Vec2D(30, 17), new Vec2D(45, 26), + new Vec2D(60, 34), new Vec2D(75, 41), new Vec2D(90, 48), + new Vec2D(105, 54), new Vec2D(120, 60), new Vec2D(135, 81), + new Vec2D(150, 103), new Vec2D(165, 123), new Vec2D(180, 138), + new Vec2D(195, 155), new Vec2D(210, 171), new Vec2D(225, 187), + new Vec2D(240, 204), new Vec2D(255, 219), new Vec2D(270, 234), + new Vec2D(285, 251), new Vec2D(300, 267), new Vec2D(315, 282), + new Vec2D(330, 298), new Vec2D(345, 329), new Vec2D(360, 0) }; + + /** + * Maximum rgb component value for a color to be classified as black. + * + * @see #isBlack() + */ + public static float BLACK_POINT = 0.08f; + + /** + * Minimum rgb component value for a color to be classified as white. + * + * @see #isWhite() + */ + public static float WHITE_POINT = 1f; + + /** + * Maximum saturations value for a color to be classified as grey + * + * @see #isGrey() + */ + public static float GREY_THRESHOLD = 0.01f; + + public static final TColor RED = newRGB(1, 0, 0); + public static final TColor GREEN = newRGB(0, 1, 0); + public static final TColor BLUE = newRGB(0, 0, 1); + public static final TColor CYAN = newRGB(0, 1, 1); + public static final TColor MAGENTA = newRGB(1, 0, 1); + public static final TColor YELLOW = newRGB(1, 1, 0); + public static final TColor BLACK = newRGB(0, 0, 0); + public static final TColor WHITE = newRGB(1, 1, 1); + + protected float[] rgb; + protected float[] cmyk; + protected float[] hsv; + public float alpha; + + /** + * Converts CMYK floats into an RGB array. + * + * @param c + * @param m + * @param y + * @param k + * @return rgb array + */ + public static final float[] cmykToRGB(float c, float m, float y, float k) { + return cmykToRGB(c, m, y, k, new float[3]); + } + + /** + * Converts CMYK floats into the given RGB array. + * + * @param c + * @param m + * @param y + * @param k + * @param rgb + * @return rgb array + */ + public static final float[] cmykToRGB(float c, float m, float y, float k, + float[] rgb) { + rgb[0] = 1.0f - MathUtils.min(1.0f, c + k); + rgb[1] = 1.0f - MathUtils.min(1.0f, m + k); + rgb[2] = 1.0f - MathUtils.min(1.0f, y + k); + return rgb; + } + + /** + * Converts hex string into a RGB array. + * + * @param hexRGB + * @return rgb array + */ + public static final float[] hexToRGB(String hexRGB) { + return hexToRGB(hexRGB, new float[3]); + } + + public static final float[] hexToRGB(String hexRGB, float[] rgb) { + try { + int rgbInt = Integer.parseInt(hexRGB, 16); + rgb[0] = ((rgbInt >> 16) & 0xff) * INV8BIT; + rgb[1] = ((rgbInt >> 8) & 0xff) * INV8BIT; + rgb[2] = (rgbInt & 0xff) * INV8BIT; + } catch (NumberFormatException e) { + rgb[0] = rgb[1] = rgb[2] = 0; + } + return rgb; + } + + /** + * Converts HSV values into RGB array. + * + * @param h + * @param s + * @param v + * @return rgb array + */ + public static final float[] hsvToRGB(float h, float s, float v) { + return hsvToRGB(h, s, v, new float[3]); + } + + public static final float[] hsvToRGB(float h, float s, float v, float[] rgb) { + if (Float.compare(s, 0.0f) == 0) { + rgb[0] = rgb[1] = rgb[2] = v; + } else { + h /= INV60DEGREES; + int i = (int) h; + float f = h - i; + float p = v * (1 - s); + float q = v * (1 - s * f); + float t = v * (1 - s * (1 - f)); + + if (i == 0) { + rgb[0] = v; + rgb[1] = t; + rgb[2] = p; + } else if (i == 1) { + rgb[0] = q; + rgb[1] = v; + rgb[2] = p; + } else if (i == 2) { + rgb[0] = p; + rgb[1] = v; + rgb[2] = t; + } else if (i == 3) { + rgb[0] = p; + rgb[1] = q; + rgb[2] = v; + } else if (i == 4) { + rgb[0] = t; + rgb[1] = p; + rgb[2] = v; + } else { + rgb[0] = v; + rgb[1] = p; + rgb[2] = q; + } + } + return rgb; + } + + public static final float[] labToRGB(float l, float a, float b) { + return labToRGB(l, a, b, new float[3]); + } + + /** + * Converts CIE Lab to RGB components. + * + * First we have to convert to XYZ color space. Conversion involves using a + * white point, in this case D65 which represents daylight illumination. + * + * Algorithm adopted from: http://www.easyrgb.com/math.php + * + * @param l + * @param a + * @param b + * @param rgb + * @return rgb array + */ + public static final float[] labToRGB(float l, float a, float b, float[] rgb) { + float y = (l + 16) / 116.0f; + float x = a / 500.0f + y; + float z = y - b / 200.0f; + rgb[0] = x; + rgb[1] = y; + rgb[2] = z; + for (int i = 0; i < 3; i++) { + float p = (float) Math.pow(rgb[i], 3); + if (p > 0.008856) { + rgb[i] = p; + } else { + rgb[i] = (rgb[i] - 16 / 116.0f) / 7.787f; + } + } + + // Observer = 2, Illuminant = D65 + x = rgb[0] * 0.95047f; + y = rgb[1]; + z = rgb[2] * 1.08883f; + + rgb[0] = x * 3.2406f + y * -1.5372f + z * -0.4986f; + rgb[1] = x * -0.9689f + y * 1.8758f + z * 0.0415f; + rgb[2] = x * 0.0557f + y * -0.2040f + z * 1.0570f; + double tpow = 1 / 2.4; + for (int i = 0; i < 3; i++) { + if (rgb[i] > 0.0031308) { + rgb[i] = (float) (1.055 * Math.pow(rgb[i], tpow) - 0.055); + } else { + rgb[i] = 12.92f * rgb[i]; + } + } + return rgb; + } + + /** + * Factory method. Creates new color from ARGB int. + * + * @param argb + * @return new color + */ + public static final TColor newARGB(int argb) { + return newRGBA(((argb >> 16) & 0xff) * INV8BIT, ((argb >> 8) & 0xff) + * INV8BIT, (argb & 0xff) * INV8BIT, (argb >>> 24) * INV8BIT); + } + + /** + * Factory method. Creates new color from CMYK values. + * + * @param c + * @param m + * @param y + * @param k + * @return new color + */ + public static final TColor newCMYK(float c, float m, float y, float k) { + return newCMYKA(c, m, y, k, 1); + } + + /** + * Factory method. Creates new color from CMYK + alpha values. + * + * @param c + * @param m + * @param y + * @param k + * @param a + * @return new color + */ + public static final TColor newCMYKA(float c, float m, float y, float k, + float a) { + TColor col = new TColor(); + col.setCMYK(new float[] { c, m, y, k }); + col.alpha = MathUtils.clip(a, 0, 1); + return col; + } + + /** + * Factory method. Creates a new shade of gray + alpha. + * + * @param gray + * @return new color. + */ + public static final TColor newGray(float gray) { + return newGrayAlpha(gray, 1); + } + + public static final TColor newGrayAlpha(float gray, float alpha) { + TColor c = new TColor(); + c.setRGB(new float[] { gray, gray, gray }); + c.alpha = alpha; + return c; + } + + /** + * Factory method. New color from hex string. + * + * @param hexRGB + * @return new color + */ + public static final TColor newHex(String hexRGB) { + TColor c = new TColor(); + c.setRGB(hexToRGB(hexRGB)); + c.alpha = 1; + return c; + } + + /** + * Factory method. New color from hsv values. + * + * @param h + * @param s + * @param v + * @return new color + */ + public static final TColor newHSV(float h, float s, float v) { + return newHSVA(h, s, v, 1); + } + + public static TColor newHSV(Hue h, float s, float v) { + return newHSV(h.getHue(), s, v); + } + + public static final TColor newHSVA(float h, float s, float v, float a) { + TColor c = new TColor(); + c.setHSV(new float[] { h, s, v }); + c.alpha = MathUtils.clip(a, 0, 1); + return c; + } + + /** + * Factory method. Creates new random color. + * + * @return random color + */ + public static final TColor newRandom() { + return newRGBA(MathUtils.random(1f), MathUtils.random(1f), MathUtils + .random(1f), 1); + } + + /** + * Factory method. Creates new color from RGB values. + * + * @param r + * @param g + * @param b + * @return new color + */ + public static final TColor newRGB(float r, float g, float b) { + return newRGBA(r, g, b, 1); + } + + public static final TColor newRGBA(float r, float g, float b, float a) { + TColor c = new TColor(); + c.setRGB(new float[] { r, g, b }); + c.alpha = MathUtils.clip(a, 0, 1); + return c; + } + + /** + * Converts the RGB values into a CMYK array. + * + * @param r + * @param g + * @param b + * @return cmyk array + */ + public static final float[] rgbToCMYK(float r, float g, float b) { + return rgbToCMYK(r, g, b, new float[4]); + } + + public static final float[] rgbToCMYK(float r, float g, float b, + float[] cmyk) { + cmyk[0] = 1 - r; + cmyk[1] = 1 - g; + cmyk[2] = 1 - b; + cmyk[3] = MathUtils.min(cmyk[0], cmyk[1], cmyk[2]); + cmyk[0] = MathUtils.clip(cmyk[0] - cmyk[3], 0, 1); + cmyk[1] = MathUtils.clip(cmyk[1] - cmyk[3], 0, 1); + cmyk[2] = MathUtils.clip(cmyk[2] - cmyk[3], 0, 1); + cmyk[3] = MathUtils.clip(cmyk[3], 0, 1); + return cmyk; + } + + /** + * Formats the RGB float values into hex integers. + * + * @param r + * @param g + * @param b + * @return hex string + */ + public static final String rgbToHex(float r, float g, float b) { + String hex = Integer.toHexString((int) (r * 0xff)) + + Integer.toHexString((int) (g * 0xff)) + + Integer.toHexString((int) (b * 0xff)); + return hex; + } + + /** + * Converts the RGB values into an HSV array. + * + * @param r + * @param g + * @param b + * @return hsv array + */ + public static final float[] rgbToHSV(float r, float g, float b) { + return rgbToHSV(r, g, b, new float[3]); + } + + public static final float[] rgbToHSV(float r, float g, float b, float[] hsv) { + float h = 0, s = 0; + float v = MathUtils.max(r, g, b); + float d = v - MathUtils.min(r, g, b); + + if (v != 0.0) { + s = d / v; + } + if (s != 0.0) { + if (Float.compare(r, v) == 0) { + h = (g - b) / d; + } else if (Float.compare(g, v) == 0) { + h = 2 + (b - r) / d; + } else { + h = 4 + (r - g) / d; + } + } + + h *= INV60DEGREES; + + if (h < 0) { + h += 1.0f; + } + + hsv[0] = h; + hsv[1] = s; + hsv[2] = v; + return hsv; + } + + protected TColor() { + rgb = new float[3]; + hsv = new float[3]; + cmyk = new float[4]; + } + + /** + * Creates a deep copy of the given color. + * + * @param c + */ + public TColor(TColor c) { + this(); + System.arraycopy(c.rgb, 0, rgb, 0, 3); + System.arraycopy(c.hsv, 0, hsv, 0, 3); + System.arraycopy(c.cmyk, 0, cmyk, 0, 4); + this.alpha = c.alpha; + } + + /** + * Changes the brightness of the color by the given amount in the direction + * towards either the black or white point (depending on if current + * brightness >= 50%) + * + * @param amount + * @return itself + */ + public TColor adjustConstrast(float amount) { + return hsv[2] < 0.5 ? darken(amount) : lighten(amount); + } + + /** + * Adds the given HSV values as offsets to the current color. Hue will + * automatically wrap. + * + * @param h + * @param s + * @param v + * @return itself + */ + public TColor adjustHSV(float h, float s, float v) { + return setHSV(new float[] { hsv[0] + h, hsv[1] + s, hsv[2] + v }); + } + + /** + * Adds the given RGB values as offsets to the current color. TColor will + * clip at black or white. + * + * @param r + * @param g + * @param b + * @return itself + */ + public TColor adjustRGB(float r, float g, float b) { + return setRGB(new float[] { rgb[0] + r, rgb[1] + g, rgb[2] + b }); + } + + /** + * @return the color's alpha component + */ + public float alpha() { + return alpha; + } + + /** + * Rotates this color by a random amount (not exceeding the one specified) + * and creates variations in saturation and brightness based on the 2nd + * parameter. + * + * @param theta + * max. rotation angle (in radians) + * @param delta + * max. sat/bri variance + * @return itself + */ + public TColor analog(float theta, float delta) { + return analog(MathUtils.degrees(theta), delta); + } + + public TColor analog(int angle, float delta) { + rotateRYB((int) (angle * MathUtils.normalizedRandom())); + hsv[1] += delta * MathUtils.normalizedRandom(); + hsv[2] += delta * MathUtils.normalizedRandom(); + return setHSV(hsv); + } + + /** + * @return the color's black component + */ + + public float black() { + return cmyk[0]; + } + + /** + * Blends the color with the given one by the stated amount + * + * @param c + * target color + * @param t + * interpolation factor + * @return itself + */ + public TColor blend(TColor c, float t) { + rgb[0] += (c.rgb[0] - rgb[0]) * t; + rgb[1] += (c.rgb[1] - rgb[1]) * t; + rgb[2] += (c.rgb[2] - rgb[2]) * t; + alpha += (c.alpha - alpha) * t; + return setRGB(rgb); + } + + /** + * @return the color's blue component + */ + + public float blue() { + return rgb[2]; + } + + /** + * @return color HSV brightness (not luminance!) + */ + public float brightness() { + return hsv[2]; + } + + /** + * @return ifself, as complementary color + */ + public TColor complement() { + return rotateRYB(180); + } + + public TColor copy() { + return new TColor(this); + } + + /** + * @return the color's cyan component + */ + + public float cyan() { + return cmyk[0]; + } + + public TColor darken(float step) { + hsv[2] = MathUtils.clip(hsv[2] - step, 0, 1); + return setHSV(hsv); + } + + public TColor desaturate(float step) { + hsv[1] = MathUtils.clip(hsv[1] - step, 0, 1); + return setHSV(hsv); + } + + /** + * Calculates the CMYK distance to the given color. + * + * @param c + * target color + * @return distance + */ + public float distanceToCMYK(TColor c) { + float dc = cmyk[0] - c.cmyk[0]; + float dm = cmyk[1] - c.cmyk[1]; + float dy = cmyk[2] - c.cmyk[2]; + float dk = cmyk[3] - c.cmyk[3]; + return (float) Math.sqrt(dc * dc + dm * dm + dy * dy + dk * dk); + } + + /** + * Calculates the HSV distance to the given color. + * + * @param c + * target color + * @return distance + */ + public float distanceToHSV(TColor c) { + float hue = hsv[0] * MathUtils.TWO_PI; + float hue2 = c.hsv[0] * MathUtils.TWO_PI; + Vec3D v1 = new Vec3D((float) (Math.cos(hue) * hsv[1]), (float) (Math + .sin(hue) * hsv[1]), hsv[2]); + Vec3D v2 = new Vec3D((float) (Math.cos(hue2) * c.hsv[1]), (float) (Math + .sin(hue2) * c.hsv[1]), c.hsv[2]); + return v1.distanceTo(v2); + } + + /** + * Calculates the RGB distance to the given color. + * + * @param c + * target color + * @return distance + */ + public float distanceToRGB(TColor c) { + float dr = rgb[0] - c.rgb[0]; + float dg = rgb[1] - c.rgb[1]; + float db = rgb[2] - c.rgb[2]; + return (float) Math.sqrt(dr * dr + dg * dg + db * db); + } + + @Override + public boolean equals(Object o) { + if (o != null && o instanceof TColor) { + TColor c = (TColor) o; + float dr = c.rgb[0] - rgb[0]; + float dg = c.rgb[1] - rgb[1]; + float db = c.rgb[2] - rgb[2]; + float da = c.alpha - alpha; + double d = Math.sqrt(dr * dr + dg * dg + db * db + da * da); + return d < EPS; + } + return false; + } + + public TColor getAnalog(float theta, float delta) { + return new TColor(this).analog(theta, delta); + } + + public TColor getAnalog(int angle, float delta) { + return new TColor(this).analog(angle, delta); + } + + public TColor getBlended(TColor c, float t) { + return new TColor(this).blend(c, t); + } + + /** + * @return an instance of the closest named hue to this color. + */ + public Hue getClosestHue() { + return Hue.getClosest(hsv[0], false); + } + + /** + * @param primaryOnly + * if true, only primary color hues are considered + * @return an instance of the closest named (primary) hue to this color. + */ + public Hue getClosestHue(boolean primaryOnly) { + return Hue.getClosest(hsv[0], primaryOnly); + } + + public TColor getComplement() { + return new TColor(this).complement(); + } + + public float getComponentValue(AccessCriteria criteria) { + switch (criteria.getMode()) { + case HSV: + return hsv[criteria.getComponent()]; + case RGB: + return rgb[criteria.getComponent()]; + case CMYK: + return cmyk[criteria.getComponent()]; + case ALPHA: + return alpha; + } + return 0; + } + + /** + * @param step + * @return a darkened copy + */ + public TColor getDarkened(float step) { + return new TColor(this).darken(step); + } + + /** + * @param step + * @return a desaturated copy + */ + public TColor getDesaturated(float step) { + return new TColor(this).desaturate(step); + } + + /** + * @param step + * @return a lightened copy + */ + public TColor getLightened(float step) { + return new TColor(this).lighten(step); + } + + /** + * @param theta + * rotation angle in radians + * @return a RYB rotated copy + */ + public TColor getRotatedRYB(float theta) { + return new TColor(this).rotateRYB(theta); + } + + /** + * @param angle + * rotation angle in degrees + * @return a RYB rotated copy + */ + public TColor getRotatedRYB(int angle) { + return new TColor(this).rotateRYB(angle); + } + + /** + * @param step + * @return a saturated copy + */ + public TColor getSaturated(float step) { + return new TColor(this).saturate(step); + } + + /** + * @return the color's green component + */ + + public float green() { + return rgb[1]; + } + + @Override + public int hashCode() { + return (int) (rgb[0] * 1000000 + rgb[1] * 100000 + rgb[2] * 10000 + alpha * 1000); + } + + /** + * @return the color's hue + */ + public float hue() { + return hsv[0]; + } + + /** + * Inverts the color. + * + * @return itself + */ + public TColor invert() { + rgb[0] = 1 - rgb[0]; + rgb[1] = 1 - rgb[1]; + rgb[2] = 1 - rgb[2]; + return setRGB(rgb); + } + + /** + * @return true, if all rgb component values are equal and less than + * {@link #BLACK_POINT} + */ + public boolean isBlack() { + return (rgb[0] <= BLACK_POINT && Float.compare(rgb[0], rgb[1]) == 0 && Float + .compare(rgb[0], rgb[2]) == 0); + } + + /** + * @return true, if the saturation component value is less than + * {@link #GREY_THRESHOLD} + */ + public boolean isGrey() { + return hsv[1] < GREY_THRESHOLD; + } + + /** + * @return true, if this colors hue is matching one of the 7 defined + * primary hues. + */ + public boolean isPrimary() { + return Hue.isPrimary(hsv[0]); + } + + /** + * @return true, if all rgb component values are equal and greater than + * {@link #WHITE_POINT} + */ + public boolean isWhite() { + return (rgb[0] >= WHITE_POINT && Float.compare(rgb[0], rgb[1]) == 0 && Float + .compare(rgb[0], rgb[2]) == 0); + } + + /** + * Lightens the color by stated amount. + * + * @param step + * lighten amount + * @return itself + */ + public TColor lighten(float step) { + hsv[2] = MathUtils.clip(hsv[2] + step, 0, 1); + return setHSV(hsv); + } + + /** + * Computes the color's luminance using this formula: lum=0.299*red + + * 0.587*green + 0.114 *blue + * + * @return luminance + */ + public float luminance() { + return rgb[0] * 0.299f + rgb[1] * 0.587f + rgb[2] * 0.114f; + } + + /** + * @return the color's magenta component + */ + + public float magenta() { + return cmyk[0]; + } + + /** + * @return the color's red component + */ + + public float red() { + return rgb[0]; + } + + public TColor rotateRYB(float theta) { + return rotateRYB((int) MathUtils.degrees(theta)); + } + + public TColor rotateRYB(int theta) { + float h = hsv[0] * 360; + theta %= 360; + + float resultHue = 0; + for (int i = 0; i < RYB_WHEEL.length - 1; i++) { + Vec2D p = RYB_WHEEL[i]; + Vec2D q = RYB_WHEEL[i + 1]; + if (q.y < p.y) { + q.y += 360; + } + if (p.y <= h && h <= q.y) { + resultHue = p.x + (q.x - p.x) * (h - p.y) / (q.y - p.y); + break; + } + } + + // And the user-given angle (e.g. complement). + resultHue = (resultHue + theta) % 360; + + // For the given angle, find out what hue is + // located there on the artistic color wheel. + for (int i = 0; i < RYB_WHEEL.length - 1; i++) { + Vec2D p = RYB_WHEEL[i]; + Vec2D q = RYB_WHEEL[i + 1]; + if (q.y < p.y) { + q.y += 360; + } + if (p.x <= resultHue && resultHue <= q.x) { + h = p.y + (q.y - p.y) * (resultHue - p.x) / (q.x - p.x); + break; + } + } + + hsv[0] = (h % 360) / 360.0f; + return setHSV(hsv); + } + + public TColor saturate(float step) { + hsv[1] = MathUtils.clip(hsv[1] + step, 0, 1); + return setHSV(hsv); + } + + public float saturation() { + return hsv[1]; + } + + public TColor setAlpha(float alpha) { + this.alpha = alpha; + return this; + } + + public TColor setBlack(float val) { + cmyk[3] = val; + return setCMYK(cmyk); + } + + public TColor setBlue(float blue) { + rgb[2] = blue; + return setRGB(rgb); + } + + public TColor setBrightness(float brightness) { + hsv[2] = MathUtils.clip(brightness, 0, 1); + return setHSV(hsv); + } + + public TColor setCMYK(float[] newCMYK) { + cmyk[0] = MathUtils.clip(newCMYK[0], 0, 1); + cmyk[1] = MathUtils.clip(newCMYK[1], 0, 1); + cmyk[2] = MathUtils.clip(newCMYK[2], 0, 1); + cmyk[3] = MathUtils.clip(newCMYK[3], 0, 1); + cmykToRGB(cmyk[0], cmyk[1], cmyk[2], cmyk[3], rgb); + rgbToHSV(rgb[0], rgb[1], rgb[2], hsv); + return this; + } + + public TColor setComponent(AccessCriteria criteria, float val) { + switch (criteria.getMode()) { + case RGB: + rgb[criteria.getComponent()] = val; + return setRGB(rgb); + case HSV: + hsv[criteria.getComponent()] = val; + return setHSV(hsv); + case ALPHA: + return setAlpha(val); + case CMYK: + cmyk[criteria.getComponent()] = val; + return setCMYK(cmyk); + default: + throw new IllegalArgumentException( + "Invalid ColorAccessCriteria mode used"); + } + } + + public TColor setCyan(float val) { + cmyk[0] = val; + return setCMYK(cmyk); + } + + public TColor setGreen(float green) { + rgb[1] = green; + return setRGB(rgb); + } + + public TColor setHSV(float[] newHSV) { + hsv[0] = newHSV[0] % 1; + if (hsv[0] < 0) + hsv[0]++; + hsv[1] = MathUtils.clip(newHSV[1], 0, 1); + hsv[2] = MathUtils.clip(newHSV[2], 0, 1); + hsvToRGB(hsv[0], hsv[1], hsv[2], rgb); + rgbToCMYK(rgb[0], rgb[1], rgb[2], cmyk); + return this; + } + + public void setHue(float hue) { + hue %= 1.0; + if (hue < 0.0) { + hue++; + } + hsv[0] = hue; + setHSV(hsv); + } + + public TColor setMagenta(float val) { + cmyk[1] = val; + return setCMYK(cmyk); + } + + public TColor setRed(float red) { + rgb[0] = red; + return setRGB(rgb); + } + + public TColor setRGB(float[] newRGB) { + rgb[0] = MathUtils.clip(newRGB[0], 0, 1); + rgb[1] = MathUtils.clip(newRGB[1], 0, 1); + rgb[2] = MathUtils.clip(newRGB[2], 0, 1); + rgbToCMYK(rgb[0], rgb[1], rgb[2], cmyk); + rgbToHSV(rgb[0], rgb[1], rgb[2], hsv); + return this; + } + + public TColor setSaturation(float saturation) { + hsv[1] = MathUtils.clip(saturation, 0, 1); + return setHSV(hsv); + } + + public TColor setYellow(float val) { + cmyk[2] = val; + return setCMYK(cmyk); + } + + /** + * Converts the color into a packed ARGB int. + * + * @return color as int + */ + public int toARGB() { + return (int) (rgb[0] * 255) << 16 | (int) (rgb[1] * 255) << 8 + | (int) (rgb[2] * 255) | (int) (alpha * 255) << 24; + } + + @Override + public String toString() { + return "TColor: rgb: " + rgb[0] + "," + rgb[1] + "," + rgb[2] + + " hsv: " + hsv[0] + "," + hsv[1] + "," + hsv[2] + " cmyk: " + + cmyk[0] + "," + cmyk[1] + "," + cmyk[2] + "," + cmyk[3] + + " alpha: " + alpha; + } + + /** + * @return the color's yellow component + */ + public float yellow() { + return cmyk[0]; + } + +} \ No newline at end of file diff --git a/java/Mekstension/tools/toxi/color/theory/AnalogousStrategy.java b/java/Mekstension/tools/toxi/color/theory/AnalogousStrategy.java new file mode 100644 index 0000000..5d053fd --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/AnalogousStrategy.java @@ -0,0 +1,116 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import toxi.color.ColorList; +import toxi.color.TColor; +import toxi.geom.Vec2D; +import toxi.math.MathUtils; + +/** + * Creates a new palette of 4 similar (slightly paler) colors in addition to the + * given start color. The hue variance and contrast can be adjusted. + */ +public class AnalogousStrategy implements ColorTheoryStrategy { + + public static final String NAME = "analogous"; + + protected static final Vec2D[] tones = new Vec2D[] { new Vec2D(1, 2.2f), + new Vec2D(2, 1), new Vec2D(-1, -0.5f), new Vec2D(-2, 1) }; + + protected float contrast = 0.25f; + protected float theta = 10 * MathUtils.DEG2RAD; + + /** + * Creates a new instance with default contrast (25%) and 10 + */ + public AnalogousStrategy() { + } + + /** + * @param theta + * variance angle in radians + * @param contrast + */ + public AnalogousStrategy(float theta, float contrast) { + this.contrast = contrast; + this.theta = theta; + } + + /** + * @param theta + * variance angle in degrees + * @param contrast + */ + public AnalogousStrategy(int theta, float contrast) { + this.contrast = contrast; + this.theta = MathUtils.radians(theta); + } + + /* + * (non-Javadoc) + * + * @see + * toxi.color.ColorTheoryStrategy#createListFromcolor(toxi.color.TColor) + */ + public ColorList createListFromColor(TColor src) { + contrast = MathUtils.clipNormalized(contrast); + TColor clr = src.copy(); + ColorList colors = new ColorList(clr); + for (Vec2D currTone : tones) { + TColor c = clr.getRotatedRYB((int) (theta * currTone.x)); + float t = 0.44f - currTone.y * 0.1f; + if (clr.brightness() - contrast * currTone.y < t) { + c.setBrightness(t); + } else { + c.setBrightness(clr.brightness() - contrast * currTone.y); + } + c.desaturate(0.05f); + colors.add(c); + } + return colors; + } + + /* + * (non-Javadoc) + * + * @see toxi.color.ColorTheoryStrategy#getName() + */ + public String getName() { + return NAME; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return NAME + " contrast: " + contrast + " theta: " + + MathUtils.degrees(theta); + } +} diff --git a/java/Mekstension/tools/toxi/color/theory/ColorTheoryRegistry.java b/java/Mekstension/tools/toxi/color/theory/ColorTheoryRegistry.java new file mode 100644 index 0000000..79ea40f --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/ColorTheoryRegistry.java @@ -0,0 +1,82 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Registry & object factory for default {@link ColorTheoryStrategy} + * implementations as well as custom ones. + */ +public class ColorTheoryRegistry { + + private static final HashMap implementations = new HashMap(); + + public static final ColorTheoryStrategy SINGLE_COMPLEMENT = new SingleComplementStrategy(); + public static final ColorTheoryStrategy COMPLEMENTARY = new ComplementaryStrategy(); + public static final ColorTheoryStrategy SPLIT_COMPLEMENTARY = new SplitComplementaryStrategy(); + public static final ColorTheoryStrategy LEFT_SPLIT_COMPLEMENTARY = new LeftSplitComplementaryStrategy(); + public static final ColorTheoryStrategy RIGHT_SPLIT_COMPLEMENTARY = new RightSplitComplementaryStrategy(); + public static final ColorTheoryStrategy ANALOGOUS = new AnalogousStrategy(); + public static final ColorTheoryStrategy MONOCHROME = new MonochromeTheoryStrategy(); + public static final ColorTheoryStrategy TRIAD = new TriadTheoryStrategy(); + public static final ColorTheoryStrategy TETRAD = new TetradTheoryStrategy(); + public static final ColorTheoryStrategy COMPOUND = new CompoundTheoryStrategy(); + + static { + Field[] fields = ColorTheoryRegistry.class.getDeclaredFields(); + try { + for (Field f : fields) { + if (f.getType() == ColorTheoryStrategy.class) { + String id = f.getName(); + implementations.put(id, (ColorTheoryStrategy) f.get(null)); + } + } + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + public static ArrayList getRegisteredNames() { + return new ArrayList(implementations.keySet()); + } + + public static ArrayList getRegisteredStrategies() { + return new ArrayList(implementations.values()); + } + + public static ColorTheoryStrategy getStrategyForName(String id) { + return implementations.get(id); + } + + public static void registerImplementation(ColorTheoryStrategy impl) { + implementations.put(impl.getName().toUpperCase(), impl); + } +} diff --git a/java/Mekstension/tools/toxi/color/theory/ColorTheoryStrategy.java b/java/Mekstension/tools/toxi/color/theory/ColorTheoryStrategy.java new file mode 100644 index 0000000..e20a4c8 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/ColorTheoryStrategy.java @@ -0,0 +1,58 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import toxi.color.ColorList; +import toxi.color.TColor; + +/** + * A simple interface for implementing rules used to create color palettes. The + * library currently comes with 10 default implementations realising different + * color theory approaches. + * + * @author toxi + * + */ +public interface ColorTheoryStrategy { + + /** + * Creates a new {@link ColorList} of colors for the supplied source color + * based on the strategy. The number of colors returned is unspecified and + * depends on the strategy. + * + * @param src + * source color + * @return list of matching colors created by the strategy. + */ + ColorList createListFromColor(TColor src); + + /** + * Returns the unique name of the strategy. + * + * @return name + */ + String getName(); +} diff --git a/java/Mekstension/tools/toxi/color/theory/ComplementaryStrategy.java b/java/Mekstension/tools/toxi/color/theory/ComplementaryStrategy.java new file mode 100644 index 0000000..2dc9a71 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/ComplementaryStrategy.java @@ -0,0 +1,103 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import toxi.color.ColorList; +import toxi.color.TColor; + +/** + * Creates 5 additional colors in relation to the given base color: + *

+ * + * @author toxi + * + */ +public class ComplementaryStrategy implements ColorTheoryStrategy { + + public static final String NAME = "complementary"; + + /* + * (non-Javadoc) + * + * @see + * toxi.color.ColorTheoryStrategy#createListFromcolor(toxi.color.TColor) + */ + public ColorList createListFromColor(TColor src) { + ColorList colors = new ColorList(src.copy()); + // # A contrasting color: much darker or lighter than the original. + TColor c = src.copy(); + if (c.brightness() > 0.4) { + c.setBrightness(0.1f + c.brightness() * 0.25f); + } else { + c.setBrightness(1.0f - c.brightness() * 0.25f); + } + colors.add(c); + + // A soft supporting color: lighter and less saturated. + c = src.copy(); + c.lighten(0.3f); + c.setSaturation(0.1f + c.saturation() * 0.3f); + colors.add(c); + + // A contrasting complement: very dark or very light. + c = src.getComplement(); + if (c.brightness() > 0.3) { + c.setBrightness(0.1f + c.brightness() * 0.25f); + } else { + c.setBrightness(1.0f - c.brightness() * 0.25f); + } + colors.add(c); + + // The complement and a light supporting variant. + colors.add(src.getComplement()); + + c = src.getComplement(); + c.lighten(0.3f); + c.setSaturation(0.1f + c.saturation() * 0.25f); + colors.add(c); + return colors; + } + + /* + * (non-Javadoc) + * + * @see toxi.color.ColorTheoryStrategy#getName() + */ + public String getName() { + return NAME; + } + + @Override + public String toString() { + return NAME; + } +} diff --git a/java/Mekstension/tools/toxi/color/theory/CompoundTheoryStrategy.java b/java/Mekstension/tools/toxi/color/theory/CompoundTheoryStrategy.java new file mode 100644 index 0000000..641a1d7 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/CompoundTheoryStrategy.java @@ -0,0 +1,117 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import toxi.color.ColorList; +import toxi.color.TColor; +import toxi.math.MathUtils; + +/** + * @author toxi + * + */ +public class CompoundTheoryStrategy implements ColorTheoryStrategy { + + public static final String NAME = "compound"; + + private boolean isFlipped = false; + + private static final float wrap(float x, float min, float threshold, + float plus) { + if (x - min < threshold) { + return x + plus; + } else { + return x - min; + } + } + + public CompoundTheoryStrategy() { + + } + + public CompoundTheoryStrategy(boolean flipped) { + isFlipped = flipped; + } + + /* + * (non-Javadoc) + * + * @see + * toxi.color.ColorTheoryStrategy#createListFromcolor(toxi.color.TColor) + */ + public ColorList createListFromColor(TColor src) { + ColorList colors = new ColorList(src.copy()); + int direction = isFlipped ? -1 : 1; + + TColor c = src.getRotatedRYB(30 * direction); + c.setBrightness(wrap(c.brightness(), 0.25f, 0.6f, 0.25f)); + colors.add(c); + + c = src.getRotatedRYB(30 * direction); + c.setSaturation(wrap(c.saturation(), 0.4f, 0.1f, 0.4f)); + c.setBrightness(wrap(c.brightness(), 0.4f, 0.2f, 0.4f)); + colors.add(c); + + c = src.getRotatedRYB(160 * direction); + c.setSaturation(wrap(c.saturation(), 0.25f, 0.1f, 0.25f)); + c.setBrightness(MathUtils.max(0.2f, c.brightness())); + colors.add(c); + + c = src.getRotatedRYB(150 * direction); + c.setSaturation(wrap(c.saturation(), 0.1f, 0.8f, 0.1f)); + c.setBrightness(wrap(c.brightness(), 0.3f, 0.6f, 0.3f)); + colors.add(c); + + c = src.getRotatedRYB(150 * direction); + c.setSaturation(wrap(c.saturation(), 0.1f, 0.8f, 0.1f)); + c.setBrightness(wrap(c.brightness(), 0.4f, 0.2f, 0.4f)); + // colors.add(c); + + return colors; + } + + /* + * (non-Javadoc) + * + * @see toxi.color.ColorTheoryStrategy#getName() + */ + public String getName() { + return NAME; + } + + public boolean isFlipped() { + return isFlipped; + } + + public void setFlipped(boolean state) { + isFlipped = state; + } + + @Override + public String toString() { + return NAME + (isFlipped ? "_flipped" : ""); + } +} diff --git a/java/Mekstension/tools/toxi/color/theory/LeftSplitComplementaryStrategy.java b/java/Mekstension/tools/toxi/color/theory/LeftSplitComplementaryStrategy.java new file mode 100644 index 0000000..d4a079e --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/LeftSplitComplementaryStrategy.java @@ -0,0 +1,69 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import toxi.color.ColorList; +import toxi.color.TColor; + +/** + * @author toxi + * + */ +public class LeftSplitComplementaryStrategy implements ColorTheoryStrategy { + + /* + * (non-Javadoc) + * + * @see + * toxi.color.ColorTheoryStrategy#createListFromcolor(toxi.color.TColor) + */ + public static final String NAME = "leftSplitComplementary"; + + public ColorList createListFromColor(TColor src) { + TColor left = src.getComplement().rotateRYB(-30).lighten(0.1f); + ColorList colors = ColorTheoryRegistry.COMPLEMENTARY + .createListFromColor(src); + for (int i = 3; i < 6; i++) { + TColor c = colors.get(i); + c.setHue(left.hue()); + } + return colors; + } + + /* + * (non-Javadoc) + * + * @see toxi.color.ColorTheoryStrategy#getName() + */ + public String getName() { + return NAME; + } + + @Override + public String toString() { + return NAME; + } +} diff --git a/java/Mekstension/tools/toxi/color/theory/MonochromeTheoryStrategy.java b/java/Mekstension/tools/toxi/color/theory/MonochromeTheoryStrategy.java new file mode 100644 index 0000000..eb1e151 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/MonochromeTheoryStrategy.java @@ -0,0 +1,97 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import toxi.color.ColorList; +import toxi.color.TColor; +import toxi.math.MathUtils; + +/** + * Creates 4 additional shades of the given color, thus creating a monochrome + * palette. + * + * @author toxi + * + */ +public class MonochromeTheoryStrategy implements ColorTheoryStrategy { + + public static final String NAME = "monochrome"; + + private static final float wrap(float x, float min, float threshold, + float plus) { + if (x - min < threshold) { + return x + plus; + } else { + return x - min; + } + } + + /* + * (non-Javadoc) + * + * @see + * toxi.color.ColorTheoryStrategy#createListFromcolor(toxi.color.TColor) + */ + public ColorList createListFromColor(TColor src) { + + ColorList colors = new ColorList(src.copy()); + + TColor c = src.copy(); + c.setBrightness(wrap(c.brightness(), 0.5f, 0.2f, 0.3f)); + c.setSaturation(wrap(c.saturation(), 0.3f, 0.1f, 0.3f)); + colors.add(c); + + c = src.copy(); + c.setBrightness(wrap(c.brightness(), 0.2f, 0.2f, 0.6f)); + colors.add(c); + + c = src.copy(); + c.setBrightness(MathUtils.max(0.2f, c.brightness() + + (1 - c.brightness()) * 0.2f)); + c.setSaturation(wrap(c.saturation(), 0.3f, 0.1f, 0.3f)); + colors.add(c); + + c = src.copy(); + c.setBrightness(wrap(c.brightness(), 0.5f, 0.2f, 0.3f)); + colors.add(c); + + return colors; + } + + /* + * (non-Javadoc) + * + * @see toxi.color.ColorTheoryStrategy#getName() + */ + public String getName() { + return NAME; + } + + @Override + public String toString() { + return NAME; + } +} diff --git a/java/Mekstension/tools/toxi/color/theory/RightSplitComplementaryStrategy.java b/java/Mekstension/tools/toxi/color/theory/RightSplitComplementaryStrategy.java new file mode 100644 index 0000000..d9f1a91 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/RightSplitComplementaryStrategy.java @@ -0,0 +1,69 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import toxi.color.ColorList; +import toxi.color.TColor; + +/** + * @author toxi + * + */ +public class RightSplitComplementaryStrategy implements ColorTheoryStrategy { + + /* + * (non-Javadoc) + * + * @see + * toxi.color.ColorTheoryStrategy#createListFromcolor(toxi.color.TColor) + */ + public static final String NAME = "rightSplitComplementary"; + + public ColorList createListFromColor(TColor src) { + TColor left = src.getComplement().rotateRYB(30).lighten(0.1f); + ColorList colors = ColorTheoryRegistry.COMPLEMENTARY + .createListFromColor(src); + for (int i = 3; i < 6; i++) { + TColor c = colors.get(i); + c.setHue(left.hue()); + } + return colors; + } + + /* + * (non-Javadoc) + * + * @see toxi.color.ColorTheoryStrategy#getName() + */ + public String getName() { + return NAME; + } + + @Override + public String toString() { + return NAME; + } +} diff --git a/java/Mekstension/tools/toxi/color/theory/SingleComplementStrategy.java b/java/Mekstension/tools/toxi/color/theory/SingleComplementStrategy.java new file mode 100644 index 0000000..475b81e --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/SingleComplementStrategy.java @@ -0,0 +1,68 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import toxi.color.ColorList; +import toxi.color.TColor; + +/** + * Implements the single complementary color scheme to create a compatible color for the + * given one. + * + * @author toxi + * + */ +public class SingleComplementStrategy implements ColorTheoryStrategy { + + public static final String NAME = "complement"; + + /* + * (non-Javadoc) + * + * @see + * toxi.color.ColorTheoryStrategy#createListFromcolor(toxi.color.TColor) + */ + public ColorList createListFromColor(TColor src) { + ColorList list = new ColorList(new TColor(src)); + list.add(src.getComplement()); + return list; + } + + /* + * (non-Javadoc) + * + * @see toxi.color.ColorTheoryStrategy#getName() + */ + public String getName() { + return NAME; + } + + @Override + public String toString() { + return NAME; + } +} diff --git a/java/Mekstension/tools/toxi/color/theory/SplitComplementaryStrategy.java b/java/Mekstension/tools/toxi/color/theory/SplitComplementaryStrategy.java new file mode 100644 index 0000000..e7ae128 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/SplitComplementaryStrategy.java @@ -0,0 +1,71 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import toxi.color.ColorList; +import toxi.color.TColor; + +/** + * Implements the split-complementary color scheme to create 2 compatible colors for the + * given one. + * + * @author toxi + * + */ +public class SplitComplementaryStrategy implements ColorTheoryStrategy { + + public static final String NAME = "splitComplementary"; + + /* + * (non-Javadoc) + * + * @see + * toxi.color.ColorTheoryStrategy#createListFromcolor(toxi.color.TColor) + */ + public ColorList createListFromColor(TColor src) { + TColor clr = src.copy(); + ColorList colors = new ColorList(clr); + clr = clr.getComplement(); + colors.add(clr.getRotatedRYB(-30).lighten(0.1f)); + colors.add(clr.getRotatedRYB(30).lighten(0.1f)); + return colors; + } + + /* + * (non-Javadoc) + * + * @see toxi.color.ColorTheoryStrategy#getName() + */ + public String getName() { + return NAME; + } + + @Override + public String toString() { + return NAME; + } +} diff --git a/java/Mekstension/tools/toxi/color/theory/TetradTheoryStrategy.java b/java/Mekstension/tools/toxi/color/theory/TetradTheoryStrategy.java new file mode 100644 index 0000000..e2c3cf0 --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/TetradTheoryStrategy.java @@ -0,0 +1,86 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import toxi.color.ColorList; +import toxi.color.TColor; + +/** + * Implements the tetradic color scheme to create 4 compatible colors for the given one. + * + * @author toxi + * + */ +public class TetradTheoryStrategy implements ColorTheoryStrategy { + + public static final String NAME = "tetrad"; + + private final int theta = 90; + + /* + * (non-Javadoc) + * + * @see + * toxi.color.ColorTheoryStrategy#createListFromcolor(toxi.color.TColor) + */ + public ColorList createListFromColor(TColor src) { + ColorList colors = new ColorList(src.copy()); + TColor c = src.getRotatedRYB(theta); + if (c.brightness() < 0.5) { + c.lighten(0.2f); + } else { + c.darken(0.2f); + } + colors.add(c); + + c = src.getRotatedRYB(theta * 2); + if (c.brightness() < 0.5) { + c.lighten(0.1f); + } else { + c.darken(0.1f); + } + colors.add(c); + + colors.add(src.getRotatedRYB(theta * 3).lighten(0.1f)); + return colors; + } + + /* + * (non-Javadoc) + * + * @see toxi.color.ColorTheoryStrategy#getName() + */ + public String getName() { + return NAME; + } + + @Override + public String toString() { + return NAME; + } + +} diff --git a/java/Mekstension/tools/toxi/color/theory/TriadTheoryStrategy.java b/java/Mekstension/tools/toxi/color/theory/TriadTheoryStrategy.java new file mode 100644 index 0000000..25be0cb --- /dev/null +++ b/java/Mekstension/tools/toxi/color/theory/TriadTheoryStrategy.java @@ -0,0 +1,69 @@ +/* + * The classes in this package have been partly inspired by & bits ported from + * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library + * of Nodebox.net. + * + * http://nodebox.net/code/index.php/Colors + * + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.color.theory; + +import toxi.color.ColorList; +import toxi.color.TColor; + +/** + * Implements the triadic color scheme to create 3 compatible colors for the given one. + * + * @author toxi + * + */ +public class TriadTheoryStrategy implements ColorTheoryStrategy { + + public static final String NAME = "triad"; + + /* + * (non-Javadoc) + * + * @see + * toxi.color.ColorTheoryStrategy#createListFromcolor(toxi.color.TColor) + */ + public ColorList createListFromColor(TColor src) { + ColorList colors = new ColorList(src.copy()); + colors.add(src.getRotatedRYB(120).lighten(0.1f)); + colors.add(src.getRotatedRYB(-120).lighten(0.1f)); + return colors; + } + + /* + * (non-Javadoc) + * + * @see toxi.color.ColorTheoryStrategy#getName() + */ + public String getName() { + return NAME; + } + + @Override + public String toString() { + return NAME; + } + +} diff --git a/java/Mekstension/tools/toxi/geom/AABB.java b/java/Mekstension/tools/toxi/geom/AABB.java new file mode 100644 index 0000000..5c9f5f4 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/AABB.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.geom; + +import toxi.math.MathUtils; + +/** + * Axis-aligned bounding box with basic intersection features for Ray, AABB and + * Sphere classes. + */ + +public class AABB extends Vec3D { + + private Vec3D extent; + + private Vec3D min, max; + + /** + * Creates a new instance from two vectors specifying opposite corners of + * the box + * + * @param min + * first corner point + * @param max + * second corner point + * @return new AABB with centre at the half point between the 2 input + * vectors + */ + public static final AABB fromMinMax(Vec3D min, Vec3D max) { + Vec3D a = Vec3D.min(min, max); + Vec3D b = Vec3D.max(min, max); + return new AABB(a.interpolateTo(b, 0.5f), b.sub(a).scaleSelf(0.5f)); + } + + /** + * Creates a new instance from centre point and extent + * + * @param pos + * @param extent + * box dimensions (the box will be double the size in each + * direction) + */ + public AABB(Vec3D pos, Vec3D extent) { + super(pos); + setExtent(extent); + } + + /** + * Creates an independent copy of the passed in box + * + * @param box + */ + public AABB(AABB box) { + this(box, box.getExtent()); + } + + /** + * Updates the position of the box in space and calls + * {@link #updateBounds()} immediately + * + * @see toxi.geom.Vec3D#set(float, float, float) + */ + public Vec3D set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + updateBounds(); + return this; + } + + /** + * Updates the position of the box in space and calls + * {@link #updateBounds()} immediately + * + * @see toxi.geom.Vec3D#set(toxi.geom.Vec3D) + */ + public Vec3D set(Vec3D v) { + x = v.x; + y = v.y; + z = v.z; + updateBounds(); + return this; + } + + /** + * Updates the size of the box and calls {@link #updateBounds()} immediately + * + * @param extent + * new box size + * @return itself, for method chaining + */ + public AABB setExtent(Vec3D extent) { + this.extent = new Vec3D(extent); + return updateBounds(); + } + + /** + * Returns the current box size as new Vec3D instance (updating this vector + * will NOT update the box size! Use {@link #setExtent(Vec3D)} for those + * purposes) + * + * @return box size + */ + public final Vec3D getExtent() { + return new Vec3D(extent); + } + + /** + * Updates the min/max corner points of the box. MUST be called after moving + * the box in space by manipulating the public x,y,z coordinates directly. + * + * @return itself + */ + public final AABB updateBounds() { + // this is check is necessary for the constructor + if (extent != null) { + this.min = this.sub(extent); + this.max = this.add(extent); + } + return this; + } + + public final float minX() { + return x - extent.x; + } + + public final float maxX() { + return x + extent.x; + } + + public final float minY() { + return y - extent.y; + } + + public final float maxY() { + return y + extent.y; + } + + public final float minZ() { + return z - extent.z; + } + + public final float maxZ() { + return z + extent.z; + } + + public final Vec3D getMin() { + return this.sub(extent); + } + + public final Vec3D getMax() { + return this.add(extent); + } + + /** + * @param c + * sphere centre + * @param r + * sphere radius + * @return true, if AABB intersects with sphere + */ + public boolean intersectsSphere(Vec3D c, float r) { + float s, d = 0; + // find the square of the distance + // from the sphere to the box + if (c.x < min.x) { + s = c.x - min.x; + d += s * s; + } else if (c.x > max.x) { + s = c.x - max.x; + d += s * s; + } + + if (c.y < min.y) { + s = c.y - min.y; + d += s * s; + } else if (c.y > max.y) { + s = c.y - max.y; + d += s * s; + } + + if (c.z < min.z) { + s = c.z - min.z; + d += s * s; + } else if (c.z > max.z) { + s = c.z - max.z; + d += s * s; + } + + return d <= r * r; + } + + public boolean intersectsSphere(Sphere s) { + return intersectsSphere(s, s.radius); + } + + /** + * Checks if the box intersects the passed in one. + * + * @param box + * box to check + * @return true, if boxes overlap + */ + public boolean intersectsBox(AABB box) { + Vec3D t = box.sub(this); + return MathUtils.abs(t.x) <= (extent.x + box.extent.x) + && MathUtils.abs(t.y) <= (extent.y + box.extent.y) + && MathUtils.abs(t.z) <= (extent.z + box.extent.z); + } + + /** + * Calculates intersection with the given ray between a certain distance + * interval. + * + * Ray-box intersection is using IEEE numerical properties to ensure the + * test is both robust and efficient, as described in: + * + * Amy Williams, Steve Barrus, R. Keith Morley, and Peter Shirley: "An + * Efficient and Robust Ray-Box Intersection Algorithm" Journal of graphics + * tools, 10(1):49-54, 2005 + * + * @param ray + * incident ray + * @param minDir + * @param maxDir + * @return intersection point on the bounding box (only the first is + * returned) or null if no intersection + */ + public Vec3D intersectsRay(Ray3D ray, float minDir, float maxDir) { + Vec3D invDir = new Vec3D(1f / ray.dir.x, 1f / ray.dir.y, 1f / ray.dir.z); + boolean signDirX = invDir.x < 0; + boolean signDirY = invDir.y < 0; + boolean signDirZ = invDir.z < 0; + Vec3D min = getMin(); + Vec3D max = getMax(); + Vec3D bbox = signDirX ? max : min; + float tmin = (bbox.x - ray.x) * invDir.x; + bbox = signDirX ? min : max; + float tmax = (bbox.x - ray.x) * invDir.x; + bbox = signDirY ? max : min; + float tymin = (bbox.y - ray.y) * invDir.y; + bbox = signDirY ? min : max; + float tymax = (bbox.y - ray.y) * invDir.y; + + if ((tmin > tymax) || (tymin > tmax)) + return null; + if (tymin > tmin) + tmin = tymin; + if (tymax < tmax) + tmax = tymax; + + bbox = signDirZ ? max : min; + float tzmin = (bbox.z - ray.z) * invDir.z; + bbox = signDirZ ? min : max; + float tzmax = (bbox.z - ray.z) * invDir.z; + + if ((tmin > tzmax) || (tzmin > tmax)) + return null; + if (tzmin > tmin) + tmin = tzmin; + if (tzmax < tmax) + tmax = tzmax; + if ((tmin < maxDir) && (tmax > minDir)) { + return ray.getPointAtDistance(tmin); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see toxi.geom.Vec3D#toString() + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(" pos: ").append(super.toString()).append(" ext: ") + .append(extent); + return sb.toString(); + } +} diff --git a/java/Mekstension/tools/toxi/geom/Intersector.java b/java/Mekstension/tools/toxi/geom/Intersector.java new file mode 100644 index 0000000..9c566ee --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Intersector.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.geom; + +/** + * Generic interface for ray intersection with 3D geometry + */ +public interface Intersector { + + /** + * Check if entity intersects with the given ray + * @param ray ray to check + * @return true, if ray hits the entity + */ + public abstract boolean intersectsRay(Ray3D ray); + + /** + * @return point of intersection on the entity surface + */ + public abstract Vec3D getIntersectionPoint(); + + /** + * @return distance from ray origin to intersection point + */ + public abstract float getIntersectionDistance(); + + /** + * @return entity's surface normal vector at intersection point + */ + public Vec3D getNormalAtIntersection(); + + /** + * @param normalized true, if a normalized version should be returned + * @return direction vector from ray origin to intersection point + */ + public abstract Vec3D getIntersectionDir(boolean normalized); + +} \ No newline at end of file diff --git a/java/Mekstension/tools/toxi/geom/Matrix4x4.java b/java/Mekstension/tools/toxi/geom/Matrix4x4.java new file mode 100644 index 0000000..81ef031 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Matrix4x4.java @@ -0,0 +1,387 @@ +package toxi.geom; + +/** + * Implements a simple row-major 4x4 matrix class, all matrix operations are + * applied to new instances. Use {@link #transpose()} to convert from + * column-major formats... + */ + +// FIXME considere OpenGL is using column-major ordering +// TODO add methods to apply to current instance only +// TODO still needs lots of refactoring +public class Matrix4x4 { + + public double[][] matrix; + + static double[] retval = new double[3]; + + // Default constructor + // Sets matrix to identity + public Matrix4x4() { + init(); + matrix[0][0] = 1; + matrix[1][1] = 1; + matrix[2][2] = 1; + matrix[3][3] = 1; + } + + public Matrix4x4(Matrix4x4 m) { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + matrix[i][j] = m.matrix[i][j]; + } + + public Matrix4x4(double v11, double v12, double v13, double v14, + double v21, double v22, double v23, double v24, double v31, + double v32, double v33, double v34, double v41, double v42, + double v43, double v44) { + init(); + + matrix[0][0] = v11; + matrix[0][1] = v12; + matrix[0][2] = v13; + matrix[0][3] = v14; + + matrix[1][0] = v21; + matrix[1][1] = v22; + matrix[1][2] = v23; + matrix[1][3] = v24; + + matrix[2][0] = v31; + matrix[2][1] = v32; + matrix[2][2] = v33; + matrix[2][3] = v34; + + matrix[3][0] = v41; + matrix[3][1] = v42; + matrix[3][2] = v43; + matrix[3][3] = v44; + } + + // Initializing constructor - single-dimensional array + // Assumes row-major ordering (column idx increases faster) + public Matrix4x4(double[] array) { + if (array.length != 9 && array.length != 16) { + throw new RuntimeException("Array.length must == 9 or 16"); + } + + init(); + + if (array.length == 16) { + matrix[0][0] = array[0]; + matrix[0][1] = array[1]; + matrix[0][2] = array[2]; + matrix[0][3] = array[3]; + + matrix[1][0] = array[4]; + matrix[1][1] = array[5]; + matrix[1][2] = array[6]; + matrix[1][3] = array[7]; + + matrix[2][0] = array[8]; + matrix[2][1] = array[9]; + matrix[2][2] = array[10]; + matrix[2][3] = array[11]; + + matrix[3][0] = array[12]; + matrix[3][1] = array[13]; + matrix[3][2] = array[14]; + matrix[3][3] = array[15]; + } else if (array.length == 9) { + matrix[0][0] = array[0]; + matrix[0][1] = array[1]; + matrix[0][2] = array[2]; + + matrix[1][0] = array[3]; + matrix[1][1] = array[4]; + matrix[1][2] = array[5]; + + matrix[2][0] = array[6]; + matrix[2][1] = array[7]; + matrix[2][2] = array[8]; + + matrix[3][0] = array[9]; + matrix[3][1] = array[10]; + matrix[3][2] = array[11]; + matrix[3][3] = 1; + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + return "| " + matrix[0][0] + " " + matrix[0][1] + " " + matrix[0][2] + + " " + matrix[0][3] + " |\n" + "| " + matrix[1][0] + " " + + matrix[1][1] + " " + matrix[1][2] + " " + matrix[1][3] + + " |\n" + "| " + matrix[2][0] + " " + matrix[2][1] + " " + + matrix[2][2] + " " + matrix[2][3] + " |\n" + "| " + + matrix[3][0] + " " + matrix[3][1] + " " + matrix[3][2] + " " + + matrix[3][3] + " |"; + } + + public Matrix4x4 identity() { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + matrix[i][j] = 0; + matrix[0][0] = 1; + matrix[1][1] = 1; + matrix[2][2] = 1; + matrix[3][3] = 1; + return this; + } + + public Matrix4x4 add(Matrix4x4 rhs) { + Matrix4x4 retval = new Matrix4x4(); + + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + retval.matrix[i][j] = matrix[i][j] + rhs.matrix[i][j]; + + return retval; + } + + // Matrix-Matrix Subtraction + public Matrix4x4 sub(Matrix4x4 rhs) { + Matrix4x4 retval = new Matrix4x4(); + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + retval.matrix[i][j] = matrix[i][j] - rhs.matrix[i][j]; + + return retval; + } + + // Matrix-Scalar Multiplication + public Matrix4x4 multiply(double c) { + Matrix4x4 retval = new Matrix4x4(); + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + retval.matrix[i][j] = c * matrix[i][j]; + return retval; + } + + // Matrix-Vector Multiplication (Application) + public Vec3D apply(Vec3D vec) { + // Create a new vector and make it 4d homogenous + double vectorOut[] = new double[4]; + double vectorIn[] = new double[4]; + vectorIn[0] = vec.x; + vectorIn[1] = vec.y; + vectorIn[2] = vec.z; + vectorIn[3] = 1; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) + vectorOut[i] += vectorIn[j] * matrix[i][j]; + } + + return new Vec3D((float) vectorOut[0], (float) vectorOut[1], + (float) vectorOut[2]).scaleSelf((float) (1 / vectorOut[3])); + } + + // Matrix-Matrix Left-multiplication + // Given matrix this, mat; performs mat*this + public Matrix4x4 leftMultiply(Matrix4x4 mat) { + return mat.multiply(this); + } + + // Matrix-Matrix Right-multiplication + public Matrix4x4 multiply(Matrix4x4 mat) { + Matrix4x4 retval = new Matrix4x4(); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + retval.matrix[i][j] = 0; + for (int k = 0; k < 4; k++) { + retval.matrix[i][j] += matrix[i][k] * mat.matrix[k][j]; + } + } + } + return retval; + } + + // Apply Translation to Matrix + public Matrix4x4 translate(double dx, double dy, double dz) { + Matrix4x4 trn = new Matrix4x4(); + trn.matrix[0][3] = dx; + trn.matrix[1][3] = dy; + trn.matrix[2][3] = dz; + // return trn.mul(this); + return trn.leftMultiply(this); + } + + // Apply Scale to Matrix + public Matrix4x4 scale(double scaleX, double scaleY, double scaleZ) { + Matrix4x4 scl = new Matrix4x4(); + scl.matrix[0][0] = scaleX; + scl.matrix[1][1] = scaleY; + scl.matrix[2][2] = scaleZ; + // return scl.mul(this); + return scl.leftMultiply(this); + } + + // Apply Rotation about X to Matrix + public Matrix4x4 rotateX(double theta) { + Matrix4x4 rot = new Matrix4x4(); + rot.matrix[1][1] = rot.matrix[2][2] = Math.cos(theta); + rot.matrix[2][1] = Math.sin(theta); + rot.matrix[1][2] = -rot.matrix[2][1]; + return rot.leftMultiply(this); + } + + // Apply Rotation about Y to Matrix + public Matrix4x4 rotateY(double theta) { + Matrix4x4 rot = new Matrix4x4(); + rot.matrix[0][0] = rot.matrix[2][2] = Math.cos(theta); + rot.matrix[0][2] = Math.sin(theta); + rot.matrix[2][0] = -rot.matrix[0][2]; + return rot.leftMultiply(this); + } + + // Apply Rotation about Z to Matrix + public Matrix4x4 rotateZ(double theta) { + Matrix4x4 rot = new Matrix4x4(); + rot.matrix[0][0] = rot.matrix[1][1] = Math.cos(theta); + rot.matrix[1][0] = Math.sin(theta); + rot.matrix[0][1] = -rot.matrix[1][0]; + return rot.leftMultiply(this); + } + + // Apply Rotation about arbitrary axis to Matrix + public Matrix4x4 rotate(Vec3D axis, double theta) { + double x, y, z, s, c, t; + x = axis.x; + y = axis.y; + z = axis.z; + s = Math.sin(theta); + c = Math.cos(theta); + // c = Math.sqrt(1 - s * s); // this may be faster than the previous + // line + t = 1 - c; + + return (new Matrix4x4(t * x * x + c, t * x * y + s * z, t * x * z - s + * y, 0, t * x * y - s * z, t * y * y + c, t * y * z + s * x, 0, + t * x * z + s * y, t * y * z - s * x, t * z * z + c, 0, 0, 0, + 0, 1)).leftMultiply(this); + } + + // Matrix Transpose + public Matrix4x4 transpose() { + return new Matrix4x4(matrix[0][0], matrix[1][0], matrix[2][0], + matrix[3][0], matrix[0][1], matrix[1][1], matrix[2][1], + matrix[3][1], matrix[0][2], matrix[1][2], matrix[2][2], + matrix[3][2], matrix[0][3], matrix[1][3], matrix[2][3], + matrix[3][3]); + } + + // Matrix Inversion using Cramer's Method + // Computes Adjoint matrix divided by determinant + // Code modified from + // http://www.intel.com/design/pentiumiii/sml/24504301.pdf + // Turns out we don't need this after all + public Matrix4x4 inverse() { + double[] mat = new double[16]; + double[] dst = new double[16]; + + // Copy all of the elements into the linear array + for (int i = 0, k = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + mat[k++] = matrix[i][j]; + + double[] tmp = new double[12]; + double src[] = new double[16]; + double det; + + for (int i = 0; i < 4; i++) { + int i4 = i << 2; + src[i] = mat[i4]; + src[i + 4] = mat[i4 + 1]; + src[i + 8] = mat[i4 + 2]; + src[i + 12] = mat[i4 + 3]; + } + + /* calculatepairsforfirst8elements(cofactors) */ + tmp[0] = src[10] * src[15]; + tmp[1] = src[11] * src[14]; + tmp[2] = src[9] * src[15]; + tmp[3] = src[11] * src[13]; + tmp[4] = src[9] * src[14]; + tmp[5] = src[10] * src[13]; + tmp[6] = src[8] * src[15]; + tmp[7] = src[11] * src[12]; + tmp[8] = src[8] * src[14]; + tmp[9] = src[10] * src[12]; + tmp[10] = src[8] * src[13]; + tmp[11] = src[9] * src[12]; + + /* calculatefirst8elements(cofactors) */ + dst[0] = tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7]; + dst[0] -= tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7]; + dst[1] = tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7]; + dst[1] -= tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7]; + dst[2] = tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7]; + dst[2] -= tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7]; + dst[3] = tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6]; + dst[3] -= tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6]; + dst[4] = tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3]; + dst[4] -= tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3]; + dst[5] = tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3]; + dst[5] -= tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3]; + dst[6] = tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3]; + dst[6] -= tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3]; + dst[7] = tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2]; + dst[7] -= tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2]; + + /* calculatepairsforsecond8elements(cofactors) */ + tmp[0] = src[2] * src[7]; + tmp[1] = src[3] * src[6]; + tmp[2] = src[1] * src[7]; + tmp[3] = src[3] * src[5]; + tmp[4] = src[1] * src[6]; + tmp[5] = src[2] * src[5]; + tmp[6] = src[0] * src[7]; + tmp[7] = src[3] * src[4]; + tmp[8] = src[0] * src[6]; + tmp[9] = src[2] * src[4]; + tmp[10] = src[0] * src[5]; + tmp[11] = src[1] * src[4]; + + /* calculatesecond8elements(cofactors) */ + dst[8] = tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15]; + dst[8] -= tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15]; + dst[9] = tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15]; + dst[9] -= tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15]; + dst[10] = tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15]; + dst[10] -= tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15]; + dst[11] = tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14]; + dst[11] -= tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14]; + dst[12] = tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9]; + dst[12] -= tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10]; + dst[13] = tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10]; + dst[13] -= tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8]; + dst[14] = tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8]; + dst[14] -= tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9]; + dst[15] = tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9]; + dst[15] -= tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8]; + + det = src[0] * dst[0] + src[1] * dst[1] + src[2] * dst[2] + src[3] + * dst[3]; + + det = 1 / det; + + Matrix4x4 ret = new Matrix4x4(); + for (int i = 0, k = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + ret.matrix[i][j] = dst[k++] * det; + return ret; + } + + private final void init() { + matrix = new double[4][]; + matrix[0] = new double[4]; + matrix[1] = new double[4]; + matrix[2] = new double[4]; + matrix[3] = new double[4]; + } +} diff --git a/java/Mekstension/tools/toxi/geom/Plane.java b/java/Mekstension/tools/toxi/geom/Plane.java new file mode 100644 index 0000000..36fa5dc --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Plane.java @@ -0,0 +1,91 @@ +package toxi.geom; + +import toxi.math.MathUtils; + +/** + * Class to describe and work with infinite generic 3D planes. Useful for + * intersection problems and classifying points. + * + * @author Karsten Schmidt + * + */ +public class Plane extends Vec3D { + + public static final Plane XY = new Plane(new Vec3D(), Vec3D.Z_AXIS); + public static final Plane XZ = new Plane(new Vec3D(), Vec3D.Y_AXIS); + public static final Plane YZ = new Plane(new Vec3D(), Vec3D.X_AXIS); + + /** + * Classifier constant for {@link #classifyPoint(Vec3D)} + */ + public static final int PLANE_FRONT = -1; + + /** + * Classifier constant for {@link #classifyPoint(Vec3D)} + */ + public static final int PLANE_BACK = 1; + + /** + * Classifier constant for {@link #classifyPoint(Vec3D)} + */ + public static final int ON_PLANE = 0; + + public Vec3D normal; + + public Plane(Vec3D origin, Vec3D norm) { + super(origin); + normal = norm.getNormalized(); + } + + // TODO add constructor for creating a plane from a Triangle or 3 Vec3D's + + /** + * Calculates distance from the plane to point P. + * + * @param p + * @return distance + */ + public float getDistanceToPoint(Vec3D p) { + float sn = -normal.dot(p.sub(this)); + float sd = normal.magSquared(); + Vec3D isec = p.add(normal.scale(sn / sd)); + return isec.distanceTo(p); + } + + /** + * Calculates the intersection point between plane and ray (line). + * + * @param r + * @return intersection point or null if ray doesn't intersect plane + */ + public Vec3D getIntersectionWithRay(Ray3D r) { + float denom = normal.dot(r.getDirection()); + if (denom > MathUtils.EPS) { + float u = normal.dot(this.sub(r)) / denom; + return r.getPointAtDistance(u); + } else + return null; + } + + /** + * Classifies the relative position of the given point to the plane. + * + * @return One of the 3 integer classification codes: PLANE_FRONT, + * PLANE_BACK, ON_PLANE + */ + public int classifyPoint(Vec3D p) { + float d = this.sub(p).dot(normal); + if (d < -MathUtils.EPS) + return PLANE_FRONT; + else if (d > MathUtils.EPS) + return PLANE_BACK; + return ON_PLANE; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("origin: ").append(super.toString()).append(" norm: ") + .append(normal.toString()); + return sb.toString(); + } +} diff --git a/java/Mekstension/tools/toxi/geom/PointList.java b/java/Mekstension/tools/toxi/geom/PointList.java new file mode 100644 index 0000000..968e3db --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/PointList.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.geom; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * This class enables group/set operations of multiple Vec3D's at once. + * + * @author Karsten Schmidt + * + */ + +@SuppressWarnings("unchecked") +public class PointList extends ArrayList { + + public PointList() { + super(); + } + + public PointList addSelf(Vec3D offset) { + Iterator i=iterator(); + while(i.hasNext()) { + ((Vec3D)i.next()).addSelf(offset); + } + return this; + } + + public PointList subSelf(Vec3D offset) { + Iterator i=iterator(); + while(i.hasNext()) { + ((Vec3D)i.next()).subSelf(offset); + } + return this; + } + + public PointList scaleSelf(Vec3D factor) { + Iterator i=iterator(); + while(i.hasNext()) { + ((Vec3D)i.next()).scaleSelf(factor); + } + return this; + } + + public PointList scaleSelf(float factor) { + Iterator i=iterator(); + while(i.hasNext()) { + ((Vec3D)i.next()).scaleSelf(factor); + } + return this; + } +} diff --git a/java/Mekstension/tools/toxi/geom/PointOctree.java b/java/Mekstension/tools/toxi/geom/PointOctree.java new file mode 100644 index 0000000..fc24d6c --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/PointOctree.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.geom; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +/** + * Implements a spatial subdivision tree to work efficiently with large numbers + * of 3D particles. This octree can only be used for particle type objects and + * does NOT support 3D mesh geometry as other forms of Octrees do. + * + * For further reference also see the OctreeDemo in the /examples folder. + * + */ +public class PointOctree extends AABB { + + /** + * alternative tree recursion limit, number of world units when cells are + * not subdivided any further + */ + protected float minNodeSize = 4; + + /** + * + */ + protected PointOctree parent; + + protected PointOctree[] children; + + protected byte numChildren; + + protected ArrayList data; + + protected float dim, dim2; + + protected Vec3D offset; + + protected int depth = 0; + + private boolean isAutoReducing = false; + + /** + * Constructs a new PointOctree node within the AABB cube volume: {o.x, o.y, + * o.z} ... {o.x+size, o.y+size, o.z+size} + * + * @param o + * tree origin + * @param size + * size of the tree volume along a single axis + */ + public PointOctree(Vec3D o, float size) { + this(null, o, size / 2); + } + + /** + * Constructs a new PointOctree node within the AABB cube volume: {o.x, o.y, + * o.z} ... {o.x+size, o.y+size, o.z+size} + * + * @param p + * parent node + * @param o + * tree origin + * @param halfSize + * half length of the tree volume along a single axis + */ + private PointOctree(PointOctree p, Vec3D o, float halfSize) { + super(o.add(halfSize, halfSize, halfSize), new Vec3D(halfSize, + halfSize, halfSize)); + parent = p; + if (parent != null) + depth = parent.depth + 1; + dim = halfSize * 2; + dim2 = halfSize; + offset = o; + numChildren = 0; + } + + /** + * Computes the local child octant/cube index for the given point + * + * @param plocal + * point in the node-local coordinate system + * @return octant index + */ + protected final int getOctantID(Vec3D plocal) { + return (plocal.x >= dim2 ? 1 : 0) + (plocal.y >= dim2 ? 2 : 0) + + (plocal.z >= dim2 ? 4 : 0); + } + + /** + * Adds a new point/particle to the tree structure. All points are stored + * within leaf nodes only. The tree implementation is using lazy + * instantiation for all intermediate tree levels. + * + * @param p + * @return true, if point has been added successfully + */ + public boolean addPoint(Vec3D p) { + // check if point is inside cube + if (p.isInAABB(this)) { + // only add data to leaves for now + if (dim2 <= minNodeSize) { + if (data == null) { + data = new ArrayList(); + } + data.add(p); + return true; + } else { + Vec3D plocal = p.sub(offset); + if (children == null) { + children = new PointOctree[8]; + } + int octant = getOctantID(plocal); + if (children[octant] == null) { + Vec3D off = offset.add(new Vec3D((octant & 1) != 0 ? dim2 + : 0, (octant & 2) != 0 ? dim2 : 0, + (octant & 4) != 0 ? dim2 : 0)); + children[octant] = new PointOctree(this, off, dim2 / 2); + numChildren++; + } + return children[octant].addPoint(p); + } + } + return false; + } + + /** + * Adds all points of the collection to the octree. IMPORTANT: Points need + * be of type Vec3D or have subclassed it. + * + * @param points + * point collection + * @return true, if all points have been added successfully. + */ + public boolean addAll(Collection points) { + Iterator i = points.iterator(); + boolean addedAll = true; + while (i.hasNext()) { + addedAll &= addPoint((Vec3D) i.next()); + } + return addedAll; + } + + /** + * Enables/disables auto reduction of branches after points have been + * deleted from the tree. Turned off by default. + * + * @param state + * true, to enable feature + */ + public void setTreeAutoReduction(boolean state) { + isAutoReducing = state; + } + + /** + * Removes a point from the tree and (optionally) tries to release memory by + * reducing now empty sub-branches. + * + * @param p + * point to delete + * @return true, if the point was found & removed + */ + public boolean remove(Vec3D p) { + boolean found = false; + PointOctree leaf = getLeafForPoint(p); + if (leaf != null) { + if (leaf.data.remove(p)) { + found = true; + if (isAutoReducing && leaf.data.size() == 0) { + leaf.reduceBranch(); + } + } + } + return found; + } + + public void removeAll(Collection points) { + Iterator i = points.iterator(); + while (i.hasNext()) { + remove((Vec3D) i.next()); + } + } + + private void reduceBranch() { + if (data != null && data.size() == 0) + data = null; + if (numChildren > 0) { + for (int i = 0; i < 8; i++) { + if (children[i] != null && children[i].data == null) + children[i] = null; + } + } + if (parent != null) { + parent.reduceBranch(); + } + } + + /** + * Finds the leaf node which spatially relates to the given point + * + * @param p + * point to check + * @return leaf node or null if point is outside the tree dimensions + */ + protected PointOctree getLeafForPoint(Vec3D p) { + // if not a leaf node... + if (p.isInAABB(this)) { + if (numChildren > 0) { + int octant = getOctantID(p.sub(offset)); + if (children[octant] != null) { + return children[octant].getLeafForPoint(p); + } + } else if (data != null) { + return this; + } + } + return null; + } + + /** + * Selects all stored points within the given sphere volume + * + * @param sphereOrigin + * @param clipRadius + * @return selected points + */ + public ArrayList getPointsWithinSphere(Vec3D sphereOrigin, + float clipRadius) { + return getPointsWithinSphere(new Sphere(sphereOrigin, clipRadius)); + } + + /** + * Selects all stored points within the given sphere volume + * + * @param s + * sphere + * @return selected points + */ + public ArrayList getPointsWithinSphere(Sphere s) { + ArrayList results = null; + if (this.intersectsSphere(s)) { + if (data != null) { + for (int i = data.size() - 1; i >= 0; i--) { + Vec3D q = (Vec3D) data.get(i); + if (q.isInSphere(s)) { + if (results == null) { + results = new ArrayList(); + } + results.add(q); + } + } + } else if (numChildren > 0) { + for (int i = 0; i < 8; i++) { + if (children[i] != null) { + ArrayList points = children[i] + .getPointsWithinSphere(s); + if (points != null) { + if (results == null) { + results = new ArrayList(); + } + results.addAll(points); + } + } + } + } + } + return results; + } + + /** + * Selects all stored points within the given axis-aligned bounding box. + * + * @param b + * AABB + * @return all points with the box volume + */ + public ArrayList getPointsWithinBox(AABB b) { + ArrayList results = null; + if (this.intersectsBox(b)) { + if (data != null) { + for (int i = data.size() - 1; i >= 0; i--) { + Vec3D q = (Vec3D) data.get(i); + if (q.isInAABB(b)) { + if (results == null) { + results = new ArrayList(); + } + results.add(q); + } + } + } else if (numChildren > 0) { + for (int i = 0; i < 8; i++) { + if (children[i] != null) { + ArrayList points = children[i] + .getPointsWithinBox(b); + if (points != null) { + if (results == null) { + results = new ArrayList(); + } + results.addAll(points); + } + } + } + } + } + return results; + } + + /* + * (non-Javadoc) + * + * @see toxi.geom.AABB#toString() + */ + public String toString() { + return " offset: " + super.toString() + " size: " + dim; + } + + /** + * Returns the minimum size of nodes (in world units). This value acts as + * tree recursion limit since nodes smaller than this size are not + * subdivided further. Leaf node are always smaller or equal to this size. + * + * @return the minimum size of tree nodes + */ + public float getMinNodeSize() { + return minNodeSize; + } + + /** + * @param minNodeSize + */ + public void setMinNodeSize(float minNodeSize) { + this.minNodeSize = minNodeSize; + } + + public float getNodeSize() { + return dim; + } + + /** + * @return the number of child nodes (max. 8) + */ + public int getNumChildren() { + return numChildren; + } + + /** + * @return a copy of the child nodes array + */ + public PointOctree[] getChildren() { + PointOctree[] clones = new PointOctree[8]; + System.arraycopy(children, 0, clones, 0, 8); + return clones; + } +} diff --git a/java/Mekstension/tools/toxi/geom/Quad.java b/java/Mekstension/tools/toxi/geom/Quad.java new file mode 100644 index 0000000..69876b8 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Quad.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.geom; + +import toxi.geom.Vec3D; + +public class Quad { + public Vec3D[] vertices; + + public Quad(Vec3D[] vertices, int vertOffset) { + this.vertices = new Vec3D[4]; + System.arraycopy(vertices, vertOffset, this.vertices, 0, 4); + } + + public Quad(Quad q) { + vertices = new Vec3D[4]; + for (int i = 0; i < 4; i++) { + vertices[i] = new Vec3D(q.vertices[i]); + } + } + + public Quad(float x1, float y1, float x2, float y2, float x3, float y3, + float x4, float y4) { + vertices = new Vec3D[] { new Vec3D(x1, y1, 0), new Vec3D(x2, y2, 0), + new Vec3D(x3, y3, 0), new Vec3D(x4, y4, 0) }; + } +} diff --git a/java/Mekstension/tools/toxi/geom/Quaternion.java b/java/Mekstension/tools/toxi/geom/Quaternion.java new file mode 100644 index 0000000..4666ba9 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Quaternion.java @@ -0,0 +1,204 @@ +package toxi.geom; + +import toxi.math.MathUtils; + +/** + * Quaternion implementation with SLERP based on http://is.gd/2n9s + * + */ +public class Quaternion { + + public static final float DOT_THRESHOLD = 0.9995f; + + float x, y, z, w; + + Quaternion() { + reset(); + } + + Quaternion(float w, float x, float y, float z) { + this.w = w; + this.x = x; + this.y = y; + this.z = z; + } + + public Quaternion(float w, Vec3D v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = w; + } + + public Quaternion(Quaternion q) { + this.w = q.w; + this.x = q.x; + this.y = q.y; + this.z = q.z; + } + + public Quaternion reset() { + w = 1.0f; + x = 0.0f; + y = 0.0f; + z = 0.0f; + return this; + } + + public Quaternion set(float w, Vec3D v) { + this.w = w; + x = v.x; + y = v.y; + z = v.z; + return this; + } + + public Quaternion set(Quaternion q) { + w = q.w; + x = q.x; + y = q.y; + z = q.z; + return this; + } + + public Quaternion multiply(Quaternion q2) { + Quaternion res = new Quaternion(); + res.w = w * q2.w - x * q2.x - y * q2.y - z * q2.z; + res.x = w * q2.x + x * q2.w + y * q2.z - z * q2.y; + res.y = w * q2.y + y * q2.w + z * q2.x - x * q2.z; + res.z = w * q2.z + z * q2.w + x * q2.y - y * q2.x; + return res; + } + + public float[] getValue() { + float[] res = new float[4]; + float sa = (float) Math.sqrt(1.0f - w * w); + if (sa < MathUtils.EPS) + sa = 1.0f; + else + sa = 1.0f / sa; + res[0] = (float) Math.acos(w) * 2.0f; + res[1] = x * sa; + res[2] = y * sa; + res[3] = z * sa; + return res; + } + + public float magnitude() { + return (float) Math.sqrt(x * x + y * y + z * z + w * w); + } + + public Quaternion normalize() { + float mag = (float) Math.sqrt(x * x + y * y + z * z + w * w); + if (mag > 0) { + mag = 1f / mag; + x *= mag; + y *= mag; + z *= mag; + w *= mag; + } + return this; + } + + public Quaternion getNormalized() { + return new Quaternion(this).normalize(); + } + + public Quaternion scale(float t) { + return new Quaternion(x * t, y * t, z * t, w * t); + } + + public Quaternion scaleSelf(float t) { + x *= t; + y *= t; + z *= t; + w *= t; + return this; + } + + public Quaternion sub(Quaternion q) { + return new Quaternion(x - q.x, y - q.y, z - q.z, w - q.w); + } + + public Quaternion subSelf(Quaternion q) { + x -= q.x; + y -= q.y; + z -= q.z; + w -= q.w; + return this; + } + + public Quaternion add(Quaternion q) { + return new Quaternion(x + q.x, y + q.y, z - q.z, w + q.w); + } + + public Quaternion addSelf(Quaternion q) { + x += q.x; + y += q.y; + z += q.z; + w += q.w; + return this; + } + + /** + * Spherical interpolation to target quat (code ported from + * http://is.gd/2n7t) + * + * @param target + * quaternion + * @param t + * interpolation factor (0..1) + * @return new interpolated quat + */ + public Quaternion interpolateTo(Quaternion target, float t) { + float dot = new Vec3D(x, y, z).dot(new Vec3D(target.x, target.y, + target.z)); + if (dot > DOT_THRESHOLD) { + Quaternion result = new Quaternion(w + (target.w - w) * t, x + + (target.x - x) * t, y + (target.y - y) * t, z + + (target.z - z) * t); + result.normalize(); + return result; + } + dot = MathUtils.clip(dot, -1, 1); + double theta = Math.acos(dot) * t; + Quaternion q = target.sub(scale(dot)); + q.normalize(); + return scale((float) Math.cos(theta)).addSelf( + q.scaleSelf((float) Math.sin(theta))); + } + + /** + * Converts the quat into a 4x4 Matrix. Assumes the quat is currently + * normalized (if not, you'll need to call {@link #normalize()} first). This + * calculation would be a lot more complicated for non-unit length + * quaternions Note: The constructor of Matrix4 expects the Matrix in + * column-major format like expected by OpenGL + * + * @return result matrix + */ + public Matrix4x4 getMatrix() { + float x2 = x * x; + float y2 = y * y; + float z2 = z * z; + float xy = x * y; + float xz = x * z; + float yz = y * z; + float wx = w * x; + float wy = w * y; + float wz = w * z; + + return new Matrix4x4(1.0f - 2.0f * (y2 + z2), 2.0f * (xy - wz), + 2.0f * (xz + wy), 0.0f, 2.0f * (xy + wz), + 1.0f - 2.0f * (x2 + z2), 2.0f * (yz - wx), 0.0f, + 2.0f * (xz - wy), 2.0f * (yz + wx), 1.0f - 2.0f * (x2 + y2), + 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); + } + + public String toString() { + StringBuffer sb = new StringBuffer(48); + sb.append("{axis: [").append(x).append(",").append(y).append(",") + .append(z).append("], w: ").append(w).append("}"); + return sb.toString(); + } +} diff --git a/java/Mekstension/tools/toxi/geom/Ray3D.java b/java/Mekstension/tools/toxi/geom/Ray3D.java new file mode 100644 index 0000000..1d6162d --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Ray3D.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.geom; + +/** + * A simple 3D ray datatype + */ +public class Ray3D extends Vec3D { + protected Vec3D dir; + + public Ray3D(float x, float y, float z, Vec3D d) { + super(x,y,z); + dir=d.getNormalized(); + } + + public Ray3D(Vec3D o, Vec3D d) { + super(o); + dir=d.getNormalized(); + } + + public Vec3D getDirection() { + return dir; + } + + public Vec3D getPointAtDistance(float dist) { + return add(dir.scale(dist)); + } + + public String toString() { + StringBuffer sb=new StringBuffer(); + sb.append("origin: ").append(super.toString()).append(" dir: ").append(dir); + return sb.toString(); + } +} diff --git a/java/Mekstension/tools/toxi/geom/Reflector.java b/java/Mekstension/tools/toxi/geom/Reflector.java new file mode 100644 index 0000000..ee44964 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Reflector.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.geom; + +/** + * Generic interface for ray reflection with 3D geometry + */ +public interface Reflector extends Intersector { + + /** + * Reflects given ray on the entity's surface + * + * @param ray incident ray + * @return reflected ray starting from intersection point + */ + public Ray3D reflectRay(Ray3D ray); + + /** + * @return angle between incident ray and surface normal + */ + public float getReflectionAngle(); + + /** + * Returns the point on the reflected ray at given distance from the + * intersection point + * + * @param dist distance from isect position + * @return point on reflected ray + */ + public Vec3D getReflectedRayPointAtDistance(float dist); +} \ No newline at end of file diff --git a/java/Mekstension/tools/toxi/geom/Sphere.java b/java/Mekstension/tools/toxi/geom/Sphere.java new file mode 100644 index 0000000..d9c7321 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Sphere.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.geom; + +public class Sphere extends Vec3D { + + float radius; + + public Sphere() { + radius=1; + } + + public Sphere(Vec3D v, float r) { + super(v); + radius = r; + } + + public Sphere(Sphere s) { + this(s,s.radius); + } +} diff --git a/java/Mekstension/tools/toxi/geom/SphereIntersectorReflector.java b/java/Mekstension/tools/toxi/geom/SphereIntersectorReflector.java new file mode 100644 index 0000000..ebb89f4 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/SphereIntersectorReflector.java @@ -0,0 +1,135 @@ +package toxi.geom; + +import toxi.math.MathUtils; + +public class SphereIntersectorReflector implements Intersector, Reflector { + protected Vec3D sOrigin, sRadius; + + protected Vec3D isectPos, isectDir; + + protected float isectDist; + + protected Vec3D sphereNormal; + + protected float reflectTheta; + + protected Vec3D reflectedDir, reflectedPos; + + public SphereIntersectorReflector(Vec3D o, float r) { + sOrigin = new Vec3D(o); + sRadius = new Vec3D(r, r, r); + } + + public SphereIntersectorReflector(Sphere s) { + this(s, s.radius); + } + + public boolean intersectsRay(Ray3D ray) { + isectDist = ray.intersectRaySphere(ray.getDirection(), sOrigin, + sRadius.x); + if (isectDist >= 0) { + // get the intersection point + isectPos = ray.add(ray.getDirection().scale(isectDist)); + // calculate the direction from our point to the intersection pos + isectDir = isectPos.sub(ray); + return true; + } + return false; + } + + /* + * (non-Javadoc) + * + * @see toxi.geom.Reflector#reflectRay(toxi.geom.Vec3D, toxi.geom.Vec3D) + */ + public Ray3D reflectRay(Ray3D ray) { + if (intersectsRay(ray)) { + // compute the normal vector of the sphere at the intersection + // position + sphereNormal = isectPos.tangentPlaneNormalOfEllipsoid(sOrigin, + sRadius); + // compute the reflection angle + reflectTheta = isectDir.angleBetween(sphereNormal, true) * 2 + + MathUtils.PI; + // then form a perpendicular vector standing on the plane spanned by + // isectDir and sphereNormal + // this vector will be used to mirror the ray around the + // intersection point + Vec3D reflectNormal = isectDir.getNormalized().cross(sphereNormal) + .normalize(); + if (!reflectNormal.isZeroVector()) { + // compute the reflected ray direction + reflectedDir = isectDir.getNormalized().rotateAroundAxis( + reflectNormal, reflectTheta); + } else { + reflectedDir = isectDir.getInverted(); + } + return new Ray3D(isectPos, reflectedDir); + } else { + return null; + } + } + + /* + * (non-Javadoc) + * + * @see toxi.geom.Reflector#getReflectionAngle() + */ + public float getReflectionAngle() { + return reflectTheta; + } + + /* + * (non-Javadoc) + * + * @see toxi.geom.Reflector#getReflectedRayPointAtDistance(float) + */ + public Vec3D getReflectedRayPointAtDistance(float dist) { + if (reflectedDir != null) { + return isectPos.add(reflectedDir.scale(dist)); + } else + return null; + } + + /* + * (non-Javadoc) + * + * @see toxi.geom.Intersector#getIntersectionPoint() + */ + public Vec3D getIntersectionPoint() { + return isectPos; + } + + /* + * (non-Javadoc) + * + * @see toxi.geom.Intersector#getIntersectionDistance() + */ + public float getIntersectionDistance() { + return isectDist; + } + + /* + * (non-Javadoc) + * + * @see toxi.geom.Intersector#getIntersectionDir(boolean) + */ + public Vec3D getIntersectionDir(boolean normalized) { + if (isectDir != null) { + if (normalized) { + return isectDir.getNormalized(); + } + return isectDir; + } + return null; + } + + /* + * (non-Javadoc) + * + * @see toxi.geom.Intersector#getNormalAtIntersection() + */ + public Vec3D getNormalAtIntersection() { + return sphereNormal; + } +} diff --git a/java/Mekstension/tools/toxi/geom/Spline3D.java b/java/Mekstension/tools/toxi/geom/Spline3D.java new file mode 100644 index 0000000..d83f7d9 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Spline3D.java @@ -0,0 +1,126 @@ +package toxi.geom; + +import java.util.ArrayList; + +/** + * This is a generic 3D B-Spline class for curves of arbitrary length, control + * handles and patches are created and joined automatically as described here: + * http://www.ibiblio.org/e-notes/Splines/Bint.htm + */ +public class Spline3D { + + public Vec3D[] points; + public Vec3D[] delta; + + protected Vec3D[] coeffA; + protected float[] bi; + + private int numP; + public ArrayList vertices; + + public BernsteinPolynomial bernstein; + + public Spline3D(Vec3D[] p) { + points = p; + numP = points.length; + coeffA = new Vec3D[numP]; + delta = new Vec3D[numP]; + bi = new float[numP]; + for (int i = 0; i < numP; i++) { + coeffA[i] = new Vec3D(); + delta[i] = new Vec3D(); + bi[i] = 0; + } + } + + protected void findCPoints() { + bi[1] = -.25f; + coeffA[1].set((points[2].x - points[0].x - delta[0].x) * 0.25f, + (points[2].y - points[0].y - delta[0].y) * 0.25f, (points[2].z + - points[0].z - delta[0].z) * 0.25f); + for (int i = 2; i < numP - 1; i++) { + bi[i] = -1 / (4 + bi[i - 1]); + coeffA[i].set( + -(points[i + 1].x - points[i - 1].x - coeffA[i - 1].x) + * bi[i], + -(points[i + 1].y - points[i - 1].y - coeffA[i - 1].y) + * bi[i], + -(points[i + 1].z - points[i - 1].z - coeffA[i - 1].z) + * bi[i]); + } + for (int i = numP - 2; i > 0; i--) { + delta[i].set(coeffA[i].x + delta[i + 1].x * bi[i], coeffA[i].y + + delta[i + 1].y * bi[i], coeffA[i].z + delta[i + 1].z + * bi[i]); + } + } + + /** + * Computes all curve vertices based on the resolution/number of + * subdivisions requested. The higher, the more vertices are computed: + * (number of control points - 1) * resolution + * + * @param res + * resolution + * @return list of Vec3D vertices along the curve + */ + public ArrayList computeVertices(int res) { + if (bernstein == null || bernstein.resolution != res) { + bernstein = new BernsteinPolynomial(res); + } + if (vertices == null) + vertices = new ArrayList(); + else + vertices.clear(); + findCPoints(); + Vec3D deltaP = new Vec3D(); + Vec3D deltaQ = new Vec3D(); + for (int i = 0; i < numP - 1; i++) { + Vec3D p = points[i]; + Vec3D q = points[i + 1]; + deltaP.set(delta[i]).addSelf(p); + deltaQ.set(q).subSelf(delta[i + 1]); + for (int k = 0; k < bernstein.resolution; k++) { + float x = p.x * bernstein.b0[k] + deltaP.x * bernstein.b1[k] + + deltaQ.x * bernstein.b2[k] + q.x * bernstein.b3[k]; + float y = p.y * bernstein.b0[k] + deltaP.y * bernstein.b1[k] + + deltaQ.y * bernstein.b2[k] + q.y * bernstein.b3[k]; + float z = p.z * bernstein.b0[k] + deltaP.z * bernstein.b1[k] + + deltaQ.z * bernstein.b2[k] + q.z * bernstein.b3[k]; + vertices.add(new Vec3D(x, y, z)); + } + } + return vertices; + } + + // FIXME this isn't generic enough & worked just for Nokia Friends project + public void createSymmetricEnds() { + delta[0] = points[1].scale(0.75f); + int lastIdx = numP - 2; + delta[lastIdx] = points[lastIdx].scale(-0.75f); + } + + class BernsteinPolynomial { + float[] b0, b1, b2, b3; + int resolution; + + public BernsteinPolynomial(int res) { + resolution = res; + b0 = new float[res]; + b1 = new float[res]; + b2 = new float[res]; + b3 = new float[res]; + float t = 0; + float dt = 1.0f / (resolution - 1); + for (int i = 0; i < resolution; i++) { + float t1 = 1 - t; + float t12 = t1 * t1, t2 = t * t; + b0[i] = t1 * t12; + b1[i] = 3 * t * t12; + b2[i] = 3 * t2 * t1; + b3[i] = t * t2; + t += dt; + } + } + } +} \ No newline at end of file diff --git a/java/Mekstension/tools/toxi/geom/Triangle.java b/java/Mekstension/tools/toxi/geom/Triangle.java new file mode 100644 index 0000000..b98d042 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Triangle.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.geom; + +import toxi.math.MathUtils; + +public class Triangle { + + Vec3D a, b, c; + Vec3D normal; + + public Triangle(Vec3D a, Vec3D b, Vec3D c) { + this.a = a; + this.b = b; + this.c = c; + } + + public void computeNormal() { + normal = a.sub(b).cross(a.sub(c)).normalize(); + } + + /** + * Checks if point vector is inside the triangle created by the points a, b + * and c. These points will create a plane and the point checked will have + * to be on this plane in the region between a,b,c. + * + * Note: The triangle must be defined in clockwise order a,b,c + * + * @return true, if point is in triangle. + */ + public boolean containsPoint(Vec3D p) { + Vec3D v1 = p.sub(a).normalize(); + Vec3D v2 = p.sub(b).normalize(); + Vec3D v3 = p.sub(c).normalize(); + + double total_angles = Math.acos(v1.dot(v2)); + total_angles += Math.acos(v2.dot(v3)); + total_angles += Math.acos(v3.dot(v1)); + + return (MathUtils.abs((float) total_angles - MathUtils.TWO_PI) <= 0.005f); + } + + /** + * Finds and returns the closest point on any of the triangle edges to the + * point given. + * + * @param p + * point to check + * @return closest point + */ + + public Vec3D getClosestVertexTo(Vec3D p) { + Vec3D Rab = p.closestPointOnLine(a, b); + Vec3D Rbc = p.closestPointOnLine(b, c); + Vec3D Rca = p.closestPointOnLine(c, a); + + float dAB = p.sub(Rab).magSquared(); + float dBC = p.sub(Rbc).magSquared(); + float dCA = p.sub(Rca).magSquared(); + + float min = dAB; + Vec3D result = Rab; + + if (dBC < min) { + min = dBC; + result = Rbc; + } + if (dCA < min) + result = Rca; + + return result; + } + + /** + * @see #closestPointOnSurface(Vec3D) + * @deprecated + */ + public Vec3D closedPoint(Vec3D p) { + return closestPointOnSurface(p); + } + + /** + * Computes the the point closest to the current vector on the surface of + * triangle abc. + * + * From Real-Time Collision Detection by Christer Ericson, published by + * Morgan Kaufmann Publishers, Copyright 2005 Elsevier Inc + * + * @return closest point on triangle (result may also be one of a, b or c) + */ + public Vec3D closestPointOnSurface(Vec3D p) { + Vec3D ab = b.sub(a); + Vec3D ac = c.sub(a); + Vec3D bc = c.sub(b); + + Vec3D pa = p.sub(a); + Vec3D pb = p.sub(b); + Vec3D pc = p.sub(c); + + Vec3D ap = a.sub(p); + Vec3D bp = b.sub(p); + Vec3D cp = c.sub(p); + + // Compute parametric position s for projection P' of P on AB, + // P' = A + s*AB, s = snom/(snom+sdenom) + float snom = pa.dot(ab); + float sdenom = pb.dot(a.sub(b)); + + // Compute parametric position t for projection P' of P on AC, + // P' = A + t*AC, s = tnom/(tnom+tdenom) + float tnom = pa.dot(ac); + float tdenom = pc.dot(a.sub(c)); + + if (snom <= 0.0f && tnom <= 0.0f) + return a; // Vertex region early out + + // Compute parametric position u for projection P' of P on BC, + // P' = B + u*BC, u = unom/(unom+udenom) + float unom = pb.dot(bc); + float udenom = pc.dot(b.sub(c)); + + if (sdenom <= 0.0f && unom <= 0.0f) + return b; // Vertex region early out + if (tdenom <= 0.0f && udenom <= 0.0f) + return c; // Vertex region early out + + // P is outside (or on) AB if the triple scalar product [N PA PB] <= 0 + Vec3D n = ab.cross(ac); + float vc = n.dot(ap.crossSelf(bp)); + + // If P outside AB and within feature region of AB, + // return projection of P onto AB + if (vc <= 0.0f && snom >= 0.0f && sdenom >= 0.0f) { + // return a + snom / (snom + sdenom) * ab; + return a.add(ab.scaleSelf(snom / (snom + sdenom))); + } + + // P is outside (or on) BC if the triple scalar product [N PB PC] <= 0 + float va = n.dot(bp.crossSelf(cp)); + // If P outside BC and within feature region of BC, + // return projection of P onto BC + if (va <= 0.0f && unom >= 0.0f && udenom >= 0.0f) { + // return b + unom / (unom + udenom) * bc; + return b.add(bc.scaleSelf(unom / (unom + udenom))); + } + + // P is outside (or on) CA if the triple scalar product [N PC PA] <= 0 + float vb = n.dot(cp.crossSelf(ap)); + // If P outside CA and within feature region of CA, + // return projection of P onto CA + if (vb <= 0.0f && tnom >= 0.0f && tdenom >= 0.0f) { + // return a + tnom / (tnom + tdenom) * ac; + return a.add(ac.scaleSelf(tnom / (tnom + tdenom))); + } + + // P must project inside face region. Compute Q using barycentric + // coordinates + float u = va / (va + vb + vc); + float v = vb / (va + vb + vc); + float w = 1.0f - u - v; // = vc / (va + vb + vc) + // return u * a + v * b + w * c; + return a.scale(u).addSelf(b.scale(v)).addSelf(c.scale(w)); + } + + public boolean isClockwiseInXY() { + return Triangle.isClockwiseInXY(a, b, c); + } + + public boolean isClockwiseInXZ() { + return Triangle.isClockwiseInXY(a, b, c); + } + + public boolean isClockwiseInYZ() { + return Triangle.isClockwiseInXY(a, b, c); + } + + public static boolean isClockwiseInXY(Vec3D a, Vec3D b, Vec3D c) { + float determ = (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y); + return (determ < 0.0); + } + + public static boolean isClockwiseInXZ(Vec3D a, Vec3D b, Vec3D c) { + float determ = (b.x - a.x) * (c.z - a.z) - (c.x - a.x) * (b.z - a.z); + return (determ < 0.0); + } + + public static boolean isClockwiseInYZ(Vec3D a, Vec3D b, Vec3D c) { + float determ = (b.y - a.y) * (c.z - a.z) - (c.y - a.y) * (b.z - a.z); + return (determ < 0.0); + } +} diff --git a/java/Mekstension/tools/toxi/geom/Vec2D.java b/java/Mekstension/tools/toxi/geom/Vec2D.java new file mode 100644 index 0000000..b4a65c5 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Vec2D.java @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.geom; + +import java.awt.Rectangle; +import java.util.Random; + +import toxi.math.InterpolateStrategy; +import toxi.math.MathUtils; + +/** + * Comprehensive 2D vector class with additional basic intersection and + * collision detection features. + * + * @author Karsten Schmidt + * + */ +public class Vec2D { + + /** + * Defines positive X axis + */ + public static final Vec2D X_AXIS = new Vec2D(1, 0); + + /** + * Defines positive Y axis + */ + public static final Vec2D Y_AXIS = new Vec2D(0, 1); + + /** + * Creates a new vector from the given angle in the XY plane. + * + * The resulting vector for theta=0 is equal to the positive X axis. + * + * @param theta + * @return new vector pointing into the direction of the passed in angle + */ + public static final Vec2D fromTheta(float theta) { + return new Vec2D((float) Math.cos(theta), (float) Math.sin(theta)); + } + + /** + * Static factory method. Creates a new random unit vector using the default + * Math.random() Random instance. + * + * @return a new random normalized unit vector. + */ + public static final Vec2D randomVector() { + Vec2D rnd = new Vec2D((float) Math.random() * 2 - 1, (float) Math + .random() * 2 - 1); + return rnd.normalize(); + } + + /** + * Static factory method. Creates a new random unit vector using the given + * Random generator instance. I recommend to have a look at the + * https://uncommons-maths.dev.java.net library for a good choice of + * reliable and high quality random number generators. + * + * @return a new random normalized unit vector. + */ + public static final Vec2D randomVector(Random rnd) { + Vec2D v = new Vec2D(rnd.nextFloat() * 2 - 1, rnd.nextFloat() * 2 - 1); + return v.normalize(); + } + + /** + * X coordinate + */ + public float x; + + /** + * Y coordinate + */ + public float y; + + /** + * Creates a new zero vector + */ + public Vec2D() { + x = y = 0; + } + + /** + * Creates a new vector with the given coordinates + * + * @param x + * @param y + */ + public Vec2D(float x, float y) { + this.x = x; + this.y = y; + } + + /** + * Creates a new vector with the coordinates of the given vector + * + * @param v + * vector to be copied + */ + public Vec2D(Vec2D v) { + set(v); + } + + public final Vec2D abs() { + x = MathUtils.abs(x); + y = MathUtils.abs(y); + return this; + } + + /** + * Adds vector {a,b,c} and returns result as new vector. + * + * @param a + * X coordinate + * @param b + * Y coordinate + * @return result as new vector + */ + public final Vec2D add(float a, float b) { + return new Vec2D(x + a, y + b); + } + + /** + * Add vector v and returns result as new vector. + * + * @param v + * vector to add + * @return result as new vector + */ + public final Vec2D add(Vec2D v) { + return new Vec2D(x + v.x, y + v.y); + } + + /** + * Adds vector {a,b,c} and overrides coordinates with result. + * + * @param a + * X coordinate + * @param b + * Y coordinate + * @return itself + */ + public final Vec2D addSelf(float a, float b) { + x += a; + y += b; + return this; + } + + /** + * Adds vector v and overrides coordinates with result. + * + * @param v + * vector to add + * @return itself + */ + public final Vec2D addSelf(Vec2D v) { + x += v.x; + y += v.y; + return this; + } + + /** + * Computes the angle between this vector and vector V. This function + * assumes both vectors are normalized, if this can't be guaranteed, use the + * alternative implementation {@link #angleBetween(Vec2D, boolean)} + * + * @param v + * vector + * @return angle in radians, or NaN if vectors are parallel + */ + public final float angleBetween(Vec2D v) { + return (float) Math.acos(dot(v)); + } + + /** + * Computes the angle between this vector and vector V + * + * @param v + * vector + * @param forceNormalize + * true, if normalized versions of the vectors are to be used + * (Note: only copies will be used, original vectors will not be + * altered by this method) + * @return angle in radians, or NaN if vectors are parallel + */ + public final float angleBetween(Vec2D v, boolean forceNormalize) { + float theta; + if (forceNormalize) { + theta = getNormalized().dot(v.getNormalized()); + } else { + theta = dot(v); + } + return (float) Math.acos(theta); + } + + /** + * Compares the length of the vector with another one. + * + * @param vec + * vector to compare with + * @return -1 if other vector is longer, 0 if both are equal or else +1 + */ + public int compareTo(Object vec) { + Vec3D v = (Vec3D) vec; + if (Float.compare(x, v.x) == 0 && Float.compare(y, v.y) == 0) + return 0; + if (magSquared() < v.magSquared()) + return -1; + return 1; + } + + @Override + public boolean equals(Object obj) { + Vec3D v = (Vec3D) obj; + return (Float.compare(x, v.x) == 0 && Float.compare(y, v.y) == 0); + } + + /** + * Sets all vector components to 0. + * + * @return itself + */ + public final Vec2D clear() { + x = y = 0; + return this; + } + + /** + * + * Helper function for {@link #closestPointOnTriangle(Vec2D, Vec2D, Vec2D)} + * + * @param a + * start point of line segment + * @param b + * end point of line segment + * @return closest point on the line segment a -> b + */ + + public Vec2D closestPointOnLine(Vec2D a, Vec2D b) { + // Determine t (the length of the vector from �a� to 'this') + Vec2D c = sub(a); + Vec2D v = b.sub(a); + + float d = v.magnitude(); + v.normalize(); + + float t = v.dot(c); + + // Check to see if point is beyond the extents of the line segment + if (t < 0.0f) + return a; + if (t > d) + return b; + + // Return the point between 'a' and 'b' + // set length of V to t. V is normalized so this is easy + v.scaleSelf(t); + + return a.add(v); + } + + /** + * Finds and returns the closest point on any of the edges of the given + * triangle. + * + * @param a + * triangle vertex + * @param b + * triangle vertex + * @param c + * triangle vertex + * @return closest point + */ + + public Vec2D closestPointOnTriangle(Vec2D a, Vec2D b, Vec2D c) { + Vec2D Rab = closestPointOnLine(a, b); + Vec2D Rbc = closestPointOnLine(b, c); + Vec2D Rca = closestPointOnLine(c, a); + + float dAB = sub(Rab).magnitude(); + float dBC = sub(Rbc).magnitude(); + float dCA = sub(Rca).magnitude(); + + float min = dAB; + Vec2D result = Rab; + + if (dBC < min) { + min = dBC; + result = Rbc; + } + if (dCA < min) + result = Rca; + + return result; + } + + /** + * Forcefully fits the vector in the given rectangle. + * + * @param r + * @return itself + */ + + public final Vec2D constrain(Rectangle r) { + x = MathUtils.clip(x, r.x, r.x + r.width); + y = MathUtils.clip(y, r.y, r.y + r.height); + return this; + } + + /** + * @return a new independent instance/copy of a given vector + */ + public final Vec2D copy() { + return new Vec2D(this); + } + + /** + * Calculates distance to another vector + * + * @param v + * non-null vector + * @return distance or Float.NaN if v=null + */ + public final float distanceTo(Vec2D v) { + if (v != null) { + float dx = x - v.x; + float dy = y - v.y; + return (float) Math.sqrt(dx * dx + dy * dy); + } else { + return Float.NaN; + } + } + + /** + * Calculates the squared distance to another vector + * + * @see #magSquared() + * @param v + * non-null vector + * @return distance or NaN if v=null + */ + public final float distanceToSquared(Vec2D v) { + if (v != null) { + float dx = x - v.x; + float dy = y - v.y; + return dx * dx + dy * dy; + } else { + return Float.NaN; + } + } + + /** + * Computes the scalar product (dot product) with the given vector. + * + * @see Wikipedia entry< + * /a> + * + * @param v + * @return dot product + */ + public final float dot(Vec2D v) { + return x * v.x + y * v.y; + } + + /** + * Replaces the vector components with integer values of their current + * values + * + * @return itself + */ + public final Vec2D floor() { + x = MathUtils.floor(x); + y = MathUtils.floor(y); + return this; + } + + /** + * Replaces the vector components with the fractional part of their current + * values + * + * @return itself + */ + public final Vec2D frac() { + x -= MathUtils.floor(x); + y -= MathUtils.floor(y); + return this; + } + + public final Vec2D getAbs() { + return new Vec2D(this).abs(); + } + + /** + * Creates a copy of the vector which forcefully fits in the given + * rectangle. + * + * @param r + * @return fitted vector + */ + public final Vec2D getConstrained(Rectangle r) { + return new Vec2D(this).constrain(r); + } + + /** + * Creates a new vector whose components are the integer value of their + * current values + * + * @return result as new vector + */ + public final Vec2D getFloored() { + return new Vec2D(this).floor(); + } + + /** + * Creates a new vector whose components are the fractional part of their + * current values + * + * @return result as new vector + */ + public final Vec2D getFrac() { + return new Vec2D(this).frac(); + } + + /** + * Scales vector uniformly by factor -1 ( v = -v ) + * + * @return result as new vector + */ + public final Vec2D getInverted() { + return new Vec2D(-x, -y); + } + + /** + * Creates a copy of the vector with its magnitude limited to the length + * given + * + * @param lim + * new maximum magnitude + * @return result as new vector + */ + public final Vec2D getLimited(float lim) { + if (magSquared() > lim * lim) { + return getNormalized().scaleSelf(lim); + } + return new Vec2D(this); + } + + /** + * Produces the normalized version as a new vector + * + * @return new vector + */ + public final Vec2D getNormalized() { + return new Vec2D(this).normalize(); + } + + public final Vec2D getPerpendicular() { + return new Vec2D(this).perpendicular(); + } + + /** + * Creates a new vector rotated by the given angle around the Z axis. + * + * @param theta + * @return rotated vector + */ + public final Vec2D getRotated(float theta) { + return new Vec2D(this).rotate(theta); + } + + /** + * Creates a new vector in which all components are replaced with the signum + * of their original values. In other words if a components value was + * negative its new value will be -1, if zero => 0, if positive => +1 + * + * @return result vector + */ + public Vec2D getSignum() { + return new Vec2D(this).signum(); + } + + /** + * Computes the vector's direction in the XY plane (for example for 2D + * points). The positive X axis equals 0 degrees. + * + * @return rotation angle + */ + public final float heading() { + return (float) Math.atan2(y, x); + } + + /** + * Interpolates the vector towards the given target vector, using linear + * interpolation + * + * @param v + * target vector + * @param f + * interpolation factor (should be in the range 0..1) + * @return result as new vector + */ + public final Vec2D interpolateTo(Vec2D v, float f) { + return new Vec2D(x + (v.x - x) * f, y + (v.y - y) * f); + } + + /** + * Interpolates the vector towards the given target vector, using the given + * {@link InterpolateStrategy} + * + * @param v + * target vector + * @param f + * interpolation factor (should be in the range 0..1) + * @param s + * InterpolateStrategy instance + * @return result as new vector + */ + public Vec2D interpolateTo(Vec2D v, float f, InterpolateStrategy s) { + return new Vec2D(s.interpolate(x, v.x, f), s.interpolate(y, v.y, f)); + } + + /** + * Interpolates the vector towards the given target vector, using linear + * interpolation + * + * @param v + * target vector + * @param f + * interpolation factor (should be in the range 0..1) + * @return itself, result overrides current vector + */ + public final Vec2D interpolateToSelf(Vec2D v, float f) { + x += (v.x - x) * f; + y += (v.y - y) * f; + return this; + } + + /** + * Interpolates the vector towards the given target vector, using the given + * {@link InterpolateStrategy} + * + * @param v + * target vector + * @param f + * interpolation factor (should be in the range 0..1) + * @param s + * InterpolateStrategy instance + * @return itself, result overrides current vector + */ + public Vec2D interpolateToSelf(Vec2D v, float f, InterpolateStrategy s) { + x = s.interpolate(x, v.x, f); + y = s.interpolate(y, v.y, f); + return this; + } + + /** + * Calculates the distance of the vector to the given sphere in the + * specified direction. A sphere is defined by a 3D point and a radius. + * Normalized directional vectors expected. + * + * @param rayDir + * intersection direction + * @param circleOrigin + * @param circleRadius + * @return distance to sphere in world units, -1 if no intersection. + */ + + public float intersectRayCircle(Vec2D rayDir, Vec2D circleOrigin, + float circleRadius) { + Vec2D q = circleOrigin.sub(this); + float distSquared = q.magSquared(); + float v = q.dot(rayDir); + float d = circleRadius * circleRadius - (distSquared - v * v); + + // If there was no intersection, return -1 + if (d < 0.0) + return -1; + + // Return the distance to the [first] intersecting point + return v - (float) Math.sqrt(d); + } + + /** + * Scales vector uniformly by factor -1 ( v = -v ), overrides coordinates + * with result + * + * @return itself + */ + public final Vec2D invert() { + x *= -1; + y *= -1; + return this; + } + + /** + * Checks if the point is inside the given sphere. + * + * @param sO + * circle origin/centre + * @param sR + * circle radius + * @return true, if point is in sphere + */ + + public boolean isInCircle(Vec2D sO, float sR) { + float d = sub(sO).magSquared(); + return (d <= sR * sR); + } + + /** + * Checks if the point is inside the given rectangle. + * + * @param r + * bounding rectangle + * @return true, if point is inside + */ + public boolean isInRecangle(Rectangle r) { + if (x < r.x || x > r.x + r.width) + return false; + if (y < r.y || y > r.y + r.height) + return false; + return true; + } + + /** + * Checks if point vector is inside the triangle created by the points a, b + * and c. These points will create a plane and the point checked will have + * to be on this plane in the region between a,b,c. + * + * Note: The triangle must be defined in clockwise order a,b,c + * + * @return true, if point is in triangle. + */ + + public boolean isInTriangle(Vec2D a, Vec2D b, Vec2D c) { + Vec2D v1 = sub(a).normalize(); + Vec2D v2 = sub(b).normalize(); + Vec2D v3 = sub(c).normalize(); + + double total_angles = Math.acos(v1.dot(v2)); + total_angles += Math.acos(v2.dot(v3)); + total_angles += Math.acos(v3.dot(v1)); + + return (MathUtils.abs((float) total_angles - MathUtils.TWO_PI) <= 0.005f); + } + + /** + * Checks if vector has a magnitude of 0 + * + * @return true, if vector = {0,0,0} + */ + public final boolean isZeroVector() { + return x == 0 && y == 0; + // return magnitude() lim * lim) { + return normalize().scaleSelf(lim); + } + return this; + } + + /** + * Calculates the magnitude/eucledian length of the vector + * + * @return vector length + */ + public final float magnitude() { + return (float) Math.sqrt(x * x + y * y); + } + + /** + * Calculates only the squared magnitude/length of the vector. Useful for + * inverse square law applications and/or for speed reasons or if the real + * eucledian distance is not required (e.g. sorting). + * + * @return squared magnitude (x^2 + y^2) + */ + public final float magSquared() { + return x * x + y * y; + } + + /** + * Constructs a new vector consisting of the largest components of both + * vectors. + * + * @param v + * @return result as new vector + */ + public final Vec2D max(Vec2D v) { + return new Vec2D(MathUtils.max(x, v.x), MathUtils.max(y, v.y)); + } + + /** + * Adjusts the vector components to the maximum values of both vectors + * + * @param v + * @return itself + */ + public final Vec2D maxSelf(Vec2D v) { + x = MathUtils.max(x, v.x); + y = MathUtils.max(y, v.y); + return this; + } + + /** + * Constructs a new vector consisting of the smallest components of both + * vectors. + * + * @param v + * comparing vector + * @return result as new vector + */ + public final Vec2D min(Vec2D v) { + return new Vec2D(MathUtils.min(x, v.x), MathUtils.min(y, v.y)); + } + + /** + * Adjusts the vector components to the minimum values of both vectors + * + * @param v + * @return itself + */ + public final Vec2D minSelf(Vec2D v) { + x = MathUtils.min(x, v.x); + y = MathUtils.min(y, v.y); + return this; + } + + /** + * Normalizes the vector so that its magnitude = 1 + * + * @return itself + */ + public final Vec2D normalize() { + float mag = (float) Math.sqrt(x * x + y * y); + if (mag > 0) { + x /= mag; + y /= mag; + } + return this; + } + + public final Vec2D perpendicular() { + float t = x; + x = -y; + y = t; + return this; + } + + /** + * Rotates the vector by the given angle around the Z axis. + * + * @param theta + * @return itself + */ + public final Vec2D rotate(float theta) { + float co = (float) Math.cos(theta); + float si = (float) Math.sin(theta); + float xx = co * x - si * y; + y = si * x + co * y; + x = xx; + return this; + } + + /** + * Scales vector uniformly and returns result as new vector. + * + * @param s + * scale factor + * @return new vector + */ + public final Vec2D scale(float s) { + return new Vec2D(x * s, y * s); + } + + /** + * Scales vector non-uniformly and returns result as new vector. + * + * @param a + * scale factor for X coordinate + * @param b + * scale factor for Y coordinate + * @return new vector + */ + public final Vec2D scale(float a, float b) { + return new Vec2D(x * a, y * b); + } + + /** + * Scales vector non-uniformly by vector v and returns result as new vector + * + * @param s + * scale vector + * @return new vector + */ + public final Vec2D scale(Vec2D s) { + return new Vec2D(x * s.x, y * s.y); + } + + /** + * Scales vector uniformly and overrides coordinates with result + * + * @param s + * scale factor + * @return itself + */ + public final Vec2D scaleSelf(float s) { + x *= s; + y *= s; + return this; + } + + /** + * Scales vector non-uniformly by vector {a,b,c} and overrides coordinates + * with result + * + * @param a + * scale factor for X coordinate + * @param b + * scale factor for Y coordinate + * @return itself + */ + public final Vec2D scaleSelf(float a, float b) { + x *= a; + y *= b; + return this; + } + + /** + * Scales vector non-uniformly by vector v and overrides coordinates with + * result + * + * @param s + * scale vector + * @return itself + */ + + public final Vec2D scaleSelf(Vec2D s) { + x *= s.x; + y *= s.y; + return this; + } + + /** + * Overrides coordinates with the given values + * + * @param x + * @param y + * @return itself + */ + public final Vec2D set(float x, float y) { + this.x = x; + this.y = y; + return this; + } + + /** + * Overrides coordinates with the ones of the given vector + * + * @param v + * vector to be copied + * @return itself + */ + public final Vec2D set(Vec2D v) { + x = v.x; + y = v.y; + return this; + } + + /** + * Replaces all vector components with the signum of their original values. + * In other words if a components value was negative its new value will be + * -1, if zero => 0, if positive => +1 + * + * @return itself + */ + public Vec2D signum() { + x = (x < 0 ? -1 : x == 0 ? 0 : 1); + y = (y < 0 ? -1 : y == 0 ? 0 : 1); + return this; + } + + /** + * Subtracts vector {a,b,c} and returns result as new vector. + * + * @param a + * X coordinate + * @param b + * Y coordinate + * @return result as new vector + */ + public final Vec2D sub(float a, float b) { + return new Vec2D(x - a, y - b); + } + + /** + * Subtracts vector v and returns result as new vector. + * + * @param v + * vector to be subtracted + * @return result as new vector + */ + public final Vec2D sub(Vec2D v) { + return new Vec2D(x - v.x, y - v.y); + } + + /** + * Subtracts vector {a,b,c} and overrides coordinates with result. + * + * @param a + * X coordinate + * @param b + * Y coordinate + * @return itself + */ + public final Vec2D subSelf(float a, float b) { + x -= a; + y -= b; + return this; + } + + /** + * Subtracts vector v and overrides coordinates with result. + * + * @param v + * vector to be subtracted + * @return itself + */ + public final Vec2D subSelf(Vec2D v) { + x -= v.x; + y -= v.y; + return this; + } + + /** + * Calculates the normal vector on the given ellipse in the direction of the + * current point. + * + * @param eO + * ellipse origin/centre + * @param eR + * ellipse radii + * @return a unit normal vector to the tangent plane of the ellipsoid in the + * point. + */ + + public Vec2D tangentNormalOfEllipse(Vec2D eO, Vec2D eR) { + Vec2D p = this.sub(eO); + + float xr2 = eR.x * eR.x; + float yr2 = eR.y * eR.y; + + return new Vec2D(p.x / xr2, p.y / yr2).normalize(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuffer sb = new StringBuffer(32); + sb.append("{x:").append(x).append(", y:").append(y).append("}"); + return sb.toString(); + } + + public float[] toArray() { + return new float[] { x, y }; + } +} \ No newline at end of file diff --git a/java/Mekstension/tools/toxi/geom/Vec3D.java b/java/Mekstension/tools/toxi/geom/Vec3D.java new file mode 100644 index 0000000..46978f1 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/Vec3D.java @@ -0,0 +1,1322 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.geom; + +import java.util.Random; + +import toxi.math.InterpolateStrategy; +import toxi.math.MathUtils; + +/** + * Comprehensive 3D vector class with additional basic intersection and + * collision detection features. + * + * @author Karsten Schmidt + * + */ +@SuppressWarnings("unchecked") +public class Vec3D implements Comparable { + + /** + * Defines positive X axis + */ + public static final Vec3D X_AXIS = new Vec3D(1, 0, 0); + + /** + * Defines positive Y axis + */ + public static final Vec3D Y_AXIS = new Vec3D(0, 1, 0); + + /** + * Defines positive Z axis + */ + public static final Vec3D Z_AXIS = new Vec3D(0, 0, 1); + + /** + * Creates a new vector from the given angle in the XY plane. The Z + * component of the vector will be zero. + * + * The resulting vector for theta=0 is equal to the positive X axis. + * + * @param theta + * @return new vector in the XY plane + */ + public static final Vec3D fromXYTheta(float theta) { + return new Vec3D((float) Math.cos(theta), (float) Math.sin(theta), 0); + } + + /** + * Creates a new vector from the given angle in the XZ plane. The Y + * component of the vector will be zero. + * + * The resulting vector for theta=0 is equal to the positive X axis. + * + * @param theta + * @return new vector in the XZ plane + */ + public static final Vec3D fromXZTheta(float theta) { + return new Vec3D((float) Math.cos(theta), 0, (float) Math.sin(theta)); + } + + /** + * Creates a new vector from the given angle in the YZ plane. The X + * component of the vector will be zero. + * + * The resulting vector for theta=0 is equal to the positive Y axis. + * + * @param theta + * @return new vector in the YZ plane + */ + public static final Vec3D fromYZTheta(float theta) { + return new Vec3D(0, (float) Math.cos(theta), (float) Math.sin(theta)); + } + + /** + * Constructs a new vector consisting of the largest components of both + * vectors. + * + * @param b + * @return result as new vector + */ + public static final Vec3D max(Vec3D a, Vec3D b) { + return new Vec3D(MathUtils.max(a.x, b.x), MathUtils.max(a.y, b.y), + MathUtils.max(a.z, b.z)); + } + + /** + * Constructs a new vector consisting of the smallest components of both + * vectors. + * + * @param b + * comparing vector + * @return result as new vector + */ + public static final Vec3D min(Vec3D a, Vec3D b) { + return new Vec3D(MathUtils.min(a.x, b.x), MathUtils.min(a.y, b.y), + MathUtils.min(a.z, b.z)); + } + + /** + * Static factory method. Creates a new random unit vector using the default + * Math.random() Random instance. + * + * @return a new random normalized unit vector. + */ + public static final Vec3D randomVector() { + Vec3D rnd = new Vec3D(MathUtils.normalizedRandom(), MathUtils + .normalizedRandom(), MathUtils.normalizedRandom()); + return rnd.normalize(); + } + + /** + * Static factory method. Creates a new random unit vector using the given + * Random generator instance. I recommend to have a look at the + * https://uncommons-maths.dev.java.net library for a good choice of + * reliable and high quality random number generators. + * + * @return a new random normalized unit vector. + */ + public static final Vec3D randomVector(Random rnd) { + Vec3D v = new Vec3D(MathUtils.normalizedRandom(rnd), MathUtils + .normalizedRandom(rnd), MathUtils.normalizedRandom(rnd)); + return v.normalize(); + } + + /** + * X coordinate + */ + public float x; + + /** + * Y coordinate + */ + public float y; + + /** + * Z coordinate + */ + public float z; + + /** + * Creates a new zero vector + */ + public Vec3D() { + x = y = z = 0; + } + + /** + * Creates a new vector with the given coordinates + * + * @param x + * @param y + * @param z + */ + public Vec3D(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Creates a new vector with the coordinates of the given vector + * + * @param v + * vector to be copied + */ + public Vec3D(Vec3D v) { + set(v); + } + + public final Vec3D abs() { + x = MathUtils.abs(x); + y = MathUtils.abs(y); + z = MathUtils.abs(z); + return this; + } + + /** + * Adds vector {a,b,c} and returns result as new vector. + * + * @param a + * X coordinate + * @param b + * Y coordinate + * @param c + * Z coordinate + * @return result as new vector + */ + public final Vec3D add(float a, float b, float c) { + return new Vec3D(x + a, y + b, z + c); + } + + /** + * Add vector v and returns result as new vector. + * + * @param v + * vector to add + * @return result as new vector + */ + public final Vec3D add(Vec3D v) { + return new Vec3D(x + v.x, y + v.y, z + v.z); + } + + /** + * Adds vector {a,b,c} and overrides coordinates with result. + * + * @param a + * X coordinate + * @param b + * Y coordinate + * @param c + * Z coordinate + * @return itself + */ + public final Vec3D addSelf(float a, float b, float c) { + x += a; + y += b; + z += c; + return this; + } + + /** + * Adds vector v and overrides coordinates with result. + * + * @param v + * vector to add + * @return itself + */ + public final Vec3D addSelf(Vec3D v) { + x += v.x; + y += v.y; + z += v.z; + return this; + } + + /** + * Computes the angle between this vector and vector V. This function + * assumes both vectors are normalized, if this can't be guaranteed, use the + * alternative implementation {@link #angleBetween(Vec3D, boolean)} + * + * @param v + * vector + * @return angle in radians, or NaN if vectors are parallel + */ + public final float angleBetween(Vec3D v) { + return (float) Math.acos(dot(v)); + } + + /** + * Computes the angle between this vector and vector V + * + * @param v + * vector + * @param forceNormalize + * true, if normalized versions of the vectors are to be used + * (Note: only copies will be used, original vectors will not be + * altered by this method) + * @return angle in radians, or NaN if vectors are parallel + */ + public final float angleBetween(Vec3D v, boolean forceNormalize) { + float theta; + if (forceNormalize) { + theta = getNormalized().dot(v.getNormalized()); + } else { + theta = dot(v); + } + return (float) Math.acos(theta); + } + + /** + * Sets all vector components to 0. + * + * @return itself + */ + public final Vec3D clear() { + x = y = z = 0; + return this; + } + + /** + * + * Helper function for {@link toxi.geom.Triangle#closedPoint(Vec3D)} + * + * @param a + * start point of line segment + * @param b + * end point of line segment + * @return closest point on the line segment a -> b + */ + + public Vec3D closestPointOnLine(Vec3D a, Vec3D b) { + Vec3D c = sub(a); + Vec3D v = b.sub(a); + + float d = v.magnitude(); + v.normalize(); + + float t = v.dot(c); + + // Check to see if t is beyond the extents of the line segment + if (t < 0.0f) + return a; + if (t > d) + return b; + + // Return the point between 'a' and 'b' + // set length of V to t. V is normalized so this is easy + v.scaleSelf(t); + + return a.add(v); + } + + /** + * Compares the length of the vector with another one. + * + * @param vec + * vector to compare with + * @return -1 if other vector is longer, 0 if both are equal or else +1 + */ + public int compareTo(Object vec) { + Vec3D v = (Vec3D) vec; + if (Float.compare(x, v.x) == 0 && Float.compare(y, v.y) == 0 + && Float.compare(z, v.z) == 0) + return 0; + if (magSquared() < v.magSquared()) + return -1; + return 1; + } + + @Override + public boolean equals(Object obj) { + Vec3D v = (Vec3D) obj; + return (Float.compare(x, v.x) == 0 && Float.compare(y, v.y) == 0 && Float + .compare(z, v.z) == 0); + } + + /** + * Forcefully fits the vector in the given AABB. + * + * @param box + * @return itself + */ + public final Vec3D constrain(AABB box) { + x = MathUtils.clip(x, box.minX(), box.maxX()); + y = MathUtils.clip(y, box.minY(), box.maxY()); + z = MathUtils.clip(z, box.minZ(), box.maxZ()); + return this; + } + + /** + * @return a new independent instance/copy of a given vector + */ + public final Vec3D copy() { + return new Vec3D(this); + } + + /** + * Calculates cross-product with vector v. The resulting vector is + * perpendicular to both the current and supplied vector. + * + * @param v + * vector to cross + * @return cross-product as new vector + */ + public final Vec3D cross(Vec3D v) { + return new Vec3D(y * v.z - v.y * z, z * v.x - v.z * x, x * v.y - v.x + * y); + } + + /** + * Calculates cross-product with vector v. The resulting vector is + * perpendicular to both the current and supplied vector and stored in the + * supplied result vector. + * + * @param v + * vector to cross + * @param result + * result vector + * @return result vector + */ + public final Vec3D crossInto(Vec3D v, Vec3D result) { + float rx = y * v.z - v.y * z; + float ry = z * v.x - v.z * x; + float rz = x * v.y - v.x * y; + result.set(rx, ry, rz); + return result; + } + + /** + * Calculates cross-product with vector v. The resulting vector is + * perpendicular to both the current and supplied vector and overrides the + * current. + * + * @param v + * @return itself + */ + public final Vec3D crossSelf(Vec3D v) { + float cx = y * v.z - v.y * z; + float cy = z * v.x - v.z * x; + z = x * v.y - v.x * y; + y = cy; + x = cx; + return this; + } + + /** + * Calculates distance to another vector + * + * @param v + * non-null vector + * @return distance or Float.NaN if v=null + */ + public final float distanceTo(Vec3D v) { + if (v != null) { + float dx = x - v.x; + float dy = y - v.y; + float dz = z - v.z; + return (float) Math.sqrt(dx * dx + dy * dy + dz * dz); + } else { + return Float.NaN; + } + } + + /** + * Calculates the squared distance to another vector + * + * @see #magSquared() + * @param v + * non-null vector + * @return distance or NaN if v=null + */ + public final float distanceToSquared(Vec3D v) { + if (v != null) { + float dx = x - v.x; + float dy = y - v.y; + float dz = z - v.z; + return dx * dx + dy * dy + dz * dz; + } else { + return Float.NaN; + } + } + + /** + * Computes the scalar product (dot product) with the given vector. + * + * @see Wikipedia entry< + * /a> + * + * @param v + * @return dot product + */ + public final float dot(Vec3D v) { + return x * v.x + y * v.y + z * v.z; + } + + /** + * Replaces the vector components with integer values of their current + * values + * + * @return itself + */ + public final Vec3D floor() { + x = MathUtils.floor(x); + y = MathUtils.floor(y); + z = MathUtils.floor(z); + return this; + } + + /** + * Replaces the vector components with the fractional part of their current + * values + * + * @return itself + */ + public final Vec3D frac() { + x -= MathUtils.floor(x); + y -= MathUtils.floor(y); + z -= MathUtils.floor(z); + return this; + } + + public final Vec3D getAbs() { + return new Vec3D(this).abs(); + } + + /** + * Creates a copy of the vector which forcefully fits in the given AABB. + * + * @param box + * @return fitted vector + */ + public final Vec3D getConstrained(AABB box) { + return new Vec3D(this).constrain(box); + } + + /** + * Creates a new vector whose components are the integer value of their + * current values + * + * @return result as new vector + */ + public final Vec3D getFloored() { + return new Vec3D(this).floor(); + } + + /** + * Creates a new vector whose components are the fractional part of their + * current values + * + * @return result as new vector + */ + public final Vec3D getFrac() { + return new Vec3D(this).frac(); + } + + /** + * Scales vector uniformly by factor -1 ( v = -v ) + * + * @return result as new vector + */ + public final Vec3D getInverted() { + return new Vec3D(-x, -y, -z); + } + + /** + * Creates a copy of the vector with its magnitude limited to the length + * given + * + * @param lim + * new maximum magnitude + * @return result as new vector + */ + public final Vec3D getLimited(float lim) { + if (magSquared() > lim * lim) { + return getNormalized().scaleSelf(lim); + } + return new Vec3D(this); + } + + /** + * Produces the normalized version as a new vector + * + * @return new vector + */ + public Vec3D getNormalized() { + return new Vec3D(this).normalize(); + } + + /** + * @see #rotateAroundAxis(Vec3D, float) + * @return new result vector + */ + public final Vec3D getRotatedAroundAxis(Vec3D axis, float theta) { + return new Vec3D(this).rotateAroundAxis(axis, theta); + } + + /** + * Creates a new vector rotated by the given angle around the X axis. + * + * @param theta + * @return rotated vector + */ + public final Vec3D getRotatedX(float theta) { + return new Vec3D(this).rotateX(theta); + } + + /** + * Creates a new vector rotated by the given angle around the Y axis. + * + * @param theta + * @return rotated vector + */ + public final Vec3D getRotatedY(float theta) { + return new Vec3D(this).rotateY(theta); + } + + /** + * Creates a new vector rotated by the given angle around the Z axis. + * + * @param theta + * @return rotated vector + */ + public final Vec3D getRotatedZ(float theta) { + return new Vec3D(this).rotateZ(theta); + } + + /** + * Creates a new vector in which all components are replaced with the signum + * of their original values. In other words if a components value was + * negative its new value will be -1, if zero => 0, if positive => +1 + * + * @return result vector + */ + public Vec3D getSignum() { + return new Vec3D(this).signum(); + } + + /** + * Computes the vector's direction in the XY plane (for example for 2D + * points). The positive X axis equals 0 degrees. + * + * @return rotation angle + */ + public final float headingXY() { + return (float) Math.atan2(y, x); + } + + /** + * Computes the vector's direction in the XZ plane. The positive X axis + * equals 0 degrees. + * + * @return rotation angle + */ + public final float headingXZ() { + return (float) Math.atan2(z, x); + } + + /** + * Computes the vector's direction in the YZ plane. The positive Z axis + * equals 0 degrees. + * + * @return rotation angle + */ + public final float headingYZ() { + return (float) Math.atan2(y, z); + } + + /** + * Interpolates the vector towards the given target vector, using linear + * interpolation + * + * @param v + * target vector + * @param f + * interpolation factor (should be in the range 0..1) + * @return result as new vector + */ + public final Vec3D interpolateTo(Vec3D v, float f) { + return new Vec3D(x + (v.x - x) * f, y + (v.y - y) * f, z + (v.z - z) + * f); + } + + /** + * Interpolates the vector towards the given target vector, using the given + * {@link InterpolateStrategy} + * + * @param v + * target vector + * @param f + * interpolation factor (should be in the range 0..1) + * @param s + * InterpolateStrategy instance + * @return result as new vector + */ + public Vec3D interpolateTo(Vec3D v, float f, InterpolateStrategy s) { + return new Vec3D(s.interpolate(x, v.x, f), s.interpolate(y, v.y, f), s + .interpolate(z, v.z, f)); + } + + /** + * Interpolates the vector towards the given target vector, using linear + * interpolation + * + * @param v + * target vector + * @param f + * interpolation factor (should be in the range 0..1) + * @return itself, result overrides current vector + */ + public final Vec3D interpolateToSelf(Vec3D v, float f) { + x += (v.x - x) * f; + y += (v.y - y) * f; + z += (v.z - z) * f; + return this; + } + + /** + * Interpolates the vector towards the given target vector, using the given + * {@link InterpolateStrategy} + * + * @param v + * target vector + * @param f + * interpolation factor (should be in the range 0..1) + * @param s + * InterpolateStrategy instance + * @return itself, result overrides current vector + */ + public Vec3D interpolateToSelf(Vec3D v, float f, InterpolateStrategy s) { + x = s.interpolate(x, v.x, f); + y = s.interpolate(y, v.y, f); + z = s.interpolate(z, v.z, f); + return this; + } + + /** + * Calculates the distance of the vector to the given plane in the specified + * direction. A plane is specified by a 3D point and a normal vector + * perpendicular to the plane. Normalized directional vectors expected (for + * rayDir and planeNormal). + * + * @param rayDir + * intersection direction + * @param planeOrigin + * @param planeNormal + * @return distance to plane in world units, -1 if no intersection. + * @deprecated + */ + // FIXME this is kind of obsolete since the arrival of the Plane class, but + // needs amends to reflector code + public float intersectRayPlane(Vec3D rayDir, Vec3D planeOrigin, + Vec3D planeNormal) { + float d = -planeNormal.dot(planeOrigin); + float numer = planeNormal.dot(this) + d; + float denom = planeNormal.dot(rayDir); + + // normal is orthogonal to vector, cant intersect + if (MathUtils.abs(denom) < MathUtils.EPS) + return -1; + + return -(numer / denom); + } + + /** + * Calculates the distance of the vector to the given sphere in the + * specified direction. A sphere is defined by a 3D point and a radius. + * Normalized directional vectors expected. + * + * @param rayDir + * intersection direction + * @param sphereOrigin + * @param sphereRadius + * @return distance to sphere in world units, -1 if no intersection. + */ + + // FIXME this really should be part of either Sphere or + // SphereIntersectorReflector + public float intersectRaySphere(Vec3D rayDir, Vec3D sphereOrigin, + float sphereRadius) { + Vec3D q = sphereOrigin.sub(this); + float distSquared = q.magSquared(); + float v = q.dot(rayDir); + float d = sphereRadius * sphereRadius - (distSquared - v * v); + + // If there was no intersection, return -1 + if (d < 0.0) { + return -1; + } + + // Return the distance to the [first] intersecting point + return v - (float) Math.sqrt(d); + } + + /** + * Considers the current vector as centre of a collision sphere with radius + * r and checks if the triangle abc intersects with this sphere. The Vec3D p + * The point on abc closest to the sphere center is returned via the + * supplied result vector argument. + * + * @param r + * collision sphere radius + * @param a + * triangle vertex + * @param b + * triangle vertex + * @param c + * triangle vertex + * @param result + * a non-null vector for storing the result + * @return true, if sphere intersects triangle ABC + */ + // FIXME this needs to be moved out from Vec3D in either + public boolean intersectSphereTriangle(float r, Vec3D a, Vec3D b, Vec3D c, + Vec3D result) { + // Find Vec3D P on triangle ABC closest to sphere center + result.set(new Triangle(a, b, c).closestPointOnSurface(this)); + + // Sphere and triangle intersect if the (squared) distance from sphere + // center to Vec3D p is less than the (squared) sphere radius + Vec3D v = result.sub(this); + return v.magSquared() <= r * r; + } + + /** + * Scales vector uniformly by factor -1 ( v = -v ), overrides coordinates + * with result + * + * @return itself + */ + public final Vec3D invert() { + x *= -1; + y *= -1; + z *= -1; + return this; + } + + /** + * Checks if the point is inside the given AABB. + * + * @param box + * bounding box to check + * @return true, if point is inside + */ + public boolean isInAABB(AABB box) { + Vec3D min = box.getMin(); + Vec3D max = box.getMax(); + if (x < min.x || x > max.x) + return false; + if (y < min.y || y > max.y) + return false; + if (z < min.z || z > max.z) + return false; + return true; + } + + /** + * Checks if the point is inside the given axis-aligned bounding box. + * + * @param bO + * bounding box origin/center + * @param bDim + * bounding box extends (half measure) + * @return true, if point is inside the box + */ + + public boolean isInAABB(Vec3D bO, Vec3D bDim) { + float w = bDim.x; + if (x < bO.x - w || x > bO.x + w) + return false; + w = bDim.y; + if (y < bO.y - w || y > bO.y + w) + return false; + w = bDim.z; + if (z < bO.z - w || z > bO.z + w) + return false; + return true; + } + + /** + * Checks if the point is inside the given sphere. + * + * @param s + * bounding sphere to check + * @return true, if point is inside + */ + // FIXME move to Sphere + public boolean isInSphere(Sphere s) { + float d = this.sub(s).magSquared(); + return (d <= s.radius * s.radius); + } + + /** + * Checks if the point is inside the given sphere. + * + * @param sO + * sphere origin/centre + * @param sR + * sphere radius + * @return true, if point is in sphere + */ + // FIXME move to Sphere + public boolean isInSphere(Vec3D sO, float sR) { + float d = this.sub(sO).magSquared(); + return (d <= sR * sR); + } + + /** + * Checks if vector has a magnitude of 0 + * + * @return true, if vector = {0,0,0} + */ + public final boolean isZeroVector() { + return x == 0 && y == 0 && z == 0; + // return magnitude() lim * lim) { + return normalize().scaleSelf(lim); + } + return this; + } + + /** + * Calculates the magnitude/eucledian length of the vector + * + * @return vector length + */ + public float magnitude() { + return (float) Math.sqrt(x * x + y * y + z * z); + } + + /** + * Calculates only the squared magnitude/length of the vector. Useful for + * inverse square law applications and/or for speed reasons or if the real + * eucledian distance is not required (e.g. sorting). + * + * @return squared magnitude (x^2 + y^2 + z^2) + */ + public float magSquared() { + return x * x + y * y + z * z; + } + + public final Vec3D maxSelf(Vec3D b) { + x = MathUtils.max(x, b.x); + y = MathUtils.max(y, b.y); + z = MathUtils.max(z, b.z); + return this; + } + + public final Vec3D minSelf(Vec3D b) { + x = MathUtils.min(x, b.x); + y = MathUtils.min(y, b.y); + z = MathUtils.min(z, b.z); + return this; + } + + /** + * Applies a uniform modulo operation to the vector, using the same base for + * all components. + * + * @param base + * @return itself + */ + public final Vec3D modSelf(float base) { + x %= base; + y %= base; + z %= base; + return this; + } + + /** + * Calculates modulo operation for each vector component separately. + * + * @param bx + * @param by + * @param bz + * @return itself + */ + + public final Vec3D modSelf(float bx, float by, float bz) { + x %= bx; + y %= by; + z %= bz; + return this; + } + + /** + * Normalizes the vector so that its magnitude = 1 + * + * @return itself + */ + public Vec3D normalize() { + float mag = (float) Math.sqrt(x * x + y * y + z * z); + if (mag > 0) { + mag = 1f / mag; + x *= mag; + y *= mag; + z *= mag; + } + return this; + } + + /** + * Rotates the vector around the giving axis + * + * @param axis + * rotation axis vector + * @param theta + * rotation angle (in radians) + * @return itself + */ + public final Vec3D rotateAroundAxis(Vec3D axis, float theta) { + float ux = axis.x * x; + float uy = axis.x * y; + float uz = axis.x * z; + float vx = axis.y * x; + float vy = axis.y * y; + float vz = axis.y * z; + float wx = axis.z * x; + float wy = axis.z * y; + float wz = axis.z * z; + double si = Math.sin(theta); + double co = Math.cos(theta); + float xx = (float) (axis.x + * (ux + vy + wz) + + (x * (axis.y * axis.y + axis.z * axis.z) - axis.x * (vy + wz)) + * co + (-wy + vz) * si); + float yy = (float) (axis.y + * (ux + vy + wz) + + (y * (axis.x * axis.x + axis.z * axis.z) - axis.y * (ux + wz)) + * co + (wx - uz) * si); + float zz = (float) (axis.z + * (ux + vy + wz) + + (z * (axis.x * axis.x + axis.y * axis.y) - axis.z * (ux + vy)) + * co + (-vx + uy) * si); + x = xx; + y = yy; + z = zz; + return this; + } + + /** + * Rotates the vector by the given angle around the X axis. + * + * @param theta + * @return itself + */ + public final Vec3D rotateX(float theta) { + float co = (float) Math.cos(theta); + float si = (float) Math.sin(theta); + float yy = co * z - si * y; + z = si * z + co * y; + y = yy; + return this; + } + + /** + * Rotates the vector by the given angle around the Y axis. + * + * @param theta + * @return itself + */ + public final Vec3D rotateY(float theta) { + float co = (float) Math.cos(theta); + float si = (float) Math.sin(theta); + float xx = co * x - si * z; + z = si * x + co * z; + x = xx; + return this; + } + + /** + * Rotates the vector by the given angle around the Z axis. + * + * @param theta + * @return itself + */ + public final Vec3D rotateZ(float theta) { + float co = (float) Math.cos(theta); + float si = (float) Math.sin(theta); + float xx = co * x - si * y; + y = si * x + co * y; + x = xx; + return this; + } + + /** + * Scales vector uniformly and returns result as new vector. + * + * @param s + * scale factor + * @return new vector + */ + public Vec3D scale(float s) { + return new Vec3D(x * s, y * s, z * s); + } + + /** + * Scales vector non-uniformly and returns result as new vector. + * + * @param a + * scale factor for X coordinate + * @param b + * scale factor for Y coordinate + * @param c + * scale factor for Z coordinate + * @return new vector + */ + public Vec3D scale(float a, float b, float c) { + return new Vec3D(x * a, y * b, z * c); + } + + /** + * Scales vector non-uniformly by vector v and returns result as new vector + * + * @param s + * scale vector + * @return new vector + */ + public Vec3D scale(Vec3D s) { + return new Vec3D(x * s.x, y * s.y, z * s.z); + } + + /** + * Scales vector uniformly and overrides coordinates with result + * + * @param s + * scale factor + * @return itself + */ + public Vec3D scaleSelf(float s) { + x *= s; + y *= s; + z *= s; + return this; + } + + /** + * Scales vector non-uniformly by vector {a,b,c} and overrides coordinates + * with result + * + * @param a + * scale factor for X coordinate + * @param b + * scale factor for Y coordinate + * @param c + * scale factor for Z coordinate + * @return itself + */ + public Vec3D scaleSelf(float a, float b, float c) { + x *= a; + y *= b; + z *= c; + return this; + } + + /** + * Scales vector non-uniformly by vector v and overrides coordinates with + * result + * + * @param s + * scale vector + * @return itself + */ + + public Vec3D scaleSelf(Vec3D s) { + x *= s.x; + y *= s.y; + z *= s.z; + return this; + } + + /** + * Overrides coordinates with the given values + * + * @param x + * @param y + * @param z + * @return itself + */ + public Vec3D set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + /** + * Overrides coordinates with the ones of the given vector + * + * @param v + * vector to be copied + * @return itself + */ + public Vec3D set(Vec3D v) { + x = v.x; + y = v.y; + z = v.z; + return this; + } + + /** + * Replaces all vector components with the signum of their original values. + * In other words if a components value was negative its new value will be + * -1, if zero => 0, if positive => +1 + * + * @return itself + */ + public Vec3D signum() { + x = (x < 0 ? -1 : x == 0 ? 0 : 1); + y = (y < 0 ? -1 : y == 0 ? 0 : 1); + z = (z < 0 ? -1 : z == 0 ? 0 : 1); + return this; + } + + /** + * Subtracts vector {a,b,c} and returns result as new vector. + * + * @param a + * X coordinate + * @param b + * Y coordinate + * @param c + * Z coordinate + * @return result as new vector + */ + public final Vec3D sub(float a, float b, float c) { + return new Vec3D(x - a, y - b, z - c); + } + + /** + * Subtracts vector v and returns result as new vector. + * + * @param v + * vector to be subtracted + * @return result as new vector + */ + public final Vec3D sub(Vec3D v) { + return new Vec3D(x - v.x, y - v.y, z - v.z); + } + + /** + * Subtracts vector {a,b,c} and overrides coordinates with result. + * + * @param a + * X coordinate + * @param b + * Y coordinate + * @param c + * Z coordinate + * @return itself + */ + public final Vec3D subSelf(float a, float b, float c) { + x -= a; + y -= b; + z -= c; + return this; + } + + /** + * Subtracts vector v and overrides coordinates with result. + * + * @param v + * vector to be subtracted + * @return itself + */ + public final Vec3D subSelf(Vec3D v) { + x -= v.x; + y -= v.y; + z -= v.z; + return this; + } + + /** + * Calculates the normal vector on the given ellipsoid in the direction of + * the current point. + * + * @param eO + * ellipsoid origin/centre + * @param eR + * ellipsoid radius + * @return a unit normal vector to the tangent plane of the ellipsoid in the + * point. + */ + + public Vec3D tangentPlaneNormalOfEllipsoid(Vec3D eO, Vec3D eR) { + Vec3D p = this.sub(eO); + + float xr2 = eR.x * eR.x; + float yr2 = eR.y * eR.y; + float zr2 = eR.z * eR.z; + + return new Vec3D(p.x / xr2, p.y / yr2, p.z / zr2).normalize(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuffer sb = new StringBuffer(48); + sb.append("{x:").append(x).append(", y:").append(y).append(", z:") + .append(z).append("}"); + return sb.toString(); + } + + public float[] toArray() { + return new float[] { x, y, z }; + } +} diff --git a/java/Mekstension/tools/toxi/geom/util/DefaultSTLColorModel.java b/java/Mekstension/tools/toxi/geom/util/DefaultSTLColorModel.java new file mode 100644 index 0000000..535a106 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/util/DefaultSTLColorModel.java @@ -0,0 +1,16 @@ +package toxi.geom.util; + +public class DefaultSTLColorModel implements STLColorModel { + + public void formatHeader(byte[] header) { + } + + public int formatRGB(int rgb) { + int col15bits = (rgb >> 3 & 0x1f); + col15bits |= (rgb >> 11 & 0x1f) << 5; + col15bits |= (rgb >> 19 & 0x1f) << 10; + col15bits |= 0x8000; + return col15bits; + } + +} diff --git a/java/Mekstension/tools/toxi/geom/util/MaterialiseSTLColorModel.java b/java/Mekstension/tools/toxi/geom/util/MaterialiseSTLColorModel.java new file mode 100644 index 0000000..9722ef8 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/util/MaterialiseSTLColorModel.java @@ -0,0 +1,29 @@ +package toxi.geom.util; + +public class MaterialiseSTLColorModel implements STLColorModel { + + int basecolor; + + public MaterialiseSTLColorModel(int rgb) { + basecolor = rgb; + } + + public void formatHeader(byte[] header) { + char[] col = new char[] { 'C', 'O', 'L', 'O', 'R', '=' }; + for (int i = 0; i < col.length; i++) { + header[i] = (byte) col[i]; + } + header[6] = (byte) (basecolor >> 16 & 0xff); + header[7] = (byte) (basecolor >> 8 & 0xff); + header[8] = (byte) (basecolor & 0xff); + header[9] = (byte) (basecolor >>> 24); + } + + public int formatRGB(int rgb) { + int col15bits = (rgb >> 19 & 0x1f); + col15bits |= (rgb >> 11 & 0x1f) << 5; + col15bits |= (rgb >> 3 & 0x1f) << 10; + col15bits |= 0x8000; + return col15bits; + } +} diff --git a/java/Mekstension/tools/toxi/geom/util/OBJWriter.java b/java/Mekstension/tools/toxi/geom/util/OBJWriter.java new file mode 100644 index 0000000..4ae7152 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/util/OBJWriter.java @@ -0,0 +1,61 @@ +package toxi.geom.util; + +import java.io.FileOutputStream; +import java.io.PrintWriter; + +import toxi.geom.Vec3D; + +/** + * Extremely bare bones Wavefront OBJ 3D format exporter. Purely handles the + * writing of data to the .obj file, but does not have any form of mesh + * management. + * + * Might get some more TLC in future versions. + * + * @author toxi + * + */ +public class OBJWriter { + + public final String VERSION = "0.25"; + + protected FileOutputStream objStream; + protected PrintWriter objWriter; + + public void beginSave(String fn) { + try { + objStream = new FileOutputStream(fn); + objWriter = new PrintWriter(objStream); + objWriter.println("# generated by OBJExport v" + VERSION); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void endSave() { + try { + objWriter.flush(); + objWriter.close(); + objStream.flush(); + objStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void newObject(String name) { + objWriter.println("o " + name); + } + + public void vertex(Vec3D v) { + objWriter.println("v " + v.x + " " + v.y + " " + v.z); + } + + public void face(int a, int b, int c) { + objWriter.println("f " + a + " " + b + " " + c); + } + + public void faceList() { + objWriter.println("s off"); + } +} diff --git a/java/Mekstension/tools/toxi/geom/util/STLColorModel.java b/java/Mekstension/tools/toxi/geom/util/STLColorModel.java new file mode 100644 index 0000000..f53f735 --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/util/STLColorModel.java @@ -0,0 +1,7 @@ +package toxi.geom.util; + +public interface STLColorModel { + void formatHeader(byte[] header); + + int formatRGB(int rgb); +} diff --git a/java/Mekstension/tools/toxi/geom/util/STLWriter.java b/java/Mekstension/tools/toxi/geom/util/STLWriter.java new file mode 100644 index 0000000..e479e3e --- /dev/null +++ b/java/Mekstension/tools/toxi/geom/util/STLWriter.java @@ -0,0 +1,144 @@ +package toxi.geom.util; + +import java.io.DataOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import toxi.geom.Vec3D; + +/** + * A simple, but flexible and memory efficient exporter for binary STL files. + * Custom color support is implemented via the STLcolorModel interface and the + * exporter comes with the 2 most common format variations defined by the + * DEFAULT and MATERIALISE constants. + * + * The minimal design of this exporter means it does not build an extra list of + * faces in RAM and so is able to easily export models with millions of faces. + * + * http://en.wikipedia.org/wiki/STL_(file_format) + * + * @author kschmidt + * + */ +public class STLWriter { + + public static final STLColorModel DEFAULT = new DefaultSTLColorModel(); + + public static final STLColorModel MATERIALISE = new MaterialiseSTLColorModel( + 0xffffffff); + + protected DataOutputStream ds; + + protected Vec3D scale = new Vec3D(1, 1, 1); + + protected boolean useInvertedNormals = false; + + protected byte[] buf = new byte[4]; + + protected STLColorModel colorModel; + + public STLWriter() { + this(DEFAULT); + } + + public STLWriter(STLColorModel cm) { + colorModel = cm; + } + + public void beginSave(String fn, int numFaces) { + try { + ds = new DataOutputStream(new FileOutputStream(fn)); + writeHeader(numFaces); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void endSave() { + try { + ds.flush(); + ds.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void face(Vec3D a, Vec3D b, Vec3D c) { + face(a, b, c, 0); + } + + public void face(Vec3D a, Vec3D b, Vec3D c, int rgb) { + try { + // normal + Vec3D normal = b.sub(a).cross(c.sub(a)).normalize(); + if (useInvertedNormals) { + normal.invert(); + } + writeVector(normal); + // vertices + writeVector(a); + writeVector(b); + writeVector(c); + // vertex attrib (color) + if (rgb != 0) { + writeShort(colorModel.formatRGB(rgb)); + } else { + writeShort(0); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private final void prepareBuffer(int a) { + buf[0] = (byte) (a >>> 24); + buf[1] = (byte) (a >> 16 & 0xff); + buf[2] = (byte) (a >> 8 & 0xff); + buf[3] = (byte) (a & 0xff); + } + + public void setScale(float s) { + scale.set(s, s, s); + } + + public void setScale(Vec3D s) { + scale.set(s); + } + + public void useInvertedNormals(boolean state) { + useInvertedNormals = state; + } + + protected void writeFloat(float a) throws IOException { + prepareBuffer(Float.floatToRawIntBits(a)); + ds.write(buf, 0, 4); + } + + protected void writeHeader(int num) throws IOException { + byte[] header = new byte[80]; + colorModel.formatHeader(header); + ds.write(header, 0, 80); + writeInt(num); + } + + protected void writeInt(int a) throws IOException { + prepareBuffer(a); + ds.write(buf, 0, 4); + } + + protected void writeShort(int a) throws IOException { + ds.writeByte(a & 0xff); + ds.writeByte(a >> 8 & 0xff); + } + + protected void writeVector(Vec3D v) { + try { + writeFloat(v.x * scale.x); + writeFloat(v.y * scale.y); + writeFloat(v.z * scale.z); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/java/Mekstension/tools/toxi/math/CircularInterpolation.java b/java/Mekstension/tools/toxi/math/CircularInterpolation.java new file mode 100644 index 0000000..29bbfa9 --- /dev/null +++ b/java/Mekstension/tools/toxi/math/CircularInterpolation.java @@ -0,0 +1,38 @@ +package toxi.math; + +/** + * Implementation of the circular interpolation function. + * + * i = a-(b-a) * (sqrt(1 - (1 - f) * (1 - f) )) + * + * @author kschmidt + * + */ +public class CircularInterpolation implements InterpolateStrategy { + + boolean isFlipped; + + public CircularInterpolation() { + this(false); + } + + /** + * The interpolation slope can be flipped to have its steepest ascent + * towards the end value, rather than at the beginning in the default + * configuration. + * + * @param isFlipped true, if slope is inverted + */ + public CircularInterpolation(boolean isFlipped) { + this.isFlipped = isFlipped; + } + + public float interpolate(float a, float b, float f) { + if (isFlipped) { + return a - (b - a) * ((float) Math.sqrt(1 - f * f) - 1); + } else { + f = 1 - f; + return a + (b - a) * ((float) Math.sqrt(1 - f * f)); + } + } +} diff --git a/java/Mekstension/tools/toxi/math/CosineInterpolation.java b/java/Mekstension/tools/toxi/math/CosineInterpolation.java new file mode 100644 index 0000000..dd78a1f --- /dev/null +++ b/java/Mekstension/tools/toxi/math/CosineInterpolation.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.math; + +/** + * Implementation of the cosine interpolation function: + * + * i = b+(a-b)*(0.5+0.5*cos(f*PI)) + * + * @author toxi + * + */ +public class CosineInterpolation implements InterpolateStrategy { + + /* (non-Javadoc) + * @see toxi.math.InterpolateStrategy#interpolate(float, float, float) + */ + public final float interpolate(float a, float b, float f) { + return b+(a-b)*(float)(0.5+0.5*Math.cos(f*MathUtils.PI)); + } + +} \ No newline at end of file diff --git a/java/Mekstension/tools/toxi/math/InterpolateStrategy.java b/java/Mekstension/tools/toxi/math/InterpolateStrategy.java new file mode 100644 index 0000000..1a0fc73 --- /dev/null +++ b/java/Mekstension/tools/toxi/math/InterpolateStrategy.java @@ -0,0 +1,23 @@ +package toxi.math; + +/** + * Defines a generic function to interpolate 2 float values. + * + * @author toxi + * + */ +public interface InterpolateStrategy { + + /** + * Implements an interpolation equation. + * + * @param a + * current value + * @param b + * target value + * @param f + * normalized interpolation factor (0.0 .. 1.0) + * @return interpolated value + */ + public float interpolate(float a, float b, float f); +} diff --git a/java/Mekstension/tools/toxi/math/LinearInterpolation.java b/java/Mekstension/tools/toxi/math/LinearInterpolation.java new file mode 100644 index 0000000..f32d867 --- /dev/null +++ b/java/Mekstension/tools/toxi/math/LinearInterpolation.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.math; + +/** + * Implementation of the linear interpolation function + * + * i = a+(b-a)*f + * + * @author toxi + * + */ +public class LinearInterpolation implements InterpolateStrategy { + + /* (non-Javadoc) + * @see toxi.math.InterpolateStrategy#interpolate(float, float, float) + */ + public final float interpolate(float a, float b, float f) { + return a+(b-a)*f; + } + +} diff --git a/java/Mekstension/tools/toxi/math/MathUtils.java b/java/Mekstension/tools/toxi/math/MathUtils.java new file mode 100644 index 0000000..aec6a06 --- /dev/null +++ b/java/Mekstension/tools/toxi/math/MathUtils.java @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.math; + +import java.util.Random; + +/** + * Miscellaneous math utilities. + */ +public class MathUtils { + + /** + * Log(2) + */ + public static final float LOG2 = (float) Math.log(2); + + /** + * PI + */ + public static final float PI = 3.14159265358979323846f; + + /** + * PI/2 + */ + public static final float HALF_PI = PI / 2; + + /** + * PI/3 + */ + public static final float THIRD_PI = PI / 3; + + /** + * PI/4 + */ + public static final float QUARTER_PI = PI / 4; + + /** + * PI*2 + */ + public static final float TWO_PI = PI * 2; + + /** + * Epsilon value, approx. 1.19E-7 + */ + public static final float EPS = 1.1920928955078125E-7f; + + /** + * Degrees to radians conversion factor + */ + public static final float DEG2RAD = PI / 180; + + /** + * Radians to degrees conversion factor + */ + public static final float RAD2DEG = 180 / PI; + + /** + * @param x + * @return absolute value of x + */ + public static final float abs(float x) { + return x < 0 ? -x : x; + } + + /** + * @param x + * @return absolute value of x + */ + public static final int abs(int x) { + int y = x >> 31; + return (x ^ y) - y; + } + + /** + * Rounds up the value to the nearest higher power^2 value. + * + * @param x + * @return power^2 value + */ + public static final int ceilPowerOf2(int x) { + return (int) Math.pow(2, (int) (Math.log(x) / LOG2 + 0.5)); + } + + public static final float clip(float a, float min, float max) { + return a < min ? min : (a > max ? max : a); + } + + public static final int clip(int a, int min, int max) { + return a < min ? min : (a > max ? max : a); + } + + /** + * Clips the value to the 0.0 .. 1.0 interval. + * + * @param a + * @return clipped value + * @since 0012 + */ + public static final float clipNormalized(float a) { + if (a < 0) { + return 0; + } else if (a > 1) { + return 1; + } + return a; + } + + public static final float degrees(float radians) { + return radians * RAD2DEG; + } + + /** + * @deprecated renamed into {@link #floor(float)} + */ + @Deprecated + public static final int fastFloor(float x) { + return floor(x); + } + + /** + * @deprecated + */ + @Deprecated + public static final float fastInverseSqrt(float x) { + float half = 0.5F * x; + int i = Float.floatToIntBits(x); + i = 0x5f375a86 - (i >> 1); + x = Float.intBitsToFloat(i); + return x * (1.5F - half * x * x); + } + + /** + * Computes a fast approximation to Math.pow(a, b). Adapted + * from http://www.dctsystems.co.uk/Software/power.html. + * + * @param a + * a positive number + * @param b + * a number + * @return a^b + * + * @deprecated + */ + @Deprecated + public static final float fastPow(float a, float b) { + float x = Float.floatToRawIntBits(a); + x *= 1.0f / (1 << 23); + x -= 127; + float y = x - (int) Math.floor(x); + b *= x + (y - y * y) * 0.346607f; + y = b - (int) Math.floor(b); + y = (y - y * y) * 0.33971f; + return Float.intBitsToFloat((int) ((b + 127 - y) * (1 << 23))); + } + + public static final boolean flipCoin() { + return Math.random() < 0.5; + } + + public static final boolean flipCoin(Random rnd) { + return rnd.nextBoolean(); + } + + public static final int floor(double x) { + return x >= 0 ? (int) x : (int) x - 1; + } + + /** + * This method is a *lot* faster than using (int)Math.floor(x). + * + * @param x + * value to be floored + * @return floored value as integer + * @since 0012 + */ + public static final int floor(float x) { + return x >= 0 ? (int) x : (int) x - 1; + } + + /** + * Rounds down the value to the nearest lower power^2 value. + * + * @param x + * @return power^2 value + */ + public static final int floorPowerOf2(int x) { + return (int) Math.pow(2, (int) (Math.log(x) / LOG2)); + } + + public static final float max(float a, float b) { + return a > b ? a : b; + } + + /** + * Returns the maximum value of three floats. + * + * @param a + * @param b + * @param c + * @return max val + */ + public static final float max(float a, float b, float c) { + return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); + } + + public static final int max(int a, int b) { + return a > b ? a : b; + } + + /** + * Returns the maximum value of three ints. + * + * @param a + * @param b + * @param c + * @return max val + */ + public static final int max(int a, int b, int c) { + return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); + } + + public static final float min(float a, float b) { + return a < b ? a : b; + } + + /** + * Returns the minimum value of three floats. + * + * @param a + * @param b + * @param c + * @return min val + */ + public static final float min(float a, float b, float c) { + return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c); + } + + public static final int min(int a, int b) { + return a < b ? a : b; + } + + /** + * Returns the minimum value of three ints. + * + * @param a + * @param b + * @param c + * @return min val + */ + public static final int min(int a, int b, int c) { + return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c); + } + + /** + * Returns a random number in the interval -1 .. +1. + * + * @return random float + */ + public static final float normalizedRandom() { + return (float) Math.random() * 2 - 1; + } + + /** + * Returns a random number in the interval -1 .. +1 using the {@link Random} + * instance provided. + * + * @return random float + */ + public static final float normalizedRandom(Random rnd) { + return rnd.nextFloat() * 2 - 1; + } + + public static final float radians(float degrees) { + return degrees * DEG2RAD; + } + + public static final float random(float max) { + return (float) Math.random() * max; + } + + public static final float random(float min, float max) { + return (float) Math.random() * (max - min) + min; + } + + public static final int random(int max) { + return (int) (Math.random() * max); + } + + public static final int random(int min, int max) { + return (int) (Math.random() * (max - min)) + min; + } + + public static final float random(Random rnd, float max) { + return rnd.nextFloat() * max; + } + + public static final float random(Random rnd, float min, float max) { + return rnd.nextFloat() * (max - min) + min; + } + + public static final int random(Random rnd, int max) { + return (int) (rnd.nextDouble() * max); + } + + public static final int random(Random rnd, int min, int max) { + return (int) (rnd.nextDouble() * (max - min)) + min; + } + + /** + * @deprecated + */ + @Deprecated + public static final float sqrt(float x) { + x = fastInverseSqrt(x); + if (x > 0) { + return 1.0f / x; + } else + return 0; + } +} diff --git a/java/Mekstension/tools/toxi/math/SigmoidInterpolation.java b/java/Mekstension/tools/toxi/math/SigmoidInterpolation.java new file mode 100644 index 0000000..7aac82e --- /dev/null +++ b/java/Mekstension/tools/toxi/math/SigmoidInterpolation.java @@ -0,0 +1,37 @@ +package toxi.math; + + +/** + * Implements the sigmoid interpolation function with adjustable curve sharpness + * + * @author kschmidt + */ +public class SigmoidInterpolation implements InterpolateStrategy { + + private float sharpness; + + private float sharpPremult; + + public SigmoidInterpolation() { + this(1f); + } + + public SigmoidInterpolation(float s) { + setSharpness(s); + } + + private void setSharpness(float s) { + sharpness = s; + sharpPremult = 5 * s; + } + + public float getSharpness() { + return sharpness; + } + + public float interpolate(float a, float b, float f) { + f = (f * 2 - 1) * sharpPremult; + f = (float) (1.0f / (1.0f + Math.exp(-f))); + return a + (b - a) * f; + } +} diff --git a/java/Mekstension/tools/toxi/math/SinCosLUT.java b/java/Mekstension/tools/toxi/math/SinCosLUT.java new file mode 100644 index 0000000..dd10eb8 --- /dev/null +++ b/java/Mekstension/tools/toxi/math/SinCosLUT.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.math; + +/** + * Lookup table for fast sine & cosine computations. The table currently has a + * fixed precision of 0.25 degrees to which input angles will be rounded to. All + * methods are static and can be used with both positive and negative input + * angles. + */ +public class SinCosLUT { + + /** + * set table precision to 0.25 degrees + */ + public static final float SC_PRECISION = 0.25f; + + /** + * calculate reciprocal for conversions + */ + public static final float SC_INV_PREC = 1.0f / SC_PRECISION; + + /** + * compute required table length + */ + public static final int SC_PERIOD = (int) (360f * SC_INV_PREC); + + /** + * LUT for sine values + */ + public static final float[] sinLUT = new float[SC_PERIOD]; + + /** + * LUT for cosine values + */ + public static final float[] cosLUT = new float[SC_PERIOD]; + + /** + * Pre-multiplied degrees -> radians + */ + private static final float DEG_TO_RAD = (float) (Math.PI / 180.0) + * SC_PRECISION; + + /** + * Pre-multiplied radians - degrees + */ + private static final float RAD_TO_DEG = (float) (180.0 / Math.PI) + / SC_PRECISION; + + // init sin/cos tables with values + static { + for (int i = 0; i < SC_PERIOD; i++) { + sinLUT[i] = (float) Math.sin(i * DEG_TO_RAD); + cosLUT[i] = (float) Math.cos(i * DEG_TO_RAD); + } + } + + /** + * Calculates sine for the passed angle in radians. + * + * @param theta + * @return sine value for theta + */ + public static final float sin(float theta) { + while (theta < 0) { + theta += MathUtils.TWO_PI; + } + return sinLUT[(int) (theta * RAD_TO_DEG) % SC_PERIOD]; + } + + /** + * Calculate cosine for the passed in angle in radians. + * + * @param theta + * @return cosine value for theta + */ + public static final float cos(float theta) { + while (theta < 0) { + theta += MathUtils.TWO_PI; + } + return cosLUT[(int) (theta * RAD_TO_DEG) % SC_PERIOD]; + } +} diff --git a/java/Mekstension/tools/toxi/math/conversion/UnitTranslator.java b/java/Mekstension/tools/toxi/math/conversion/UnitTranslator.java new file mode 100644 index 0000000..92a3084 --- /dev/null +++ b/java/Mekstension/tools/toxi/math/conversion/UnitTranslator.java @@ -0,0 +1,37 @@ +package toxi.math.conversion; + +public class UnitTranslator { + + static final double INCH_MM = 25.4; + + static final double POINT_POSTSCRIPT = 72.0; + + double pixelsToInch(int pix, int dpi) { + return (double) pix / dpi; + } + + double pixelsToPoints(int pix, int dpi) { + return pixelsToInch(pix, dpi) * POINT_POSTSCRIPT; + } + + double pixelsToMillis(int pix, int dpi) { + return pixelsToInch(pix, dpi) * INCH_MM; + } + + double millisToPoints(double mm) { + return mm / INCH_MM * POINT_POSTSCRIPT; + } + + double pointsToMillis(double pt) { + return pt / POINT_POSTSCRIPT * INCH_MM; + } + + int pointsToPixels(double pt, int dpi) { + return millisToPixels(pointsToMillis(pt), dpi); + } + + int millisToPixels(double mm, int dpi) { + return (int) (mm / INCH_MM * dpi); + } + +} diff --git a/java/Mekstension/tools/toxi/math/noise/PerlinNoise.java b/java/Mekstension/tools/toxi/math/noise/PerlinNoise.java new file mode 100644 index 0000000..09e533c --- /dev/null +++ b/java/Mekstension/tools/toxi/math/noise/PerlinNoise.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.math.noise; + +import java.util.Random; + +import toxi.math.SinCosLUT; + + +public class PerlinNoise { + // //////////////////////////////////////////////////////////// + // PERLIN NOISE taken from the + // [toxi 040903] + // octaves and amplitude amount per octave are now user controlled + // via the noiseDetail() function. + // [toxi 030902] + // cleaned up code and now using bagel's cosine table to speed up + // [toxi 030901] + // implementation by the german demo group farbrausch + // as used in their demo "art": http://www.farb-rausch.de/fr010src.zip + + protected static final int PERLIN_YWRAPB = 4; + + protected static final int PERLIN_YWRAP = 1 << PERLIN_YWRAPB; + + protected static final int PERLIN_ZWRAPB = 8; + + protected static final int PERLIN_ZWRAP = 1 << PERLIN_ZWRAPB; + + protected static final int PERLIN_SIZE = 4095; + + private static final float PERLIN_MIN_AMPLITUDE = 0.001f; + + protected int perlin_octaves = 4; // default to medium smooth + + protected float perlin_amp_falloff = 0.5f; // 50% reduction/octave + + // [toxi 031112] + // new vars needed due to recent change of cos table in PGraphics + protected int perlin_TWOPI, perlin_PI; + + protected float[] perlin_cosTable; + + protected float perlin[]; + + protected Random perlinRandom; + + public PerlinNoise() { + noiseSeed(System.nanoTime()); + } + + /** + * Computes the Perlin noise function value at point x. + */ + public float noise(float x) { + // is this legit? it's a dumb way to do it (but repair it later) + return noise(x, 0f, 0f); + } + + /** + * Computes the Perlin noise function value at the point x, y. + */ + public float noise(float x, float y) { + return noise(x, y, 0f); + } + + /** + * Computes the Perlin noise function value at x, y, z. + */ + public float noise(float x, float y, float z) { + if (perlin == null) { + if (perlinRandom == null) { + perlinRandom = new Random(); + } + perlin = new float[PERLIN_SIZE + 1]; + for (int i = 0; i < PERLIN_SIZE + 1; i++) { + perlin[i] = perlinRandom.nextFloat(); // (float)Math.random(); + } + // [toxi 031112] + // noise broke due to recent change of cos table in PGraphics + // this will take care of it + perlin_cosTable = SinCosLUT.cosLUT; + perlin_TWOPI = perlin_PI = SinCosLUT.SC_PERIOD; + perlin_PI >>= 1; + } + + if (x < 0) + x = -x; + if (y < 0) + y = -y; + if (z < 0) + z = -z; + + int xi = (int) x, yi = (int) y, zi = (int) z; + float xf = (float) (x - xi); + float yf = (float) (y - yi); + float zf = (float) (z - zi); + float rxf, ryf; + + float r = 0; + float ampl = 0.5f; + + float n1, n2, n3; + + for (int i = 0; i < perlin_octaves; i++) { + int of = xi + (yi << PERLIN_YWRAPB) + (zi << PERLIN_ZWRAPB); + + rxf = noise_fsc(xf); + ryf = noise_fsc(yf); + + n1 = perlin[of & PERLIN_SIZE]; + n1 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n1); + n2 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE]; + n2 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n2); + n1 += ryf * (n2 - n1); + + of += PERLIN_ZWRAP; + n2 = perlin[of & PERLIN_SIZE]; + n2 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n2); + n3 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE]; + n3 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n3); + n2 += ryf * (n3 - n2); + + n1 += noise_fsc(zf) * (n2 - n1); + + r += n1 * ampl; + ampl *= perlin_amp_falloff; + + // break if amp has no more impact + if (ampl= 1.0f) { + xi++; + xf--; + } + if (yf >= 1.0f) { + yi++; + yf--; + } + if (zf >= 1.0f) { + zi++; + zf--; + } + } + return r; + } + + // [toxi 031112] + // now adjusts to the size of the cosLUT used via + // the new variables, defined above + private float noise_fsc(float i) { + // using bagel's cosine table instead + return 0.5f * (1.0f - perlin_cosTable[(int) (i * perlin_PI) + % perlin_TWOPI]); + } + + // [toxi 040903] + // make perlin noise quality user controlled to allow + // for different levels of detail. lower values will produce + // smoother results as higher octaves are surpressed + + public void noiseDetail(int lod) { + if (lod > 0) + perlin_octaves = lod; + } + + public void noiseDetail(int lod, float falloff) { + if (lod > 0) + perlin_octaves = lod; + if (falloff > 0) + perlin_amp_falloff = falloff; + } + + public void noiseSeed(long what) { + if (perlinRandom == null) + perlinRandom = new Random(); + perlinRandom.setSeed(what); + perlin = null; + } +} diff --git a/java/Mekstension/tools/toxi/math/noise/SimplexNoise.java b/java/Mekstension/tools/toxi/math/noise/SimplexNoise.java new file mode 100644 index 0000000..113e09e --- /dev/null +++ b/java/Mekstension/tools/toxi/math/noise/SimplexNoise.java @@ -0,0 +1,519 @@ +package toxi.math.noise; + +/** + * Simplex Noise in 2D, 3D and 4D. Based on the example code of this paper: + * http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf + * + * @author Stefan Gustavson, Linkping University, Sweden (stegu at itn dot liu + * dot se) + * + * Slight optimizations & restructuring by + * @author Karsten Schmidt (info at toxi dot co dot uk) + * + */ + +public class SimplexNoise { + + private static final double SQRT3 = Math.sqrt(3.0); + private static final double SQRT5 = Math.sqrt(5.0); + + /** + * Skewing and unskewing factors for 2D, 3D and 4D, some of them + * pre-multiplied. + */ + private static final double F2 = 0.5 * (SQRT3 - 1.0); + private static final double G2 = (3.0 - SQRT3) / 6.0; + private static final double G22 = G2 * 2.0 - 1; + + private static final double F3 = 1.0 / 3.0; + private static final double G3 = 1.0 / 6.0; + + private static final double F4 = (SQRT5 - 1.0) / 4.0; + private static final double G4 = (5.0 - SQRT5) / 20.0; + private static final double G42 = G4 * 2.0; + private static final double G43 = G4 * 3.0; + private static final double G44 = G4 * 4.0 - 1.0; + + /** + * Gradient vectors for 3D (pointing to mid points of all edges of a unit + * cube) + */ + private static final int[][] grad3 = { { 1, 1, 0 }, { -1, 1, 0 }, + { 1, -1, 0 }, { -1, -1, 0 }, { 1, 0, 1 }, { -1, 0, 1 }, + { 1, 0, -1 }, { -1, 0, -1 }, { 0, 1, 1 }, { 0, -1, 1 }, + { 0, 1, -1 }, { 0, -1, -1 } }; + + /** + * Gradient vectors for 4D (pointing to mid points of all edges of a unit 4D + * hypercube) + */ + private static final int[][] grad4 = { { 0, 1, 1, 1 }, { 0, 1, 1, -1 }, + { 0, 1, -1, 1 }, { 0, 1, -1, -1 }, { 0, -1, 1, 1 }, + { 0, -1, 1, -1 }, { 0, -1, -1, 1 }, { 0, -1, -1, -1 }, + { 1, 0, 1, 1 }, { 1, 0, 1, -1 }, { 1, 0, -1, 1 }, { 1, 0, -1, -1 }, + { -1, 0, 1, 1 }, { -1, 0, 1, -1 }, { -1, 0, -1, 1 }, + { -1, 0, -1, -1 }, { 1, 1, 0, 1 }, { 1, 1, 0, -1 }, + { 1, -1, 0, 1 }, { 1, -1, 0, -1 }, { -1, 1, 0, 1 }, + { -1, 1, 0, -1 }, { -1, -1, 0, 1 }, { -1, -1, 0, -1 }, + { 1, 1, 1, 0 }, { 1, 1, -1, 0 }, { 1, -1, 1, 0 }, { 1, -1, -1, 0 }, + { -1, 1, 1, 0 }, { -1, 1, -1, 0 }, { -1, -1, 1, 0 }, + { -1, -1, -1, 0 } }; + + /** + * Permutation table + */ + private static final int[] p = { 151, 160, 137, 91, 90, 15, 131, 13, 201, + 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, + 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, + 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, + 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, + 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, + 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, + 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, + 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, + 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, + 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, + 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, + 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, + 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, + 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, + 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, + 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, + 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 }; + + /** + * To remove the need for index wrapping, double the permutation table + * length + */ + private static int[] perm = new int[0x200]; + /** + * A lookup table to traverse the simplex around a given point in 4D. + * Details can be found where this table is used, in the 4D noise method. + */ + private static final int[][] simplex = { { 0, 1, 2, 3 }, { 0, 1, 3, 2 }, + { 0, 0, 0, 0 }, { 0, 2, 3, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 1, 2, 3, 0 }, { 0, 2, 1, 3 }, { 0, 0, 0, 0 }, + { 0, 3, 1, 2 }, { 0, 3, 2, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 1, 3, 2, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 2, 0, 3 }, { 0, 0, 0, 0 }, + { 1, 3, 0, 2 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, + { 2, 3, 0, 1 }, { 2, 3, 1, 0 }, { 1, 0, 2, 3 }, { 1, 0, 3, 2 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 2, 0, 3, 1 }, + { 0, 0, 0, 0 }, { 2, 1, 3, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 2, 0, 1, 3 }, { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 3, 0, 1, 2 }, { 3, 0, 2, 1 }, + { 0, 0, 0, 0 }, { 3, 1, 2, 0 }, { 2, 1, 0, 3 }, { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 3, 1, 0, 2 }, { 0, 0, 0, 0 }, + { 3, 2, 0, 1 }, { 3, 2, 1, 0 } }; + + static { + for (int i = 0; i < 0x200; i++) { + perm[i] = p[i & 0xff]; + } + } + + /** + * Computes dot product in 2D. + * + * @param g + * 2-vector (grid offset) + * @param x + * @param y + * @return dot product + */ + private static double dot(int g[], double x, double y) { + return g[0] * x + g[1] * y; + } + + /** + * Computes dot product in 3D. + * + * @param g + * 3-vector (grid offset) + * @param x + * @param y + * @param z + * @return dot product + */ + private static double dot(int g[], double x, double y, double z) { + return g[0] * x + g[1] * y + g[2] * z; + } + + /** + * Computes dot product in 4D. + * + * @param g + * 4-vector (grid offset) + * @param x + * @param y + * @param z + * @param w + * @return dot product + */ + private static double dot(int g[], double x, double y, double z, double w) { + return g[0] * x + g[1] * y + g[2] * z + g[3] * w; + } + + /** + * This method is a *lot* faster than using (int)Math.floor(x). + * + * @param x + * value to be floored + * @return + */ + private static final int fastfloor(double x) { + return x > 0 ? (int) x : (int) x - 1; + } + + /** + * Computes 2D Simplex Noise. + * + * @param x + * coordinate + * @param y + * coordinate + * @return noise value in range -1 ... +1. + */ + public static double noise(double x, double y) { + double n0 = 0, n1 = 0, n2 = 0; // Noise contributions from the three + // corners + // Skew the input space to determine which simplex cell we're in + double s = (x + y) * F2; // Hairy factor for 2D + int i = fastfloor(x + s); + int j = fastfloor(y + s); + double t = (i + j) * G2; + double x0 = x - (i - t); // The x,y distances from the cell origin + double y0 = y - (j - t); + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) + if (x0 > y0) { + i1 = 1; + j1 = 0; + } // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else { + i1 = 0; + j1 = 1; + } // upper triangle, YX order: (0,0)->(0,1)->(1,1) + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed + double y1 = y0 - j1 + G2; + double x2 = x0 + G22; // Offsets for last corner in (x,y) unskewed + double y2 = y0 + G22; + // Work out the hashed gradient indices of the three simplex corners + int ii = i & 0xff; + int jj = j & 0xff; + // Calculate the contribution from the three corners + double t0 = 0.5 - x0 * x0 - y0 * y0; + if (t0 > 0) { + t0 *= t0; + int gi0 = perm[ii + perm[jj]] % 12; + n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for + // 2D gradient + } + double t1 = 0.5 - x1 * x1 - y1 * y1; + if (t1 > 0) { + t1 *= t1; + int gi1 = perm[ii + i1 + perm[jj + j1]] % 12; + n1 = t1 * t1 * dot(grad3[gi1], x1, y1); + } + double t2 = 0.5 - x2 * x2 - y2 * y2; + if (t2 > 0) { + t2 *= t2; + int gi2 = perm[ii + 1 + perm[jj + 1]] % 12; + n2 = t2 * t2 * dot(grad3[gi2], x2, y2); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70.0 * (n0 + n1 + n2); + } + + /** + * Computes 3D Simplex Noise. + * + * @param x + * coordinate + * @param y + * coordinate + * @param z + * coordinate + * @return noise value in range -1 ... +1 + */ + public static double noise(double x, double y, double z) { + double n0 = 0, n1 = 0, n2 = 0, n3 = 0; + // Noise contributions from the + // four corners + // Skew the input space to determine which simplex cell we're in + // final double F3 = 1.0 / 3.0; + double s = (x + y + z) * F3; // Very nice and simple skew factor + // for 3D + int i = fastfloor(x + s); + int j = fastfloor(y + s); + int k = fastfloor(z + s); + // final double G3 = 1.0 / 6.0; // Very nice and simple unskew factor, + // too + double t = (i + j + k) * G3; + double x0 = x - (i - t); // The x,y,z distances from the cell origin + double y0 = y - (j - t); + double z0 = z - (k - t); + // For the 3D case, the simplex shape is a slightly irregular + // tetrahedron. + // Determine which simplex we are in. + int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) + // coords + int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + if (x0 >= y0) { + if (y0 >= z0) { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 1; + k2 = 0; + } // X Y Z order + else if (x0 >= z0) { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 0; + k2 = 1; + } // X Z Y order + else { + i1 = 0; + j1 = 0; + k1 = 1; + i2 = 1; + j2 = 0; + k2 = 1; + } // Z X Y order + } else { // x0 0) { + t0 *= t0; + int gi0 = perm[ii + perm[jj + perm[kk]]] % 12; + n0 = t0 * t0 * dot(grad3[gi0], x0, y0, z0); + } + double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1; + if (t1 > 0) { + t1 *= t1; + int gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1]]] % 12; + n1 = t1 * t1 * dot(grad3[gi1], x1, y1, z1); + } + double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2; + if (t2 > 0) { + t2 *= t2; + int gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2]]] % 12; + n2 = t2 * t2 * dot(grad3[gi2], x2, y2, z2); + } + double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3; + if (t3 > 0) { + t3 *= t3; + int gi3 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1]]] % 12; + n3 = t3 * t3 * dot(grad3[gi3], x3, y3, z3); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to stay just inside [-1,1] + return 32.0 * (n0 + n1 + n2 + n3); + } + + /** + * Computes 4D Simplex Noise. + * + * @param x + * coordinate + * @param y + * coordinate + * @param z + * coordinate + * @param w + * coordinate + * @return noise value in range -1 ... +1 + */ + public static double noise(double x, double y, double z, double w) { + // The skewing and unskewing factors are hairy again for the 4D case + double n0 = 0, n1 = 0, n2 = 0, n3 = 0, n4 = 0; // Noise contributions + // from the five corners + // Skew the (x,y,z,w) space to determine which cell of 24 simplices + double s = (x + y + z + w) * F4; // Factor for 4D skewing + int i = fastfloor(x + s); + int j = fastfloor(y + s); + int k = fastfloor(z + s); + int l = fastfloor(w + s); + double t = (i + j + k + l) * G4; // Factor for 4D unskewing + double x0 = x - (i - t); // The x,y,z,w distances from the cell origin + double y0 = y - (j - t); + double z0 = z - (k - t); + double w0 = w - (l - t); + // For the 4D case, the simplex is a 4D shape I won't even try to + // describe. + // To find out which of the 24 possible simplices we're in, we need to + // determine the magnitude ordering of x0, y0, z0 and w0. + // The method below is a good way of finding the ordering of x,y,z,w and + // then find the correct traversal order for the simplex were in. + // First, six pair-wise comparisons are performed between each possible + // pair of the four coordinates, and the results are used to add up + // binary bits for an integer index. + int c = 0; + if (x0 > y0) { + c = 0x20; + } + if (x0 > z0) { + c |= 0x10; + } + if (y0 > z0) { + c |= 0x08; + } + if (x0 > w0) { + c |= 0x04; + } + if (y0 > w0) { + c |= 0x02; + } + if (z0 > w0) { + c |= 0x01; + } + int i1, j1, k1, l1; // The integer offsets for the second simplex corner + int i2, j2, k2, l2; // The integer offsets for the third simplex corner + int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner + // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some + // order. Many values of c will never occur, since e.g. x>y>z>w makes + // x= 3 ? 1 : 0; + j1 = sc[1] >= 3 ? 1 : 0; + k1 = sc[2] >= 3 ? 1 : 0; + l1 = sc[3] >= 3 ? 1 : 0; + // The number 2 in the "simplex" array is at the second largest + // coordinate. + i2 = sc[0] >= 2 ? 1 : 0; + j2 = sc[1] >= 2 ? 1 : 0; + k2 = sc[2] >= 2 ? 1 : 0; + l2 = sc[3] >= 2 ? 1 : 0; + // The number 1 in the "simplex" array is at the second smallest + // coordinate. + i3 = sc[0] >= 1 ? 1 : 0; + j3 = sc[1] >= 1 ? 1 : 0; + k3 = sc[2] >= 1 ? 1 : 0; + l3 = sc[3] >= 1 ? 1 : 0; + // The fifth corner has all coordinate offsets = 1, so no need to look + // that up. + double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) + double y1 = y0 - j1 + G4; + double z1 = z0 - k1 + G4; + double w1 = w0 - l1 + G4; + + double x2 = x0 - i2 + G42; // Offsets for third corner in (x,y,z,w) + double y2 = y0 - j2 + G42; + double z2 = z0 - k2 + G42; + double w2 = w0 - l2 + G42; + + double x3 = x0 - i3 + G43; // Offsets for fourth corner in (x,y,z,w) + double y3 = y0 - j3 + G43; + double z3 = z0 - k3 + G43; + double w3 = w0 - l3 + G43; + + double x4 = x0 + G44; // Offsets for last corner in (x,y,z,w) + double y4 = y0 + G44; + double z4 = z0 + G44; + double w4 = w0 + G44; + + // Work out the hashed gradient indices of the five simplex corners + int ii = i & 0xff; + int jj = j & 0xff; + int kk = k & 0xff; + int ll = l & 0xff; + + // Calculate the contribution from the five corners + double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0; + if (t0 > 0) { + t0 *= t0; + int gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32; + n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0); + } + double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1; + if (t1 > 0) { + t1 *= t1; + int gi1 = perm[ii + i1 + + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32; + n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1); + } + double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2; + if (t2 > 0) { + t2 *= t2; + int gi2 = perm[ii + i2 + + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32; + n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2); + } + double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3; + if (t3 > 0) { + t3 *= t3; + int gi3 = perm[ii + i3 + + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32; + n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3); + } + double t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4; + if (t4 > 0) { + t4 *= t4; + int gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32; + n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4); + } + // Sum up and scale the result to cover the range [-1,1] + return 27.0 * (n0 + n1 + n2 + n3 + n4); + } +} \ No newline at end of file diff --git a/java/Mekstension/tools/toxi/math/waves/AMFMSineWave.java b/java/Mekstension/tools/toxi/math/waves/AMFMSineWave.java new file mode 100644 index 0000000..af5fa76 --- /dev/null +++ b/java/Mekstension/tools/toxi/math/waves/AMFMSineWave.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.math.waves; + +/** + *

+ * Amplitude and frequency modulated sine wave. Uses 2 secondary waves to + * modulate the shape of the main wave. + *

+ * + *

+ * Note: You must NEVER call the update() method on the + * modulating waves. + *

+ */ +public class AMFMSineWave extends AbstractWave { + + public AbstractWave fmod; + public AbstractWave amod; + + /** + * Creates a new instance from + * + * @param phase + * @param freq + * @param fmod + * @param amod + */ + public AMFMSineWave(float phase, float freq, AbstractWave fmod, + AbstractWave amod) { + super(phase, freq); + this.amod = amod; + this.fmod = fmod; + } + + /** + * @param phase + * @param freq + * @param offset + * @param fmod + * @param amod + */ + public AMFMSineWave(float phase, float freq, float offset, + AbstractWave fmod, AbstractWave amod) { + super(phase, freq, 1, offset); + this.amod = amod; + this.fmod = fmod; + } + + /** + * Progresses the wave and updates the result value. You must NEVER call the + * update() method on the 2 modulating wave since this is handled + * automatically by this method. + * + * @see toxi.math.waves.AbstractWave#update() + */ + public float update() { + amp = amod.update(); + value = amp * (float) Math.sin(phase) + offset; + cyclePhase(frequency + fmod.update()); + return value; + } + + /** + * @deprecated + * @param fmod + */ + public void setFMod(AbstractWave fmod) { + this.fmod = fmod; + } + + /** + * @deprecated + * @param amod + */ + public void setAMod(AbstractWave amod) { + this.amod = amod; + } + + /** + * @deprecated + */ + public AbstractWave getFMod() { + return fmod; + } + + /** + * @deprecated + */ + public AbstractWave getAMod() { + return amod; + } + + /** + * Resets this wave and its modulating waves as well. + * + * @see toxi.math.waves.AbstractWave#reset() + */ + public void reset() { + super.reset(); + fmod.reset(); + amod.reset(); + } +} diff --git a/java/Mekstension/tools/toxi/math/waves/AbstractWave.java b/java/Mekstension/tools/toxi/math/waves/AbstractWave.java new file mode 100644 index 0000000..758f59d --- /dev/null +++ b/java/Mekstension/tools/toxi/math/waves/AbstractWave.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.math.waves; + +/** + * Abstract wave oscillator type which needs to be subclassed to implement + * different waveforms. Please note that the frequency unit is radians, but + * conversion methods to & from Hertz ({@link #hertzToRadians(float, float)}) + * are included in this base class. + */ +public abstract class AbstractWave { + + public static final float PI = 3.14159265358979323846f; + public static final float TWO_PI = 2 * PI; + + /** + * Current wave phase + */ + public float phase; + protected float origPhase; + + public float frequency; + + public float amp; + + public float offset; + + public float value = 0; + + /** + * @param phase + */ + public AbstractWave(float phase) { + this(phase, 1, 1, 0); + } + + /** + * + * @param phase + * @param freq + */ + public AbstractWave(float phase, float freq) { + this(phase, freq, 1, 0); + } + + /** + * @param phase + * @param freq + * @param amp + * @param offset + */ + public AbstractWave(float phase, float freq, float amp, float offset) { + this.phase = this.origPhase = phase; + this.frequency = freq; + this.amp = amp; + this.offset = offset; + } + + /** + * Updates the wave and returns new value. Implementing classes should + * manually ensure the phase remains in the 0...TWO_PI interval or by + * calling {@link #cyclePhase()}. + * + * @return current (newly calculated) wave value + */ + public abstract float update(); + + /** + * Ensures phase remains in the 0...TWO_PI interval. + * + * @return current phase + */ + protected final float cyclePhase() { + phase %= TWO_PI; + if (phase < 0) + phase += TWO_PI; + return phase; + } + + /** + * Progresses phase and ensures it remains in the 0...TWO_PI interval. + * + * @param freq + * normalized progress frequency + * @return update phase value + */ + protected final float cyclePhase(float freq) { + phase = (phase + freq) % TWO_PI; + if (phase < 0) + phase += TWO_PI; + return phase; + } + + /** + * @deprecated + * @param amp + */ + // @Deprecated + public void setAmp(float amp) { + this.amp = amp; + } + + /** + * @deprecated + */ + // @Deprecated + public float getAmp() { + return amp; + } + + /** + * @deprecated + */ + // @Deprecated + public float getValue() { + return value; + } + + /** + * @deprecated + */ + // @Deprecated + public float getPhase() { + return phase; + } + + /** + * Starts the wave from a new phase. The new phase position will also be + * used for any later call to {{@link #reset()} + * + * @param phase + * new phase + */ + public void setPhase(float phase) { + this.phase = this.origPhase = phase; + } + + /** + * Resets the wave phase to the original start value + */ + public void reset() { + phase = origPhase; + } + + /** + * @deprecated + */ + // @Deprecated + public float getFrequency() { + return frequency; + } + + /** + * @deprecated + * @param freq + */ + // @Deprecated + public void setFrequency(float freq) { + this.frequency = freq; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(this.getClass().getName()).append(" phase: ").append(phase); + sb.append(" frequency: ").append(frequency); + sb.append(" amp: ").append(amp); + return sb.toString(); + } + + /** + * Converts a frequency in Hertz into radians. + * + * @param hz + * frequency to convert (in Hz) + * @param sampleRate + * sampling rate in Hz (equals period length @ 1 Hz) + * @return frequency in radians + */ + public static final float hertzToRadians(float hz, float sampleRate) { + return hz / sampleRate * TWO_PI; + } + + /** + * Converts a frequency from radians to Hertz. + * + * @param f + * frequency in radians + * @param sampleRate + * sampling rate in Hz (equals period length @ 1 Hz) + * @return freq in Hz + */ + public static final float radiansToHertz(float f, float sampleRate) { + return f / TWO_PI * sampleRate; + } +} \ No newline at end of file diff --git a/java/Mekstension/tools/toxi/math/waves/ConstantWave.java b/java/Mekstension/tools/toxi/math/waves/ConstantWave.java new file mode 100644 index 0000000..ab2bfff --- /dev/null +++ b/java/Mekstension/tools/toxi/math/waves/ConstantWave.java @@ -0,0 +1,17 @@ +package toxi.math.waves; + +/** + * Implements a constant value as waveform. + * @author toxi + */ +public class ConstantWave extends AbstractWave { + + public ConstantWave(float value) { + super(0); + this.value = value; + } + + public final float update() { + return value; + } +} diff --git a/java/Mekstension/tools/toxi/math/waves/FMHarmonicSquareWave.java b/java/Mekstension/tools/toxi/math/waves/FMHarmonicSquareWave.java new file mode 100644 index 0000000..718c4bd --- /dev/null +++ b/java/Mekstension/tools/toxi/math/waves/FMHarmonicSquareWave.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.math.waves; + +/** + *

+ * Frequency modulated bandwidth-limited square wave using a + * fourier series of harmonics. Also uses a secondary wave to modulate the + * frequency of the main wave. + *

+ * + *

+ * Note: You must NEVER call the update() method on the + * modulating wave. + *

+ */ +public class FMHarmonicSquareWave extends AbstractWave { + + public AbstractWave fmod; + + /** + * Maximum harmonics to add (make sure you stay under Nyquist freq), default + * = 9 + */ + public int maxHarmonics = 3; + + /** + * Convenience constructor to create a non frequency modulated square wave + * + * @param phase + * @param freq + * base frequency (in radians) + * @param amp + * @param offset + */ + public FMHarmonicSquareWave(float phase, float freq, float amp, + float offset) { + this(phase, freq, amp, offset, new ConstantWave(0)); + } + + public FMHarmonicSquareWave(float phase, float freq, + AbstractWave fmod) { + super(phase, freq); + this.fmod = fmod; + } + + public FMHarmonicSquareWave(float phase, float freq, float amp, + float offset, AbstractWave fmod) { + super(phase, freq, amp, offset); + this.fmod = fmod; + } + + /** + * Progresses the wave and updates the result value. You must NEVER call the + * update() method on the modulating wave since this is handled + * automatically by this method. + * + * @see toxi.math.waves.AbstractWave#update() + */ + public float update() { + value = 0; + for (int i = 1; i <= maxHarmonics; i += 2) { + value += 1.0 / i * (float) Math.sin(i * phase); + } + value = value * amp + offset; + cyclePhase(frequency + fmod.update()); + return value; + } +} diff --git a/java/Mekstension/tools/toxi/math/waves/FMSawtoothWave.java b/java/Mekstension/tools/toxi/math/waves/FMSawtoothWave.java new file mode 100644 index 0000000..416c36c --- /dev/null +++ b/java/Mekstension/tools/toxi/math/waves/FMSawtoothWave.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.math.waves; + +/** + *

+ * Frequency modulated bandwidth unlimited pure sawtooth wave. Uses a secondary + * wave to modulate the frequency of the main wave. + *

+ * + *

+ * Note: You must NEVER call the update() method on the + * modulating wave. + *

+ */ +public class FMSawtoothWave extends AbstractWave { + + public AbstractWave fmod; + + /** + * Convenience constructor to create a non frequency modulated sawtooth. + * + * @param phase + * @param freq + * base frequency (in radians) + * @param amp + * @param offset + */ + public FMSawtoothWave(float phase, float freq, float amp, float offset) { + this(phase, freq, amp, offset, new ConstantWave(0)); + } + + public FMSawtoothWave(float phase, float freq, AbstractWave fmod) { + super(phase, freq); + this.fmod = fmod; + } + + public FMSawtoothWave(float phase, float freq, float amp, float offset, + AbstractWave fmod) { + super(phase, freq, amp, offset); + this.fmod = fmod; + } + + /** + * Progresses the wave and updates the result value. You must NEVER call the + * update() method on the modulating wave since this is handled + * automatically by this method. + * + * @see toxi.math.waves.AbstractWave#update() + */ + public float update() { + value = ((phase / TWO_PI) * 2 - 1) * amp + offset; + cyclePhase(frequency + fmod.update()); + return value; + } +} diff --git a/java/Mekstension/tools/toxi/math/waves/FMSineWave.java b/java/Mekstension/tools/toxi/math/waves/FMSineWave.java new file mode 100644 index 0000000..d51f903 --- /dev/null +++ b/java/Mekstension/tools/toxi/math/waves/FMSineWave.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.math.waves; + +/** + *

+ * Frequency modulated sine wave. Uses a secondary wave to modulate the + * frequency of the main wave. + *

+ * + *

+ * Note: You must NEVER call the update() method on the + * modulating wave. + *

+ */ +public class FMSineWave extends AbstractWave { + + public AbstractWave fmod; + + public FMSineWave(float phase, float freq, float amp, float offset) { + this(phase, freq, amp, offset, new ConstantWave(0)); + } + + public FMSineWave(float phase, float freq, AbstractWave fmod) { + super(phase, freq); + this.fmod = fmod; + } + + public FMSineWave(float phase, float freq, float amp, float offset, + AbstractWave fmod) { + super(phase, freq, amp, offset); + this.fmod = fmod; + } + + /** + * Progresses the wave and updates the result value. You must NEVER call the + * update() method on the modulating wave since this is handled + * automatically by this method. + * + * @see toxi.math.waves.AbstractWave#update() + */ + public float update() { + value = (float) (Math.sin(phase) * amp) + offset; + cyclePhase(frequency + fmod.update()); + return value; + } +} diff --git a/java/Mekstension/tools/toxi/math/waves/FMSquareWave.java b/java/Mekstension/tools/toxi/math/waves/FMSquareWave.java new file mode 100644 index 0000000..38f73dd --- /dev/null +++ b/java/Mekstension/tools/toxi/math/waves/FMSquareWave.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package toxi.math.waves; + +/** + *

+ * Frequency modulated bandwidth unlimited pure digital square wave. Uses a + * secondary wave to modulate the frequency of the main wave. + *

+ * + *

+ * Note: You must NEVER call the update() method on the + * modulating wave. + *

+ */ +public class FMSquareWave extends AbstractWave { + + public AbstractWave fmod; + + /** + * Convenience constructor to create a non frequency modulated square wave + * + * @param phase + * @param freq + * base frequency (in radians) + * @param amp + * @param offset + */ + public FMSquareWave(float phase, float freq, float amp, float offset) { + this(phase, freq, amp, offset, new ConstantWave(0)); + } + + public FMSquareWave(float phase, float freq, AbstractWave fmod) { + super(phase, freq); + this.fmod = fmod; + } + + public FMSquareWave(float phase, float freq, float amp, float offset, + AbstractWave fmod) { + super(phase, freq, amp, offset); + this.fmod = fmod; + } + + /** + * Progresses the wave and updates the result value. You must NEVER call the + * update() method on the modulating wave since this is handled + * automatically by this method. + * + * @see toxi.math.waves.AbstractWave#update() + */ + public float update() { + value = (phase / TWO_PI < 0.5 ? 1 : -1) * amp + offset; + cyclePhase(frequency + fmod.update()); + return value; + } +} diff --git a/java/Mekstension/tools/toxi/math/waves/SineWave.java b/java/Mekstension/tools/toxi/math/waves/SineWave.java new file mode 100644 index 0000000..fb5547c --- /dev/null +++ b/java/Mekstension/tools/toxi/math/waves/SineWave.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2006, 2007 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.math.waves; + +/** + * Standard Sine wave at fixed frequency and values normalized to the given + * amplitude. + */ +public class SineWave extends AbstractWave { + + /** + * @param phase + * starting phase + * @param freq + * in radians (not Hertz) + */ + public SineWave(float phase, float freq) { + super(phase, freq); + } + + /** + * @param phase + * starting phase + * @param freq + * in radians (not Hertz) + * @param amp + * amplitude factor + * @param offset + * centre oscillation value + */ + public SineWave(float phase, float freq, float amp, float offset) { + super(phase, freq, amp, offset); + } + + /* + * (non-Javadoc) + * + * @see toxi.math.waves.AbstractWave#update() + */ + public float update() { + value = (float) (Math.sin(phase) * amp) + offset; + cyclePhase(frequency); + return value; + } +} diff --git a/java/Mekstension/tools/toxi/util/datatypes/ArrayUtil.java b/java/Mekstension/tools/toxi/util/datatypes/ArrayUtil.java new file mode 100644 index 0000000..0868607 --- /dev/null +++ b/java/Mekstension/tools/toxi/util/datatypes/ArrayUtil.java @@ -0,0 +1,164 @@ +package toxi.util.datatypes; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Random; + +/** + * A collection of array utilities. + * + * @author toxi + */ +public class ArrayUtil { + + /** + * Rearranges the array items in random order using the default + * java.util.Random generator. Operation is in-place, no copy is created. + * + * @param array + */ + public static void shuffle(T[] array) { + shuffle(array, new Random()); + } + + /** + * Rearranges the array items in random order using the given RNG. Operation + * is in-place, no copy is created. + * + * @param array + * @param rnd + */ + public static void shuffle(T[] array, Random rnd) { + int N = array.length; + for (int i = 0; i < N; i++) { + int r = i + (int) (rnd.nextFloat() * (N - i)); // between i and N-1 + T swap = array[i]; + array[i] = array[r]; + array[r] = swap; + } + } + + /** + * Reverses the item order of the supplied array (generic types). + * + * @param array + */ + public static void reverse(T[] array) { + int len = array.length - 1; + int len2 = array.length / 2; + for (int i = 0; i < len2; i++) { + T tmp = array[i]; + array[i] = array[len - i]; + array[len - i] = tmp; + } + } + + /** + * Reverses the item order of the supplied byte array. + * + * @param array + */ + public static void reverse(byte[] array) { + int len = array.length - 1; + int len2 = array.length / 2; + for (int i = 0; i < len2; i++) { + byte tmp = array[i]; + array[i] = array[len - i]; + array[len - i] = tmp; + } + } + + /** + * Reverses the item order of the supplied short array. + * + * @param array + */ + public static void reverse(short[] array) { + int len = array.length - 1; + int len2 = array.length / 2; + for (int i = 0; i < len2; i++) { + short tmp = array[i]; + array[i] = array[len - i]; + array[len - i] = tmp; + } + } + + /** + * Reverses the item order of the supplied char array. + * + * @param array + */ + public static void reverse(char[] array) { + int len = array.length - 1; + int len2 = array.length / 2; + for (int i = 0; i < len2; i++) { + char tmp = array[i]; + array[i] = array[len - i]; + array[len - i] = tmp; + } + } + + /** + * Reverses the item order of the supplied int array. + * + * @param array + */ + public static void reverse(int[] array) { + int len = array.length - 1; + int len2 = array.length / 2; + for (int i = 0; i < len2; i++) { + int tmp = array[i]; + array[i] = array[len - i]; + array[len - i] = tmp; + } + } + + /** + * Reverses the item order of the supplied float array. + * + * @param array + */ + public static void reverse(float[] array) { + int len = array.length - 1; + int len2 = array.length / 2; + for (int i = 0; i < len2; i++) { + float tmp = array[i]; + array[i] = array[len - i]; + array[len - i] = tmp; + } + } + + /** + * Converts the generic array into an {@link ArrayList} of the same type. + * + * @param array + * @return array list version + */ + public static ArrayList arrayToList(T[] array) { + ArrayList list = new ArrayList(array.length); + for (int i = 0; i < array.length; i++) { + list.add(array[i]); + } + return list; + } + + /** + * Adds all array elements to the given collection of the same type. + * + * @param + * @param array + * array + * @param collection + * existing collection or null (to create a new {@link ArrayList} + * automatically) + */ + static void addArrayToCollection(T[] array, Collection collection) { + if (collection == null) { + collection = new ArrayList(); + } + for (T o : array) { + collection.add(o); + } + } + +} diff --git a/java/Mekstension/tools/toxi/util/datatypes/BiasedFloatRange.java b/java/Mekstension/tools/toxi/util/datatypes/BiasedFloatRange.java new file mode 100644 index 0000000..baedf21 --- /dev/null +++ b/java/Mekstension/tools/toxi/util/datatypes/BiasedFloatRange.java @@ -0,0 +1,41 @@ +package toxi.util.datatypes; + +import toxi.math.MathUtils; + +public class BiasedFloatRange extends FloatRange { + + public float bias; + public float standardDeviation; + + /** + * @param min + * min value (inclusive) + * @param max + * max value (inclusive) + * @param bias + * bias value (can be outside the min/max range, but values will + * be clipped) + * @param sd + * standard deviation (if bias at range mean sd=1.0, the entire + * range will be covered) + */ + public BiasedFloatRange(float min, float max, float bias, float sd) { + super(min, max); + this.bias = bias; + this.standardDeviation = sd * 0.5f; + } + + @Override + public float pickRandom() { + current = (float) (random.nextGaussian() * standardDeviation * (max - min)) + + bias; + current = MathUtils.clip(current, min, max); + return current; + } + + @Override + public String toString() { + return "BiasedFloatRange: " + min + " -> " + max + " bias: " + bias + + " q: " + standardDeviation; + } +} diff --git a/java/Mekstension/tools/toxi/util/datatypes/BiasedIntegerRange.java b/java/Mekstension/tools/toxi/util/datatypes/BiasedIntegerRange.java new file mode 100644 index 0000000..d5ec2cb --- /dev/null +++ b/java/Mekstension/tools/toxi/util/datatypes/BiasedIntegerRange.java @@ -0,0 +1,41 @@ +package toxi.util.datatypes; + +import toxi.math.MathUtils; + +public class BiasedIntegerRange extends IntegerRange { + + public int bias; + public float standardDeviation; + + /** + * @param min + * min value (inclusive) + * @param max + * max value (inclusive) + * @param bias + * bias value (can be outside the min/max range, but values will + * be clipped) + * @param sd + * standard deviation (if bias at range mean sd=1.0, the entire + * range will be covered) + */ + public BiasedIntegerRange(int min, int max, int bias, float sd) { + super(min, max); + this.bias = bias; + this.standardDeviation = sd * 0.5f; + } + + @Override + public int pickRandom() { + current = (int) (random.nextGaussian() * standardDeviation * (max - min)) + + bias; + current = MathUtils.clip(current, min, max); + return current; + } + + @Override + public String toString() { + return "BiasedIntegerRange: " + min + " -> " + max + " bias: " + bias + + " q: " + standardDeviation; + } +} diff --git a/java/Mekstension/tools/toxi/util/datatypes/FloatRange.java b/java/Mekstension/tools/toxi/util/datatypes/FloatRange.java new file mode 100644 index 0000000..d06678a --- /dev/null +++ b/java/Mekstension/tools/toxi/util/datatypes/FloatRange.java @@ -0,0 +1,47 @@ +package toxi.util.datatypes; + +import java.util.Random; + +import toxi.math.MathUtils; + +public class FloatRange { + + public float min, max; + public float current; + + protected Random random = new Random(); + + public FloatRange(float min, float max) { + this.min = min; + this.max = max; + } + + public void setRandom(Random rnd) { + random = rnd; + } + + public float pickRandom() { + current = MathUtils.random(random, min, max); + return current; + } + + public float getCurrent() { + return current; + } + + @Override + public String toString() { + return "FloatRange: " + min + " -> " + max; + } + + public FloatRange copy() { + FloatRange range = new FloatRange(min, max); + range.current = current; + range.random = random; + return range; + } + + public boolean isValueInRange(float val) { + return val >= min && val <= max; + } +} diff --git a/java/Mekstension/tools/toxi/util/datatypes/GenericSet.java b/java/Mekstension/tools/toxi/util/datatypes/GenericSet.java new file mode 100644 index 0000000..f4ece15 --- /dev/null +++ b/java/Mekstension/tools/toxi/util/datatypes/GenericSet.java @@ -0,0 +1,89 @@ +package toxi.util.datatypes; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Random; + +import toxi.math.MathUtils; + +public class GenericSet implements Iterable { + + protected ArrayList items; + protected int currID = -1; + protected T current; + + protected Random random = new Random(); + + public GenericSet(Collection items) { + this.items = new ArrayList(items); + pickRandom(); + } + + public GenericSet(T obj) { + items = new ArrayList(); + items.add(obj); + current = obj; + } + + public void add(T obj) { + items.add(obj); + } + + public void addAll(Collection coll) { + items.addAll(coll); + } + + public void clear() { + items.clear(); + } + + public boolean contains(T obj) { + return items.contains(obj); + } + + public GenericSet copy() { + GenericSet set = new GenericSet(items); + set.current = current; + set.currID = currID; + set.random = random; + return set; + } + + public T getCurrent() { + return current; + } + + public ArrayList getItems() { + return items; + } + + public Iterator iterator() { + return items.iterator(); + } + + public T pickRandom() { + currID = MathUtils.random(random, items.size()); + current = items.get(currID); + return current; + } + + public T pickRandomUnique() { + int size = items.size(); + if (size > 1) { + int newID = currID; + while (newID == currID) { + newID = MathUtils.random(random, size); + } + currID = newID; + } else { + currID = 0; + } + current = items.get(currID); + return current; + } + + public void setRandom(Random rnd) { + random = rnd; + } +} diff --git a/java/Mekstension/tools/toxi/util/datatypes/IntegerRange.java b/java/Mekstension/tools/toxi/util/datatypes/IntegerRange.java new file mode 100644 index 0000000..eb9520e --- /dev/null +++ b/java/Mekstension/tools/toxi/util/datatypes/IntegerRange.java @@ -0,0 +1,40 @@ +package toxi.util.datatypes; + +import java.util.Random; + +import toxi.math.MathUtils; + +public class IntegerRange { + + public int min, max; + public int current; + + protected Random random = new Random(); + + public IntegerRange(int min, int max) { + this.min = min; + this.max = max; + } + + public void setRandom(Random rnd) { + random = rnd; + } + + public int pickRandom() { + current = MathUtils.random(min, max); + return current; + } + + public int getCurrent() { + return current; + } + + @Override + public String toString() { + return "IntegerRange: " + min + " -> " + max; + } + + public boolean isValueInRange(int val) { + return val >= min && val < max; + } +} diff --git a/java/Mekstension/tools/toxi/util/datatypes/IntegerSet.java b/java/Mekstension/tools/toxi/util/datatypes/IntegerSet.java new file mode 100644 index 0000000..94590f3 --- /dev/null +++ b/java/Mekstension/tools/toxi/util/datatypes/IntegerSet.java @@ -0,0 +1,55 @@ +package toxi.util.datatypes; + +import java.util.Random; + +import toxi.math.MathUtils; + +public class IntegerSet { + + public int[] items; + public int currID = -1; + public int current; + + private Random random = new Random(); + + public IntegerSet(int[] items) { + this.items = items; + pickRandom(); + } + + public void setRandom(Random rnd) { + random = rnd; + } + + public int pickRandom() { + currID = MathUtils.random(random, items.length); + current = items[currID]; + return current; + } + + public int pickRandomUnique() { + if (items.length > 1) { + int newID = currID; + while (newID == currID) { + newID = MathUtils.random(random, items.length); + } + currID = newID; + } else { + currID = 0; + } + current = items[currID]; + return current; + } + + public int getCurrent() { + return current; + } + + public boolean contains(int value) { + for (int i = 0; i < items.length; i++) { + if (items[i] == value) + return true; + } + return false; + } +} diff --git a/java/Mekstension/tools/toxi/util/datatypes/SingletonRegistry.java b/java/Mekstension/tools/toxi/util/datatypes/SingletonRegistry.java new file mode 100644 index 0000000..2f51651 --- /dev/null +++ b/java/Mekstension/tools/toxi/util/datatypes/SingletonRegistry.java @@ -0,0 +1,60 @@ +package toxi.util.datatypes; + +import java.util.HashMap; +import java.util.logging.Logger; + +/** + * Implements a registry for dynamic singleton management. Use this registry + * instead of using "new" to enforce singletons of any class with a visible + * default constructor. The registry itself is implemented as singleton. + */ +public class SingletonRegistry { + + /** + * The singleton instance of the registry itself. + */ + public static SingletonRegistry REGISTRY = new SingletonRegistry(); + + private static HashMap map = new HashMap(); + + private static Logger logger = Logger.getLogger(SingletonRegistry.class + .getName()); + + protected SingletonRegistry() { + } + + /** + * Alternative, more conventional accessor to the singleton instance of the + * registry itself. + * + * @return registry instance + */ + public static SingletonRegistry getRegistry() { + return REGISTRY; + } + + /** + * Creates or returns an instance of the class requested by name. + * + * @param className + * @return class singleton instance + */ + public static synchronized Object getInstanceOf(String className) { + Object instance = map.get(className); + if (instance != null) { + return instance; + } + try { + instance = Class.forName(className).newInstance(); + logger.info("Created singleton: " + instance); + } catch (ClassNotFoundException cnf) { + logger.severe("Couldn't find class: " + className); + } catch (InstantiationException ie) { + logger.severe("Couldn't instantiate the class: " + className); + } catch (IllegalAccessException ia) { + logger.severe("Couldn't access class: " + className); + } + map.put(className, instance); + return instance; + } +} diff --git a/java/Mekstension/tools/toxi/util/datatypes/TypedProperties.java b/java/Mekstension/tools/toxi/util/datatypes/TypedProperties.java new file mode 100644 index 0000000..82bda09 --- /dev/null +++ b/java/Mekstension/tools/toxi/util/datatypes/TypedProperties.java @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2006-2008 Karsten Schmidt + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * http://creativecommons.org/licenses/LGPL/2.1/ + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package toxi.util.datatypes; + +import java.io.FileInputStream; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.logging.Logger; + +/** + * Convenience wrapper providing typed access to Java {@link Properties} files. + * + * @author toxi + * + */ + +@SuppressWarnings("serial") +public class TypedProperties extends Properties { + + public static final String DELIM = "\t\n\r\f\u00A0,"; + + private static final Logger logger = Logger.getLogger(TypedProperties.class + .getName()); + + /** + * Attempts to load properties from the specified (absolute) file path (In + * Processing use sketchPath() or dataPath() to build absolute path). + * + * @param path + * config file + * @return true, if successful. + */ + public boolean load(String path) { + try { + load(new FileInputStream(path)); + return true; + } catch (Exception e) { + logger.warning("error opening config file: " + path); + return false; + } + } + + /** + * Returns a property as boolean. + * + * @param id + * property name + * @param defaultState + * @return prop value + */ + public boolean getBoolean(String id, boolean defaultState) { + return Boolean.parseBoolean(getProperty(id, "" + defaultState)); + } + + /** + * Returns a property as integer. + * + * @param id + * property name + * @param defaultValue + * @return prop value + */ + public int getInt(String id, int defaultValue) { + return Integer.parseInt(getProperty(id, "" + defaultValue)); + } + + /** + * Returns a hexadecimal property as integer + * + * @param id + * prop name + * @param defaultValue + * @return prop value + */ + public int getHexInt(String id, int defaultValue) { + return Integer.parseInt(getProperty(id, Integer + .toHexString(defaultValue)), 16); + } + + /** + * Returns a property as float. + * + * @param id + * @param defaultValue + * @return prop value + */ + public float getFloat(String id, float defaultValue) { + return Float.parseFloat(getProperty(id, "" + defaultValue)); + } + + /** + * Shorthand wrapper for {{@link #getIntArray(String, int[])} automatically + * supplying an empty int[] array as default value. + * + * @param id + * @return prop values as array + */ + public int[] getIntArray(String id) { + return getIntArray(id, new int[0]); + } + + /** + * Returns a comma delimited property value as int[] array. Non-integer + * items will be ignored. + * + * @param id + * prop name + * @return prop items as array + */ + public int[] getIntArray(String id, int[] defaultArray) { + StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""), + DELIM); + int pieces[] = new int[tokenizer.countTokens()]; + int index = 0; + while (tokenizer.hasMoreTokens()) { + try { + pieces[index] = Integer.parseInt(tokenizer.nextToken()); + index++; + } catch (NumberFormatException e) { + // ignore non-integer items + } + } + if (index > 0) { + int[] result = new int[index]; + System.arraycopy(pieces, 0, result, 0, index); + return result; + } else + return defaultArray; + } + + /** + * Shorthand wrapper for {@link #getFloatArray(String, float[])} + * automatically supplying an empty float[] array as default value. + * + * @param id + * @return prop values as array + */ + public float[] getFloatArray(String id) { + return getFloatArray(id, new float[0]); + } + + /** + * Returns a comma delimited property value as float[] array. + * + * @param id + * prop name + * @return prop items as array + */ + public float[] getFloatArray(String id, float[] defaultArray) { + StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""), + DELIM); + float pieces[] = new float[tokenizer.countTokens()]; + int index = 0; + while (tokenizer.hasMoreTokens()) { + try { + pieces[index] = Float.parseFloat(tokenizer.nextToken()); + index++; + } catch (NumberFormatException e) { + // ignore NaN items + } + } + if (index > 0) { + float[] result = new float[index]; + System.arraycopy(pieces, 0, result, 0, index); + return result; + } else + return defaultArray; + } + + /** + * Shorthand wrapper for {@link #getByteArray(String, byte[])} automatically + * supplying an empty byte[] as default value. + * + * @param id + * @return prop values as array + */ + public byte[] getByteArray(String id) { + return getByteArray(id, new byte[0]); + } + + /** + * Returns a comma delimited property value as byte[] array. Non-byte values + * will be ignored. + * + * @param id + * prop name + * @return prop values as array + */ + public byte[] getByteArray(String id, byte[] defaultArray) { + StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""), + DELIM); + byte[] pieces = new byte[tokenizer.countTokens()]; + int index = 0; + while (tokenizer.hasMoreTokens()) { + try { + pieces[index] = Byte.parseByte(tokenizer.nextToken()); + index++; + } catch (NumberFormatException e) { + // ignore non-integer items + } + } + if (index > 0) { + byte[] result = new byte[index]; + System.arraycopy(pieces, 0, result, 0, index); + return result; + } else + return defaultArray; + } + + public String[] getStringArray(String id) { + return getStringArray(id, new String[0]); + } + + public String[] getStringArray(String id, String[] defaultArray) { + StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""), + DELIM); + int index = 0; + String[] pieces = null; + while (tokenizer.hasMoreTokens()) { + if (pieces == null) { + pieces = new String[tokenizer.countTokens()]; + } + String token = tokenizer.nextToken(); + if (token.length() > 0) { + pieces[index++] = token; + } + } + if (index > 0) { + String[] result = new String[index]; + System.arraycopy(pieces, 0, result, 0, index); + return result; + } else + return defaultArray; + } +} \ No newline at end of file