--- /dev/null
+ 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
+
+
+
+
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/core"/>
+ <classpathentry kind="lib" path="library/jogl.jar" sourcepath="jogl-src.zip">
+ <attributes>
+ <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="opengl/library"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="lib" path="library/gluegen-rt.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+ <name>opengl</name>\r
+ <comment></comment>\r
+ <projects>\r
+ </projects>\r
+ <buildSpec>\r
+ <buildCommand>\r
+ <name>org.eclipse.jdt.core.javabuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ </buildSpec>\r
+ <natures>\r
+ <nature>org.eclipse.jdt.core.javanature</nature>\r
+ </natures>\r
+</projectDescription>\r
--- /dev/null
+#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
--- /dev/null
+#Thu Jan 10 10:50:38 PST 2008
+eclipse.preferences.version=1
+formatter_profile=_fry
+formatter_settings_version=11
--- /dev/null
+# 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
--- /dev/null
+/* -*- 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * When exporting applets, the JOGL Applet Launcher is used. More information
+ * about the launcher can be found at its <A HREF="http://download.java.net/media/jogl/builds/nightly/javadoc_public/com/sun/opengl/util/JOGLAppletLauncher.html">documentation page</A>.
+ */
+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<numSegments-1; i++) {
+ gl.glPushMatrix();
+ gl.glTranslatef(x[i], y[i], z);
+ float r = w[i]/2f;
+ gl.glScalef(r, r, r);
+ gl.glColor4f(1, 1, 1, 150.0/255.0);
+ gl.glCallList(ellipseList);
+ gl.glScalef(0.5, 0.5, 0.5);
+ gl.glColor4f(1, 1, 1, 50.0/255.0);
+ gl.glCallList(ellipseList);
+ gl.glPopMatrix();
+ }
+ pgl.endGL();
+ */
+
+
+ //public void arc(float a, float b, float c, float d,
+ // float start, float stop)
+
+
+ //protected void arcImpl(float x, float y, float w, float h,
+ // float start, float stop)
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // BOX
+
+ // TODO P3D overrides box to turn on triangle culling, but that's a waste
+ // for OpenGL. Also could just use the cube method from GL or GLUT.
+
+
+ //public void box(float size)
+
+
+ //public void box(float w, float h, float d) // P3D
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SPHERE
+
+ // TODO P3D overrides sphere to turn on triangle culling, but that's a waste
+ // for OpenGL. Also could just use the cube method from GL or GLUT.
+
+
+ //public void sphereDetail(int res)
+
+
+ //public void sphereDetail(int ures, int vres)
+
+
+ //public void sphere(float r)
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // BEZIER
+
+
+ //public float bezierPoint(float a, float b, float c, float d, float t)
+
+
+ //public float bezierTangent(float a, float b, float c, float d, float t)
+
+
+ //public void bezierDetail(int detail)
+
+
+ //public void bezier(float x1, float y1,
+ // float x2, float y2,
+ // float x3, float y3,
+ // float x4, float y4)
+
+
+ //public void bezier(float x1, float y1, float z1,
+ // float x2, float y2, float z2,
+ // float x3, float y3, float z3,
+ // float x4, float y4, float z4)
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // CATMULL-ROM CURVES
+
+
+ //public float curvePoint(float a, float b, float c, float d, float t)
+
+
+ //public float curveTangent(float a, float b, float c, float d, float t)
+
+
+ //public void curveDetail(int detail)
+
+
+ //public void curveTightness(float tightness)
+
+
+ //public void curve(float x1, float y1,
+ // float x2, float y2,
+ // float x3, float y3,
+ // float x4, float y4)
+
+
+ //public void curve(float x1, float y1, float z1,
+ // float x2, float y2, float z2,
+ // float x3, float y3, float z3,
+ // float x4, float y4, float z4)
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SMOOTH
+
+
+ public void smooth() {
+ smooth = true;
+ if (hints[DISABLE_OPENGL_2X_SMOOTH]) {
+ //gl.glEnable(GL.GL_MULTISAMPLE);
+ gl.glEnable(GL.GL_POINT_SMOOTH);
+ gl.glEnable(GL.GL_LINE_SMOOTH);
+ gl.glEnable(GL.GL_POLYGON_SMOOTH);
+ }
+ }
+
+
+ public void noSmooth() {
+ smooth = false;
+ if (hints[DISABLE_OPENGL_2X_SMOOTH]) {
+ //gl.glDisable(GL.GL_MULTISAMPLE);
+ gl.glDisable(GL.GL_POINT_SMOOTH);
+ gl.glDisable(GL.GL_LINE_SMOOTH);
+ gl.glDisable(GL.GL_POLYGON_SMOOTH);
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // IMAGES
+
+
+ //public void imageMode(int mode)
+
+
+ //public void image(PImage image, float x, float y)
+
+
+ //public void image(PImage image, float x, float y, float c, float d)
+
+
+ //public void image(PImage image,
+ // float a, float b, float c, float d,
+ // int u1, int v1, int u2, int v2)
+
+
+ //protected void imageImpl(PImage image,
+ // float x1, float y1, float x2, float y2,
+ // int u1, int v1, int u2, int v2)
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SHAPE
+
+
+ //public void shapeMode(int mode)
+
+
+ //public void shape(PShape shape)
+
+
+ //public void shape(PShape shape, float x, float y)
+
+
+ //public void shape(PShape shape, float x, float y, float c, float d)
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // TEXT SETTINGS
+
+
+ //public void textAlign(int align)
+
+
+ //public void textAlign(int alignX, int alignY)
+
+
+ public float textAscent() {
+ Font font = textFont.getFont();
+ if ((textMode != SHAPE) || (font == null)) {
+ return super.textAscent();
+ }
+ FontMetrics metrics = parent.getFontMetrics(font);
+ return metrics.getAscent();
+ }
+
+
+ public float textDescent() {
+ Font font = textFont.getFont();
+ if ((textMode != SHAPE) || (font == null)) {
+ return super.textDescent();
+ }
+ FontMetrics metrics = parent.getFontMetrics(font);
+ return metrics.getDescent();
+ }
+
+
+ public void textFont(PFont which) {
+ super.textFont(which);
+
+ if (textMode == SHAPE) {
+ if (textFont.findFont() == null) {
+ showWarning("Cannot use " + which.name + " as with textMode(SHAPE) " +
+ "because its native equivalent cannot be found.");
+ }
+ }
+ }
+
+
+ //public void textFont(PFont which, float size)
+
+
+ //public void textLeading(float leading)
+
+
+// public void textMode(int mode) {
+// if (mode == SHAPE) {
+// textMode = SHAPE;
+//
+// } else {
+// // if not SHAPE mode, then pass off to the PGraphics.textMode()
+// // which is built for error handling (but objects to SHAPE).
+// super.textMode(mode);
+// }
+// }
+
+
+ protected boolean textModeCheck(int mode) {
+ return (textMode == MODEL) || (textMode == SCREEN) || (textMode == SHAPE);
+ }
+
+
+ /**
+ * Same as parent, but override for native version of the font.
+ * <p/>
+ * 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.
+ * <p/>
+ * <EM>No attempt has been made to optimize this code</EM>
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <P>
+ * 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:
+ * <A HREF="http://dev.processing.org/bugs/show_bug.cgi?id=941">Bug 941</A>,
+ * <A HREF="http://dev.processing.org/bugs/show_bug.cgi?id=942">Bug 942</A>.
+ */
+ 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;
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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
--- /dev/null
+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<ArrayList<Vec3D>> shapeSegments = new ArrayList<ArrayList<Vec3D>>();
+ ArrayList <Vec3D> segments = new ArrayList <Vec3D> ();
+ 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<Vec3D> seg = new ArrayList<Vec3D>();
+ 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<Vec3D> seg = shapeSegments.get(j);
+ ArrayList<Vec3D> 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;
+ }
+}
--- /dev/null
+package shapes;
+
+import java.util.ArrayList;
+
+public class ShapeList extends ArrayList<Shape>
+{
+
+ private int currentIndex = 0;
+
+ public Shape next()
+ {
+ if (size() == 0) return null;
+ if (currentIndex++ >= size() - 1) {
+ currentIndex = 0;
+ }
+ return get(currentIndex);
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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<String> name = new ArrayList <String> ();
+ ArrayList<Integer> val = new ArrayList <Integer> ();
+
+ 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 ) );
+ }
+ }
+}
--- /dev/null
+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
--- /dev/null
+/*\r
+ * Some classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.color;\r
+\r
+import java.util.Comparator;\r
+\r
+/**\r
+ * Defines color component access criterias and associated {@link Comparator}s\r
+ * used to sort colors based on component values.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public class AccessCriteria {\r
+\r
+ public static final AccessCriteria HUE = new AccessCriteria(AccessMode.HSV,\r
+ 0);\r
+ public static final AccessCriteria SATURATION = new AccessCriteria(\r
+ AccessMode.HSV, 1);\r
+ public static final AccessCriteria BRIGHTNESS = new AccessCriteria(\r
+ AccessMode.HSV, 2);\r
+ public static final AccessCriteria RED = new AccessCriteria(AccessMode.RGB,\r
+ 0);\r
+ public static final AccessCriteria GREEN = new AccessCriteria(\r
+ AccessMode.RGB, 1);\r
+ public static final AccessCriteria BLUE = new AccessCriteria(\r
+ AccessMode.RGB, 2);\r
+ public static final AccessCriteria CYAN = new AccessCriteria(\r
+ AccessMode.CMYK, 0);\r
+ public static final AccessCriteria MAGENTA = new AccessCriteria(\r
+ AccessMode.CMYK, 1);\r
+ public static final AccessCriteria YELLOW = new AccessCriteria(\r
+ AccessMode.CMYK, 2);\r
+ public static final AccessCriteria BLACK = new AccessCriteria(\r
+ AccessMode.CMYK, 3);\r
+\r
+ public static final AccessCriteria ALPHA = new AccessCriteria(\r
+ AccessMode.ALPHA, 0);\r
+\r
+ public static final AccessCriteria LUMINANCE = new AccessCriteria(\r
+ AccessMode.DIRECT, 0, new LuminanceComparator());\r
+\r
+ protected final AccessMode mode;\r
+ protected final int component;\r
+ protected Comparator<TColor> comparator;\r
+\r
+ protected AccessCriteria(AccessMode mode, int compID) {\r
+ this(mode, compID, null);\r
+ }\r
+\r
+ protected AccessCriteria(AccessMode mode, int compID,\r
+ Comparator<TColor> comparator) {\r
+ this.mode = mode;\r
+ this.component = compID;\r
+ this.comparator = comparator;\r
+ }\r
+\r
+ /**\r
+ * @return the comparator associated with the criteria.\r
+ */\r
+ public Comparator<TColor> getComparator() {\r
+ if (comparator == null) {\r
+ switch (mode) {\r
+ case HSV:\r
+ comparator = new HSVComparator(component);\r
+ break;\r
+ case RGB:\r
+ comparator = new RGBComparator(component);\r
+ break;\r
+ case CMYK:\r
+ comparator = new CMYKComparator(component);\r
+ break;\r
+ case ALPHA:\r
+ comparator = new AlphaComparator();\r
+ break;\r
+ }\r
+ }\r
+ return comparator;\r
+ }\r
+\r
+ public int getComponent() {\r
+ return component;\r
+ }\r
+\r
+ public AccessMode getMode() {\r
+ return mode;\r
+ }\r
+}\r
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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;
+}
--- /dev/null
+/*\r
+ * Some classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.color;\r
+\r
+import java.util.Comparator;\r
+\r
+/**\r
+ * Compares 2 colors by their alpha value.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public class AlphaComparator implements Comparator<TColor> {\r
+\r
+ public int compare(TColor a, TColor b) {\r
+ return a.alpha < b.alpha ? -1 : a.alpha > b.alpha ? 1 : 0;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Some classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.color;\r
+\r
+import java.util.Comparator;\r
+\r
+/**\r
+ * Compares 2 colors by one of their CMYK component values.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public class CMYKComparator implements Comparator<TColor> {\r
+\r
+ private final int component;\r
+\r
+ public CMYKComparator(int comp) {\r
+ component = comp;\r
+ }\r
+\r
+ public int compare(TColor a, TColor b) {\r
+ return Float.compare(a.cmyk[component], b.cmyk[component]);\r
+ }\r
+\r
+}\r
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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);
+ }
+
+}
--- /dev/null
+/*\r
+ * Some classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.color;\r
+\r
+import java.util.Iterator;\r
+import java.util.TreeSet;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+/**\r
+ * This class can be used to calculate multi-color gradients with colors\r
+ * positioned along an imaginary line.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public class ColorGradient {\r
+\r
+ protected class GradPoint implements Comparable<GradPoint> {\r
+ float pos;\r
+ TColor color;\r
+\r
+ GradPoint(float p, TColor c) {\r
+ pos = p;\r
+ color = c;\r
+ }\r
+\r
+ public int compareTo(GradPoint p) {\r
+ if (Float.compare(p.pos, pos) == 0) {\r
+ return 0;\r
+ } else {\r
+ return pos < p.pos ? -1 : 1;\r
+ }\r
+ }\r
+ }\r
+\r
+ protected TreeSet<GradPoint> gradient;\r
+\r
+ protected float maxDither;\r
+\r
+ /**\r
+ * Constructs a new empty gradient.\r
+ */\r
+ public ColorGradient() {\r
+ gradient = new TreeSet<GradPoint>();\r
+ }\r
+\r
+ /**\r
+ * Adds a new color at specified position.\r
+ * \r
+ * @param p\r
+ * @param c\r
+ */\r
+ public void addColorAt(float p, TColor c) {\r
+ gradient.add(new GradPoint(p, c));\r
+ }\r
+\r
+ /**\r
+ * Calculates the gradient from specified position.\r
+ * \r
+ * @param pos\r
+ * @param width\r
+ * @return list of interpolated gradient colors\r
+ */\r
+ public ColorList calcGradient(float pos, int width) {\r
+ ColorList result = new ColorList();\r
+\r
+ if (gradient.size() == 0) {\r
+ return result;\r
+ }\r
+\r
+ float frac = 0;\r
+ GradPoint currPoint = null;\r
+ GradPoint nextPoint = null;\r
+ float endPos = pos + width;\r
+ // find 1st color needed, clamp start position to positive values only\r
+ for (GradPoint gp : gradient) {\r
+ if (gp.pos < pos) {\r
+ currPoint = gp;\r
+ }\r
+ }\r
+ boolean isPremature = currPoint == null;\r
+ TreeSet<GradPoint> activeGradient = null;\r
+ if (!isPremature) {\r
+ activeGradient = (TreeSet<GradPoint>) gradient.tailSet(currPoint);\r
+ } else {\r
+ // start position is before 1st gradient color, so use whole\r
+ // gradient\r
+ activeGradient = gradient;\r
+ currPoint = activeGradient.first();\r
+ }\r
+ float currWidth = 0;\r
+ Iterator<GradPoint> iter = activeGradient.iterator();\r
+ if (currPoint != activeGradient.last()) {\r
+ nextPoint = iter.next();\r
+ if (isPremature) {\r
+ currWidth = 1f / (currPoint.pos - pos);\r
+ } else {\r
+ if (nextPoint.pos - currPoint.pos > 0) {\r
+ currWidth = 1f / (nextPoint.pos - currPoint.pos);\r
+ }\r
+ }\r
+ }\r
+ while (pos < endPos) {\r
+ if (isPremature) {\r
+ frac = 1 - (currPoint.pos - pos) * currWidth;\r
+ } else {\r
+ frac = (pos - currPoint.pos) * currWidth;\r
+ }\r
+ // switch to next color?\r
+ if (frac > 1.0) {\r
+ currPoint = nextPoint;\r
+ isPremature = false;\r
+ if (iter.hasNext()) {\r
+ nextPoint = iter.next();\r
+ if (currPoint != activeGradient.last()) {\r
+ currWidth = 1f / (nextPoint.pos - currPoint.pos);\r
+ } else {\r
+ currWidth = 0;\r
+ }\r
+ frac = (pos - currPoint.pos) * currWidth;\r
+ }\r
+ }\r
+ if (currPoint != activeGradient.last()) {\r
+ float ditheredFrac = MathUtils.clip(frac\r
+ + MathUtils.normalizedRandom() * maxDither, 0f, 1f);\r
+ result.add(currPoint.color.getBlended(nextPoint.color,\r
+ ditheredFrac));\r
+ } else {\r
+ result.add(currPoint.color.copy());\r
+ }\r
+ pos++;\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * @return the maximum dither amount.\r
+ */\r
+ public float getMaxDither() {\r
+ return maxDither;\r
+ }\r
+\r
+ /**\r
+ * Sets the maximum dither amount. Setting this to values >0 will jitter the\r
+ * interpolated colors in the calculated gradient. The value range for this\r
+ * parameter is 0.0 (off) to 1.0 (100%).\r
+ * \r
+ * @param maxDither\r
+ */\r
+ public void setMaxDither(float maxDither) {\r
+ this.maxDither = MathUtils.clip(maxDither, 0f, 1f);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Some classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.color;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.Iterator;\r
+\r
+import toxi.color.theory.ColorTheoryRegistry;\r
+import toxi.color.theory.ColorTheoryStrategy;\r
+import toxi.math.MathUtils;\r
+\r
+/**\r
+ * A container class of concrete colors. ColorLists can be built manually and\r
+ * are also created when working with {@link ColorRange}s. The class has various\r
+ * methods to manipulate all colors in the list in parallel, as well as sort\r
+ * them by various criteria.\r
+ * \r
+ * @see ColorRange\r
+ * @see AccessCriteria\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public class ColorList implements Iterable<TColor> {\r
+\r
+ protected ArrayList<TColor> colors = new ArrayList<TColor>();\r
+\r
+ /**\r
+ * Factory method. Creates a new ColorList of colors sampled from the given\r
+ * ARGB image array.\r
+ * \r
+ * @param pixels\r
+ * int array of ARGB pixels\r
+ * @param num\r
+ * number of colors samples (clipped automatically to number of\r
+ * pixels in the image)\r
+ * @param uniqueOnly\r
+ * flag if only unique samples are to be taken (doesn't guarantee\r
+ * unique colors though)\r
+ * @return new color list\r
+ */\r
+ public static final ColorList createFromARGBArray(int[] pixels, int num,\r
+ boolean uniqueOnly) {\r
+ num = MathUtils.min(num, pixels.length);\r
+ int[] colors = new int[num];\r
+ int[] index = new int[num];\r
+ for (int i = 0; i < num; i++) {\r
+ int idx;\r
+ if (uniqueOnly) {\r
+ boolean isUnique = true;\r
+ do {\r
+ idx = MathUtils.random(pixels.length);\r
+ for (int j = 0; j < i; j++) {\r
+ if (index[j] == idx) {\r
+ isUnique = false;\r
+ break;\r
+ }\r
+ }\r
+ } while (!isUnique);\r
+ } else {\r
+ idx = MathUtils.random(pixels.length);\r
+ }\r
+ index[i] = idx;\r
+ colors[i] = pixels[idx];\r
+ }\r
+ return new ColorList(colors);\r
+ }\r
+\r
+ /**\r
+ * Factory method. Creates a new ColorList based on the given\r
+ * {@link ColorTheoryStrategy} instance and the given source color.\r
+ * \r
+ * @param strategy\r
+ * @param c\r
+ * @return new list\r
+ */\r
+ public static final ColorList createUsingStrategy(\r
+ ColorTheoryStrategy strategy, TColor c) {\r
+ return strategy.createListFromColor(c);\r
+ }\r
+\r
+ /**\r
+ * Factory method. Creates a ColorList based on the\r
+ * {@link ColorTheoryStrategy} name and the given source color.\r
+ * \r
+ * @param name\r
+ * @param c\r
+ * @return new color list or null, if the supplied strategy name is not\r
+ * mapped to a registered implementation.\r
+ */\r
+ public static final ColorList createUsingStrategy(String name, TColor c) {\r
+ ColorTheoryStrategy strategy = ColorTheoryRegistry\r
+ .getStrategyForName(name);\r
+ ColorList list = null;\r
+ if (strategy != null) {\r
+ list = strategy.createListFromColor(c);\r
+ }\r
+ return list;\r
+ }\r
+\r
+ /**\r
+ * Creates an empty list.\r
+ */\r
+ public ColorList() {\r
+\r
+ }\r
+\r
+ /**\r
+ * Creates a ColorList by wrapping the given ArrayList of colors. No copies\r
+ * of the given colors are created (shallow copy only).\r
+ * \r
+ * @param colors\r
+ */\r
+ public ColorList(ArrayList<TColor> colors) {\r
+ this.colors.addAll(colors);\r
+ }\r
+\r
+ /**\r
+ * Creates a deep copy of the given ColorList. Manipulating the new list\r
+ * does NOT change the colors of the original.\r
+ * \r
+ * @param list\r
+ * source list\r
+ */\r
+ public ColorList(ColorList list) {\r
+ for (TColor c : list) {\r
+ this.colors.add(c.copy());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Creates a new color list from the array of ARGB int values.\r
+ * \r
+ * @param argbArray\r
+ */\r
+ public ColorList(int[] argbArray) {\r
+ for (int c : argbArray) {\r
+ colors.add(TColor.newARGB(c));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Creates a color list with the supplied color as first entry.\r
+ * \r
+ * @param c\r
+ * color\r
+ */\r
+ public ColorList(TColor c) {\r
+ this.colors.add(c.copy());\r
+ }\r
+\r
+ /**\r
+ * Creates new ColorList from the given array of colors No copies of the\r
+ * given colors are created (shallow copy only).\r
+ * \r
+ * @param colorArray\r
+ */\r
+ public ColorList(TColor[] colorArray) {\r
+ for (TColor c : colorArray) {\r
+ colors.add(c);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Adds the given color to the list\r
+ * \r
+ * @param c\r
+ * @return itself\r
+ */\r
+ public ColorList add(TColor c) {\r
+ colors.add(c);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Adds all entries of the TColor collection to the list (shallow copy only,\r
+ * manipulating the new list will modify the original colors).\r
+ * \r
+ * @param collection\r
+ * @return itself\r
+ */\r
+ public ColorList addAll(Collection<TColor> collection) {\r
+ colors.addAll(collection);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Adjusts the brightness component of all list colors by the given amount.\r
+ * \r
+ * @param step\r
+ * adjustment value\r
+ * @return itself\r
+ */\r
+ public ColorList adjustBrightness(float step) {\r
+ for (TColor c : colors) {\r
+ c.lighten(step);\r
+ }\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Adjusts the saturation component of all list colors by the given amount.\r
+ * \r
+ * @param step\r
+ * adjustment value\r
+ * @return itself\r
+ */\r
+ public ColorList adjustSaturation(float step) {\r
+ for (TColor c : colors) {\r
+ c.saturate(step);\r
+ }\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Sorts the list based on two criteria to create clusters/segments within\r
+ * the list.\r
+ * \r
+ * @param clusterCriteria\r
+ * main sort criteria\r
+ * @param subClusterCriteria\r
+ * secondary sort criteria\r
+ * @param numClusters\r
+ * number of clusters\r
+ * @param isReversed\r
+ * true, if reversed sort\r
+ * @return itself\r
+ */\r
+ public ColorList clusterSort(AccessCriteria clusterCriteria,\r
+ AccessCriteria subClusterCriteria, int numClusters,\r
+ boolean isReversed) {\r
+ ArrayList<TColor> sorted = new ArrayList<TColor>(colors);\r
+ Collections.sort(sorted, clusterCriteria.getComparator());\r
+ Collections.reverse(sorted);\r
+ ArrayList<TColor> clusters = new ArrayList<TColor>();\r
+\r
+ float d = 1;\r
+ int i = 0;\r
+ int num = sorted.size();\r
+ for (int j = 0; j < num; j++) {\r
+ TColor c = sorted.get(j);\r
+ if (c.getComponentValue(clusterCriteria) < d) {\r
+ ArrayList<TColor> slice = new ArrayList<TColor>();\r
+ slice.addAll(sorted.subList(i, j));\r
+ Collections.sort(slice, subClusterCriteria.getComparator());\r
+ clusters.addAll(slice);\r
+ d -= 1.0f / numClusters;\r
+ i = j;\r
+ }\r
+ }\r
+ ArrayList<TColor> slice = new ArrayList<TColor>();\r
+ slice.addAll(sorted.subList(i, sorted.size()));\r
+ Collections.sort(slice, subClusterCriteria.getComparator());\r
+ clusters.addAll(slice);\r
+ if (isReversed) {\r
+ Collections.reverse(clusters);\r
+ }\r
+ colors = clusters;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Switches all list colors to their complementary color.\r
+ * \r
+ * @return itself\r
+ */\r
+ public ColorList complement() {\r
+ for (TColor c : colors) {\r
+ c.complement();\r
+ }\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Checks if the given color is part of the list. Check is done by value,\r
+ * not instance.\r
+ * \r
+ * @param color\r
+ * @return true, if the color is present.\r
+ */\r
+ public boolean contains(TColor color) {\r
+ for (TColor c : colors) {\r
+ if (c.equals(color)) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Returns the color at the given index.\r
+ * \r
+ * @param i\r
+ * @return color\r
+ */\r
+ public TColor get(int i) {\r
+ return colors.get(i);\r
+ }\r
+\r
+ /**\r
+ * Calculates and returns the average color of the list.\r
+ * \r
+ * @return average color or null, if there're no entries yet.\r
+ */\r
+ public TColor getAverage() {\r
+ float r = 0;\r
+ float g = 0;\r
+ float b = 0;\r
+ float a = 0;\r
+ for (TColor c : colors) {\r
+ r += c.rgb[0];\r
+ g += c.rgb[1];\r
+ b += c.rgb[2];\r
+ a += c.alpha;\r
+ }\r
+ int num = colors.size();\r
+ if (num > 0) {\r
+ return TColor.newRGBA(r / num, g / num, b / num, a / num);\r
+ } else {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Creates a new ColorList by blending all colors in the list with each\r
+ * other (successive indices only)\r
+ * \r
+ * @param amount\r
+ * blend amount\r
+ * @return new color list\r
+ */\r
+ public ColorList getBlended(float amount) {\r
+ TColor[] clrs = new TColor[colors.size()];\r
+ for (int i = 0; i < clrs.length; i++) {\r
+ TColor c = colors.get(i > 0 ? i - 1 : clrs.length - 1);\r
+ clrs[i] = colors.get(i).getBlended(c, amount);\r
+ }\r
+ return new ColorList(clrs);\r
+ }\r
+\r
+ /**\r
+ * Finds and returns the darkest color of the list.\r
+ * \r
+ * @return darkest color or null if there're no entries yet.\r
+ */\r
+ public TColor getDarkest() {\r
+ TColor darkest = null;\r
+ float minBrightness = Float.MAX_VALUE;\r
+ for (TColor c : colors) {\r
+ float luma = c.luminance();\r
+ if (luma < minBrightness) {\r
+ darkest = c;\r
+ minBrightness = luma;\r
+ }\r
+ }\r
+ return darkest;\r
+ }\r
+\r
+ /**\r
+ * Finds and returns the lightest (luminance) color of the list.\r
+ * \r
+ * @return lightest color or null, if there're no entries yet.\r
+ */\r
+ public TColor getLightest() {\r
+ TColor lightest = null;\r
+ float maxBrightness = Float.MIN_VALUE;\r
+ for (TColor c : colors) {\r
+ float luma = c.luminance();\r
+ if (luma > maxBrightness) {\r
+ lightest = c;\r
+ maxBrightness = luma;\r
+ }\r
+ }\r
+ return lightest;\r
+ }\r
+\r
+ /**\r
+ * Returns a reversed copy of the current list.\r
+ * \r
+ * @return reversed copy of the list\r
+ */\r
+ public ColorList getReverse() {\r
+ return new ColorList(colors).reverse();\r
+ }\r
+\r
+ /**\r
+ * Inverts all colors in the list.\r
+ * \r
+ * @return itself\r
+ */\r
+ public ColorList invert() {\r
+ for (TColor c : colors) {\r
+ c.invert();\r
+ }\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Returns an iterator over the internal list. This means the list can be\r
+ * accessed via standard Iterator loops.\r
+ * \r
+ * @return list iterator\r
+ */\r
+ public Iterator<TColor> iterator() {\r
+ return colors.iterator();\r
+ }\r
+\r
+ /**\r
+ * Reverses the current order of the list.\r
+ * \r
+ * @return itself\r
+ */\r
+ public ColorList reverse() {\r
+ Collections.reverse(colors);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Rotates the hues of all colors in the list by the given amount.\r
+ * \r
+ * @param theta\r
+ * rotation angle in radians\r
+ * @return itself\r
+ */\r
+ public ColorList rotateRYB(float theta) {\r
+ return rotateRYB(MathUtils.degrees(theta));\r
+ }\r
+\r
+ /**\r
+ * Rotates the hues of all colors in the list by the given amount.\r
+ * \r
+ * @param angle\r
+ * rotation angle in degrees\r
+ * @return itself\r
+ */\r
+ public ColorList rotateRYB(int angle) {\r
+ for (TColor c : colors) {\r
+ c.rotateRYB(angle);\r
+ }\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * @return the number of colors in the list\r
+ */\r
+ public float size() {\r
+ return colors.size();\r
+ }\r
+\r
+ /**\r
+ * Convenience method. Sorts the list by hue.\r
+ * \r
+ * @return itself\r
+ */\r
+ public ColorList sort() {\r
+ return sortByCriteria(AccessCriteria.HUE, false);\r
+ }\r
+\r
+ /**\r
+ * Sorts the list using the given comparator.\r
+ * \r
+ * @param comp\r
+ * comparator\r
+ * @param isReversed\r
+ * true, if reversed sort\r
+ * @return itself\r
+ */\r
+ public ColorList sortByComparator(Comparator<TColor> comp,\r
+ boolean isReversed) {\r
+ Collections.sort(colors, comp);\r
+ if (isReversed) {\r
+ Collections.reverse(colors);\r
+ }\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Sorts the list using the given {@link AccessCriteria}.\r
+ * \r
+ * @param criteria\r
+ * sort criteria\r
+ * @param isReversed\r
+ * true, if reversed sort\r
+ * @return itself\r
+ */\r
+ public ColorList sortByCriteria(AccessCriteria criteria, boolean isReversed) {\r
+ Comparator<TColor> comparator = criteria.getComparator();\r
+ if (comparator != null) {\r
+ return sortByComparator(comparator, isReversed);\r
+ } else {\r
+ return this;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Sorts the list by relative distance to each predecessor, starting with\r
+ * the darkest color in the list.\r
+ * \r
+ * @param isReversed\r
+ * true, if list is to be sorted in reverse.\r
+ * @return itself\r
+ */\r
+ public ColorList sortByDistance(boolean isReversed) {\r
+ return sortByDistance(new HSVDistanceProxy(), isReversed);\r
+ }\r
+\r
+ /**\r
+ * Sorts the list by relative distance to each predecessor, starting with\r
+ * the darkest color in the list.\r
+ * \r
+ * @param isReversed\r
+ * true, if list is to be sorted in reverse.\r
+ * @return itself\r
+ */\r
+ public ColorList sortByDistance(DistanceProxy proxy, boolean isReversed) {\r
+ if (colors.size() == 0) {\r
+ return this;\r
+ }\r
+\r
+ TColor root = getDarkest();\r
+\r
+ // Remove the darkest color from the stack,\r
+ // put it in the sorted list as starting element.\r
+ ArrayList<TColor> stack = new ArrayList<TColor>(colors);\r
+ stack.remove(root);\r
+ ArrayList<TColor> sorted = new ArrayList<TColor>(colors.size());\r
+ sorted.add(root);\r
+\r
+ // Now find the color in the stack closest to that color.\r
+ // Take this color from the stack and add it to the sorted list.\r
+ // Now find the color closest to that color, etc.\r
+ int sortedCount = 0;\r
+ while (stack.size() > 1) {\r
+ TColor closest = stack.get(0);\r
+ TColor lastSorted = sorted.get(sortedCount);\r
+ float distance = proxy.distanceBetween(closest, lastSorted);\r
+ for (int i = stack.size() - 1; i >= 0; i--) {\r
+ TColor c = stack.get(i);\r
+ float d = proxy.distanceBetween(c, lastSorted);\r
+ if (d < distance) {\r
+ closest = c;\r
+ distance = d;\r
+ }\r
+ }\r
+ stack.remove(closest);\r
+ sorted.add(closest);\r
+ sortedCount++;\r
+ }\r
+ sorted.add(stack.get(0));\r
+ if (isReversed) {\r
+ Collections.reverse(sorted);\r
+ }\r
+ colors = sorted;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Sorts the list by proximity to the given target color (using RGB distance\r
+ * metrics).\r
+ * \r
+ * @see #sortByProximityTo(TColor, DistanceProxy, boolean)\r
+ * @param target\r
+ * color\r
+ * @param isReversed\r
+ * true, if reverse sorted\r
+ * @return sorted list\r
+ */\r
+ public ColorList sortByProximityTo(TColor target, boolean isReversed) {\r
+ return sortByProximityTo(target, new RGBDistanceProxy(), isReversed);\r
+ }\r
+\r
+ /**\r
+ * Sorts the list by proximity to the given target color using the given\r
+ * {@link DistanceProxy} implementation.\r
+ * \r
+ * @param target\r
+ * color\r
+ * @param proxy\r
+ * distance metrics\r
+ * @param isReversed\r
+ * true, if reverse sorted\r
+ * @return sorted list\r
+ */\r
+ public ColorList sortByProximityTo(TColor target, DistanceProxy proxy,\r
+ boolean isReversed) {\r
+ return sortByComparator(new ProximityComparator(target, proxy),\r
+ isReversed);\r
+ }\r
+}\r
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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<String, ColorRange> PRESETS = new HashMap<String, ColorRange>();
+
+ private static int UNTITLED_ID = 1;
+
+ protected GenericSet<FloatRange> hueConstraint;
+
+ protected GenericSet<FloatRange> saturationConstraint;
+ protected GenericSet<FloatRange> brightnessConstraint;
+ protected GenericSet<FloatRange> 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<FloatRange>(hue != null ? hue
+ : new FloatRange(0, 1));
+ saturationConstraint = new GenericSet<FloatRange>(sat != null ? sat
+ : new FloatRange(0, 1));
+ brightnessConstraint = new GenericSet<FloatRange>(bri != null ? bri
+ : new FloatRange(0, 1));
+ alphaConstraint = new GenericSet<FloatRange>(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<FloatRange>(new FloatRange(
+ hue, hue));
+ range.alphaConstraint = new GenericSet<FloatRange>(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<FloatRange> rangeSet) {
+ boolean isValid = false;
+ for (FloatRange r : rangeSet) {
+ isValid |= r.isValueInRange(val);
+ }
+ return isValid;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Some classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.color;\r
+\r
+import java.util.ArrayList;\r
+import java.util.StringTokenizer;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+/**\r
+ * A ColorTheme is a weighted collection of {@link ColorRange}s used to define\r
+ * custom palettes with a certain balance between individual colors/shades. New\r
+ * theme parts can be added via textual descriptors referring to one of the\r
+ * preset {@link ColorRange}s and/or {@link NamedColor}s: e.g.\r
+ * "warm springgreen". For each theme part a weight has to be specified. The\r
+ * magnitude of the weight value is irrelevant and is only important in relation\r
+ * to the weights of other theme parts. For example: Theme part A with a weight\r
+ * of 0.5 will only have 1/20 of the weight of theme part B with a weight of\r
+ * 5.0...\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public class ColorTheme {\r
+\r
+ class ThemePart {\r
+ ColorRange range;\r
+ TColor col;\r
+ float weight;\r
+\r
+ ThemePart(ColorRange range, TColor col, float weight) {\r
+ this.range = range;\r
+ this.col = col;\r
+ this.weight = weight;\r
+ }\r
+\r
+ public TColor getColor() {\r
+ return range.getColor(col, ColorRange.DEFAULT_VARIANCE);\r
+ }\r
+ }\r
+\r
+ protected String name;\r
+ protected ArrayList<ThemePart> parts = new ArrayList<ThemePart>();\r
+\r
+ protected float weightedSum;\r
+\r
+ public ColorTheme(String name) {\r
+ this.name = name;\r
+ }\r
+\r
+ public ColorTheme addRange(ColorRange range, TColor col, float weight) {\r
+ parts.add(new ThemePart(range, col, weight));\r
+ weightedSum += weight;\r
+ return this;\r
+ }\r
+\r
+ public ColorTheme addRange(String descriptor, float weight) {\r
+ StringTokenizer st = new StringTokenizer(descriptor, " ,");\r
+ TColor col = null;\r
+ ColorRange range = null;\r
+ while (st.hasMoreTokens()) {\r
+ String item = st.nextToken();\r
+ if (ColorRange.getPresetForName(item) != null) {\r
+ range = ColorRange.getPresetForName(item);\r
+ } else if (NamedColor.getForName(item) != null) {\r
+ col = NamedColor.getForName(item);\r
+ }\r
+ }\r
+ if (range != null) {\r
+ addRange(range, col, weight);\r
+ }\r
+ return this;\r
+ }\r
+\r
+ public TColor getColor() {\r
+ float rnd = MathUtils.random(1f);\r
+ for (ThemePart t : parts) {\r
+ float currWeight = t.weight / weightedSum;\r
+ if (currWeight >= rnd) {\r
+ return t.getColor();\r
+ }\r
+ rnd -= currWeight;\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Creates a {@link ColorList} of {@link TColor}s based on the theme's\r
+ * ranges and balance defined by their weights\r
+ * \r
+ * @param num\r
+ * number of colors to create\r
+ * @return new list\r
+ */\r
+ public ColorList getColors(int num) {\r
+ ColorList list = new ColorList();\r
+ for (int i = 0; i < num; i++) {\r
+ list.add(getColor());\r
+ }\r
+ return list;\r
+ }\r
+\r
+ /**\r
+ * @return the theme's name\r
+ */\r
+ public String getName() {\r
+ return name;\r
+ }\r
+}\r
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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);
+}
--- /dev/null
+/*\r
+ * Some classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.color;\r
+\r
+import java.util.Comparator;\r
+\r
+/**\r
+ * Compares 2 colors by one of their HSV component values.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public class HSVComparator implements Comparator<TColor> {\r
+\r
+ private final int component;\r
+\r
+ public HSVComparator(int comp) {\r
+ component = comp;\r
+ }\r
+\r
+ public int compare(TColor a, TColor b) {\r
+ return Float.compare(a.hsv[component], b.hsv[component]);\r
+ }\r
+\r
+}\r
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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);
+ }
+}
--- /dev/null
+/*\r
+ * Some classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.color;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+/**\r
+ * This class defines color hues and allows them to be access by name. There\r
+ * are also methods to check if a hue is one of the 7 primary hues (rainbow) or\r
+ * to find the closest defined hue for a given color.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public class Hue {\r
+\r
+ protected static final HashMap<String, Hue> namedHues = new HashMap<String, Hue>();\r
+ protected static final ArrayList<Hue> primaryHues = new ArrayList<Hue>();\r
+\r
+ public static final Hue RED = new Hue("red", 0, true);\r
+ public static final Hue ORANGE = new Hue("orange", 30 / 360.0f, true);\r
+ public static final Hue YELLOW = new Hue("yellow", 60 / 360.0f, true);\r
+ public static final Hue LIME = new Hue("lime", 90 / 360.0f);\r
+ public static final Hue GREEN = new Hue("green", 120 / 360.0f, true);\r
+ public static final Hue TEAL = new Hue("teal", 150 / 360.0f);\r
+ public static final Hue CYAN = new Hue("cyan", 180 / 360.0f);\r
+ public static final Hue AZURE = new Hue("azure", 210 / 360.0f);\r
+ public static final Hue BLUE = new Hue("blue", 240 / 360.0f, true);\r
+ public static final Hue INDIGO = new Hue("indigo", 270 / 360.0f);\r
+ public static final Hue PURPLE = new Hue("purple", 300 / 360.0f, true);\r
+ public static final Hue PINK = new Hue("pink", 330 / 360.0f, true);\r
+\r
+ /**\r
+ * Tolerance value for checking if a given hue is primary (default 0.01)\r
+ */\r
+ public static float PRIMARY_VARIANCE = 0.01f;\r
+\r
+ protected String name;\r
+ protected float hue;\r
+ protected boolean isPrimary;\r
+\r
+ /**\r
+ * Finds the closest defined & named Hue for the given hue value.\r
+ * Optionally, the search can be limited to primary hues only.\r
+ * \r
+ * @param hue\r
+ * normalized hue (0.0 ... 1.0) will be automatically wrapped\r
+ * @param primaryOnly\r
+ * only consider the 7 primary hues\r
+ * @return closest Hue instance\r
+ */\r
+ public static final Hue getClosest(float hue, boolean primaryOnly) {\r
+ hue %= 1;\r
+ float dist = Float.MAX_VALUE;\r
+ Hue closest = null;\r
+ Iterable<Hue> hues = (primaryOnly ? primaryHues : namedHues.values());\r
+ for (Hue h : hues) {\r
+ float d = MathUtils.min(MathUtils.abs(h.hue - hue), MathUtils.abs(1\r
+ + h.hue - hue));\r
+ if (d < dist) {\r
+ dist = d;\r
+ closest = h;\r
+ }\r
+ }\r
+ return closest;\r
+ }\r
+\r
+ public static final Hue getForName(String name) {\r
+ return namedHues.get(name.toLowerCase());\r
+ }\r
+\r
+ public static boolean isPrimary(float hue) {\r
+ return isPrimary(hue, PRIMARY_VARIANCE);\r
+ }\r
+\r
+ public static boolean isPrimary(float hue, float variance) {\r
+ boolean isPrimary = false;\r
+ for (Hue h : primaryHues) {\r
+ if (MathUtils.abs(hue - h.hue) < variance) {\r
+ isPrimary = true;\r
+ break;\r
+ }\r
+ }\r
+ return isPrimary;\r
+ }\r
+\r
+ public Hue(String name, float hue) {\r
+ this(name, hue, false);\r
+ }\r
+\r
+ public Hue(String name, float hue, boolean isPrimary) {\r
+ this.name = name;\r
+ this.hue = hue;\r
+ this.isPrimary = isPrimary;\r
+ namedHues.put(name, this);\r
+ if (isPrimary) {\r
+ primaryHues.add(this);\r
+ }\r
+ }\r
+\r
+ public float getHue() {\r
+ return hue;\r
+ }\r
+\r
+ public String getName() {\r
+ return name;\r
+ }\r
+\r
+ public boolean isPrimary() {\r
+ return isPrimary;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "Hue: ID:" + name + " @ " + (int) (hue * 360) + " degrees";\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Some classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.color;\r
+\r
+import java.util.Comparator;\r
+\r
+/**\r
+ * Compares 2 colors by their luminance values.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public class LuminanceComparator implements Comparator<TColor> {\r
+\r
+ public int compare(TColor a, TColor b) {\r
+ float lumA = a.luminance();\r
+ float lumB = b.luminance();\r
+ if (lumA < lumB)\r
+ return -1;\r
+ if (lumA > lumB)\r
+ return 1;\r
+ else\r
+ return 0;\r
+ }\r
+\r
+}\r
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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<String, TColor> namedColorMap = new HashMap<String, TColor>();
+
+ 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<String> getNames() {
+ return new ArrayList<String>(namedColorMap.keySet());
+ }
+}
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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<TColor> {
+
+ 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;
+ }
+
+}
--- /dev/null
+/*\r
+ * Some classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.color;\r
+\r
+import java.util.Comparator;\r
+\r
+/**\r
+ * Compares 2 colors by one of their RGB component values.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public class RGBComparator implements Comparator<TColor> {\r
+\r
+ private final int component;\r
+\r
+ public RGBComparator(int comp) {\r
+ component = comp;\r
+ }\r
+\r
+ public int compare(TColor a, TColor b) {\r
+ return Float.compare(a.rgb[component], b.rgb[component]);\r
+ }\r
+\r
+}\r
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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);
+ }
+
+}
--- /dev/null
+/*\r
+ * Some classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.color;\r
+\r
+import toxi.geom.Vec2D;\r
+import toxi.geom.Vec3D;\r
+import toxi.math.MathUtils;\r
+\r
+/**\r
+ * Floating point color class with implicit RGB, HSV, CMYK access modes,\r
+ * conversion and color theory utils. Based on the <a href="">Colors\r
+ * library</a> for <a href="http:/Nodebox.net/">NodeBox</a>\r
+ * \r
+ */\r
+public class TColor {\r
+\r
+ protected static final float INV60DEGREES = 60.0f / 360;\r
+ protected static final float INV8BIT = 1f / 255;\r
+ protected static final double EPS = .001;\r
+\r
+ protected static final Vec2D[] RYB_WHEEL = new Vec2D[] { new Vec2D(0, 0),\r
+ new Vec2D(15, 8), new Vec2D(30, 17), new Vec2D(45, 26),\r
+ new Vec2D(60, 34), new Vec2D(75, 41), new Vec2D(90, 48),\r
+ new Vec2D(105, 54), new Vec2D(120, 60), new Vec2D(135, 81),\r
+ new Vec2D(150, 103), new Vec2D(165, 123), new Vec2D(180, 138),\r
+ new Vec2D(195, 155), new Vec2D(210, 171), new Vec2D(225, 187),\r
+ new Vec2D(240, 204), new Vec2D(255, 219), new Vec2D(270, 234),\r
+ new Vec2D(285, 251), new Vec2D(300, 267), new Vec2D(315, 282),\r
+ new Vec2D(330, 298), new Vec2D(345, 329), new Vec2D(360, 0) };\r
+\r
+ /**\r
+ * Maximum rgb component value for a color to be classified as black.\r
+ * \r
+ * @see #isBlack()\r
+ */\r
+ public static float BLACK_POINT = 0.08f;\r
+\r
+ /**\r
+ * Minimum rgb component value for a color to be classified as white.\r
+ * \r
+ * @see #isWhite()\r
+ */\r
+ public static float WHITE_POINT = 1f;\r
+\r
+ /**\r
+ * Maximum saturations value for a color to be classified as grey\r
+ * \r
+ * @see #isGrey()\r
+ */\r
+ public static float GREY_THRESHOLD = 0.01f;\r
+\r
+ public static final TColor RED = newRGB(1, 0, 0);\r
+ public static final TColor GREEN = newRGB(0, 1, 0);\r
+ public static final TColor BLUE = newRGB(0, 0, 1);\r
+ public static final TColor CYAN = newRGB(0, 1, 1);\r
+ public static final TColor MAGENTA = newRGB(1, 0, 1);\r
+ public static final TColor YELLOW = newRGB(1, 1, 0);\r
+ public static final TColor BLACK = newRGB(0, 0, 0);\r
+ public static final TColor WHITE = newRGB(1, 1, 1);\r
+\r
+ protected float[] rgb;\r
+ protected float[] cmyk;\r
+ protected float[] hsv;\r
+ public float alpha;\r
+\r
+ /**\r
+ * Converts CMYK floats into an RGB array.\r
+ * \r
+ * @param c\r
+ * @param m\r
+ * @param y\r
+ * @param k\r
+ * @return rgb array\r
+ */\r
+ public static final float[] cmykToRGB(float c, float m, float y, float k) {\r
+ return cmykToRGB(c, m, y, k, new float[3]);\r
+ }\r
+\r
+ /**\r
+ * Converts CMYK floats into the given RGB array.\r
+ * \r
+ * @param c\r
+ * @param m\r
+ * @param y\r
+ * @param k\r
+ * @param rgb\r
+ * @return rgb array\r
+ */\r
+ public static final float[] cmykToRGB(float c, float m, float y, float k,\r
+ float[] rgb) {\r
+ rgb[0] = 1.0f - MathUtils.min(1.0f, c + k);\r
+ rgb[1] = 1.0f - MathUtils.min(1.0f, m + k);\r
+ rgb[2] = 1.0f - MathUtils.min(1.0f, y + k);\r
+ return rgb;\r
+ }\r
+\r
+ /**\r
+ * Converts hex string into a RGB array.\r
+ * \r
+ * @param hexRGB\r
+ * @return rgb array\r
+ */\r
+ public static final float[] hexToRGB(String hexRGB) {\r
+ return hexToRGB(hexRGB, new float[3]);\r
+ }\r
+\r
+ public static final float[] hexToRGB(String hexRGB, float[] rgb) {\r
+ try {\r
+ int rgbInt = Integer.parseInt(hexRGB, 16);\r
+ rgb[0] = ((rgbInt >> 16) & 0xff) * INV8BIT;\r
+ rgb[1] = ((rgbInt >> 8) & 0xff) * INV8BIT;\r
+ rgb[2] = (rgbInt & 0xff) * INV8BIT;\r
+ } catch (NumberFormatException e) {\r
+ rgb[0] = rgb[1] = rgb[2] = 0;\r
+ }\r
+ return rgb;\r
+ }\r
+\r
+ /**\r
+ * Converts HSV values into RGB array.\r
+ * \r
+ * @param h\r
+ * @param s\r
+ * @param v\r
+ * @return rgb array\r
+ */\r
+ public static final float[] hsvToRGB(float h, float s, float v) {\r
+ return hsvToRGB(h, s, v, new float[3]);\r
+ }\r
+\r
+ public static final float[] hsvToRGB(float h, float s, float v, float[] rgb) {\r
+ if (Float.compare(s, 0.0f) == 0) {\r
+ rgb[0] = rgb[1] = rgb[2] = v;\r
+ } else {\r
+ h /= INV60DEGREES;\r
+ int i = (int) h;\r
+ float f = h - i;\r
+ float p = v * (1 - s);\r
+ float q = v * (1 - s * f);\r
+ float t = v * (1 - s * (1 - f));\r
+\r
+ if (i == 0) {\r
+ rgb[0] = v;\r
+ rgb[1] = t;\r
+ rgb[2] = p;\r
+ } else if (i == 1) {\r
+ rgb[0] = q;\r
+ rgb[1] = v;\r
+ rgb[2] = p;\r
+ } else if (i == 2) {\r
+ rgb[0] = p;\r
+ rgb[1] = v;\r
+ rgb[2] = t;\r
+ } else if (i == 3) {\r
+ rgb[0] = p;\r
+ rgb[1] = q;\r
+ rgb[2] = v;\r
+ } else if (i == 4) {\r
+ rgb[0] = t;\r
+ rgb[1] = p;\r
+ rgb[2] = v;\r
+ } else {\r
+ rgb[0] = v;\r
+ rgb[1] = p;\r
+ rgb[2] = q;\r
+ }\r
+ }\r
+ return rgb;\r
+ }\r
+\r
+ public static final float[] labToRGB(float l, float a, float b) {\r
+ return labToRGB(l, a, b, new float[3]);\r
+ }\r
+\r
+ /**\r
+ * Converts CIE Lab to RGB components.\r
+ * \r
+ * First we have to convert to XYZ color space. Conversion involves using a\r
+ * white point, in this case D65 which represents daylight illumination.\r
+ * \r
+ * Algorithm adopted from: http://www.easyrgb.com/math.php\r
+ * \r
+ * @param l\r
+ * @param a\r
+ * @param b\r
+ * @param rgb\r
+ * @return rgb array\r
+ */\r
+ public static final float[] labToRGB(float l, float a, float b, float[] rgb) {\r
+ float y = (l + 16) / 116.0f;\r
+ float x = a / 500.0f + y;\r
+ float z = y - b / 200.0f;\r
+ rgb[0] = x;\r
+ rgb[1] = y;\r
+ rgb[2] = z;\r
+ for (int i = 0; i < 3; i++) {\r
+ float p = (float) Math.pow(rgb[i], 3);\r
+ if (p > 0.008856) {\r
+ rgb[i] = p;\r
+ } else {\r
+ rgb[i] = (rgb[i] - 16 / 116.0f) / 7.787f;\r
+ }\r
+ }\r
+\r
+ // Observer = 2, Illuminant = D65\r
+ x = rgb[0] * 0.95047f;\r
+ y = rgb[1];\r
+ z = rgb[2] * 1.08883f;\r
+\r
+ rgb[0] = x * 3.2406f + y * -1.5372f + z * -0.4986f;\r
+ rgb[1] = x * -0.9689f + y * 1.8758f + z * 0.0415f;\r
+ rgb[2] = x * 0.0557f + y * -0.2040f + z * 1.0570f;\r
+ double tpow = 1 / 2.4;\r
+ for (int i = 0; i < 3; i++) {\r
+ if (rgb[i] > 0.0031308) {\r
+ rgb[i] = (float) (1.055 * Math.pow(rgb[i], tpow) - 0.055);\r
+ } else {\r
+ rgb[i] = 12.92f * rgb[i];\r
+ }\r
+ }\r
+ return rgb;\r
+ }\r
+\r
+ /**\r
+ * Factory method. Creates new color from ARGB int.\r
+ * \r
+ * @param argb\r
+ * @return new color\r
+ */\r
+ public static final TColor newARGB(int argb) {\r
+ return newRGBA(((argb >> 16) & 0xff) * INV8BIT, ((argb >> 8) & 0xff)\r
+ * INV8BIT, (argb & 0xff) * INV8BIT, (argb >>> 24) * INV8BIT);\r
+ }\r
+\r
+ /**\r
+ * Factory method. Creates new color from CMYK values.\r
+ * \r
+ * @param c\r
+ * @param m\r
+ * @param y\r
+ * @param k\r
+ * @return new color\r
+ */\r
+ public static final TColor newCMYK(float c, float m, float y, float k) {\r
+ return newCMYKA(c, m, y, k, 1);\r
+ }\r
+\r
+ /**\r
+ * Factory method. Creates new color from CMYK + alpha values.\r
+ * \r
+ * @param c\r
+ * @param m\r
+ * @param y\r
+ * @param k\r
+ * @param a\r
+ * @return new color\r
+ */\r
+ public static final TColor newCMYKA(float c, float m, float y, float k,\r
+ float a) {\r
+ TColor col = new TColor();\r
+ col.setCMYK(new float[] { c, m, y, k });\r
+ col.alpha = MathUtils.clip(a, 0, 1);\r
+ return col;\r
+ }\r
+\r
+ /**\r
+ * Factory method. Creates a new shade of gray + alpha.\r
+ * \r
+ * @param gray\r
+ * @return new color.\r
+ */\r
+ public static final TColor newGray(float gray) {\r
+ return newGrayAlpha(gray, 1);\r
+ }\r
+\r
+ public static final TColor newGrayAlpha(float gray, float alpha) {\r
+ TColor c = new TColor();\r
+ c.setRGB(new float[] { gray, gray, gray });\r
+ c.alpha = alpha;\r
+ return c;\r
+ }\r
+\r
+ /**\r
+ * Factory method. New color from hex string.\r
+ * \r
+ * @param hexRGB\r
+ * @return new color\r
+ */\r
+ public static final TColor newHex(String hexRGB) {\r
+ TColor c = new TColor();\r
+ c.setRGB(hexToRGB(hexRGB));\r
+ c.alpha = 1;\r
+ return c;\r
+ }\r
+\r
+ /**\r
+ * Factory method. New color from hsv values.\r
+ * \r
+ * @param h\r
+ * @param s\r
+ * @param v\r
+ * @return new color\r
+ */\r
+ public static final TColor newHSV(float h, float s, float v) {\r
+ return newHSVA(h, s, v, 1);\r
+ }\r
+\r
+ public static TColor newHSV(Hue h, float s, float v) {\r
+ return newHSV(h.getHue(), s, v);\r
+ }\r
+\r
+ public static final TColor newHSVA(float h, float s, float v, float a) {\r
+ TColor c = new TColor();\r
+ c.setHSV(new float[] { h, s, v });\r
+ c.alpha = MathUtils.clip(a, 0, 1);\r
+ return c;\r
+ }\r
+\r
+ /**\r
+ * Factory method. Creates new random color.\r
+ * \r
+ * @return random color\r
+ */\r
+ public static final TColor newRandom() {\r
+ return newRGBA(MathUtils.random(1f), MathUtils.random(1f), MathUtils\r
+ .random(1f), 1);\r
+ }\r
+\r
+ /**\r
+ * Factory method. Creates new color from RGB values.\r
+ * \r
+ * @param r\r
+ * @param g\r
+ * @param b\r
+ * @return new color\r
+ */\r
+ public static final TColor newRGB(float r, float g, float b) {\r
+ return newRGBA(r, g, b, 1);\r
+ }\r
+\r
+ public static final TColor newRGBA(float r, float g, float b, float a) {\r
+ TColor c = new TColor();\r
+ c.setRGB(new float[] { r, g, b });\r
+ c.alpha = MathUtils.clip(a, 0, 1);\r
+ return c;\r
+ }\r
+\r
+ /**\r
+ * Converts the RGB values into a CMYK array.\r
+ * \r
+ * @param r\r
+ * @param g\r
+ * @param b\r
+ * @return cmyk array\r
+ */\r
+ public static final float[] rgbToCMYK(float r, float g, float b) {\r
+ return rgbToCMYK(r, g, b, new float[4]);\r
+ }\r
+\r
+ public static final float[] rgbToCMYK(float r, float g, float b,\r
+ float[] cmyk) {\r
+ cmyk[0] = 1 - r;\r
+ cmyk[1] = 1 - g;\r
+ cmyk[2] = 1 - b;\r
+ cmyk[3] = MathUtils.min(cmyk[0], cmyk[1], cmyk[2]);\r
+ cmyk[0] = MathUtils.clip(cmyk[0] - cmyk[3], 0, 1);\r
+ cmyk[1] = MathUtils.clip(cmyk[1] - cmyk[3], 0, 1);\r
+ cmyk[2] = MathUtils.clip(cmyk[2] - cmyk[3], 0, 1);\r
+ cmyk[3] = MathUtils.clip(cmyk[3], 0, 1);\r
+ return cmyk;\r
+ }\r
+\r
+ /**\r
+ * Formats the RGB float values into hex integers.\r
+ * \r
+ * @param r\r
+ * @param g\r
+ * @param b\r
+ * @return hex string\r
+ */\r
+ public static final String rgbToHex(float r, float g, float b) {\r
+ String hex = Integer.toHexString((int) (r * 0xff))\r
+ + Integer.toHexString((int) (g * 0xff))\r
+ + Integer.toHexString((int) (b * 0xff));\r
+ return hex;\r
+ }\r
+\r
+ /**\r
+ * Converts the RGB values into an HSV array.\r
+ * \r
+ * @param r\r
+ * @param g\r
+ * @param b\r
+ * @return hsv array\r
+ */\r
+ public static final float[] rgbToHSV(float r, float g, float b) {\r
+ return rgbToHSV(r, g, b, new float[3]);\r
+ }\r
+\r
+ public static final float[] rgbToHSV(float r, float g, float b, float[] hsv) {\r
+ float h = 0, s = 0;\r
+ float v = MathUtils.max(r, g, b);\r
+ float d = v - MathUtils.min(r, g, b);\r
+\r
+ if (v != 0.0) {\r
+ s = d / v;\r
+ }\r
+ if (s != 0.0) {\r
+ if (Float.compare(r, v) == 0) {\r
+ h = (g - b) / d;\r
+ } else if (Float.compare(g, v) == 0) {\r
+ h = 2 + (b - r) / d;\r
+ } else {\r
+ h = 4 + (r - g) / d;\r
+ }\r
+ }\r
+\r
+ h *= INV60DEGREES;\r
+\r
+ if (h < 0) {\r
+ h += 1.0f;\r
+ }\r
+\r
+ hsv[0] = h;\r
+ hsv[1] = s;\r
+ hsv[2] = v;\r
+ return hsv;\r
+ }\r
+\r
+ protected TColor() {\r
+ rgb = new float[3];\r
+ hsv = new float[3];\r
+ cmyk = new float[4];\r
+ }\r
+\r
+ /**\r
+ * Creates a deep copy of the given color.\r
+ * \r
+ * @param c\r
+ */\r
+ public TColor(TColor c) {\r
+ this();\r
+ System.arraycopy(c.rgb, 0, rgb, 0, 3);\r
+ System.arraycopy(c.hsv, 0, hsv, 0, 3);\r
+ System.arraycopy(c.cmyk, 0, cmyk, 0, 4);\r
+ this.alpha = c.alpha;\r
+ }\r
+\r
+ /**\r
+ * Changes the brightness of the color by the given amount in the direction\r
+ * towards either the black or white point (depending on if current\r
+ * brightness >= 50%)\r
+ * \r
+ * @param amount\r
+ * @return itself\r
+ */\r
+ public TColor adjustConstrast(float amount) {\r
+ return hsv[2] < 0.5 ? darken(amount) : lighten(amount);\r
+ }\r
+\r
+ /**\r
+ * Adds the given HSV values as offsets to the current color. Hue will\r
+ * automatically wrap.\r
+ * \r
+ * @param h\r
+ * @param s\r
+ * @param v\r
+ * @return itself\r
+ */\r
+ public TColor adjustHSV(float h, float s, float v) {\r
+ return setHSV(new float[] { hsv[0] + h, hsv[1] + s, hsv[2] + v });\r
+ }\r
+\r
+ /**\r
+ * Adds the given RGB values as offsets to the current color. TColor will\r
+ * clip at black or white.\r
+ * \r
+ * @param r\r
+ * @param g\r
+ * @param b\r
+ * @return itself\r
+ */\r
+ public TColor adjustRGB(float r, float g, float b) {\r
+ return setRGB(new float[] { rgb[0] + r, rgb[1] + g, rgb[2] + b });\r
+ }\r
+\r
+ /**\r
+ * @return the color's alpha component\r
+ */\r
+ public float alpha() {\r
+ return alpha;\r
+ }\r
+\r
+ /**\r
+ * Rotates this color by a random amount (not exceeding the one specified)\r
+ * and creates variations in saturation and brightness based on the 2nd\r
+ * parameter.\r
+ * \r
+ * @param theta\r
+ * max. rotation angle (in radians)\r
+ * @param delta\r
+ * max. sat/bri variance\r
+ * @return itself\r
+ */\r
+ public TColor analog(float theta, float delta) {\r
+ return analog(MathUtils.degrees(theta), delta);\r
+ }\r
+\r
+ public TColor analog(int angle, float delta) {\r
+ rotateRYB((int) (angle * MathUtils.normalizedRandom()));\r
+ hsv[1] += delta * MathUtils.normalizedRandom();\r
+ hsv[2] += delta * MathUtils.normalizedRandom();\r
+ return setHSV(hsv);\r
+ }\r
+\r
+ /**\r
+ * @return the color's black component\r
+ */\r
+\r
+ public float black() {\r
+ return cmyk[0];\r
+ }\r
+\r
+ /**\r
+ * Blends the color with the given one by the stated amount\r
+ * \r
+ * @param c\r
+ * target color\r
+ * @param t\r
+ * interpolation factor\r
+ * @return itself\r
+ */\r
+ public TColor blend(TColor c, float t) {\r
+ rgb[0] += (c.rgb[0] - rgb[0]) * t;\r
+ rgb[1] += (c.rgb[1] - rgb[1]) * t;\r
+ rgb[2] += (c.rgb[2] - rgb[2]) * t;\r
+ alpha += (c.alpha - alpha) * t;\r
+ return setRGB(rgb);\r
+ }\r
+\r
+ /**\r
+ * @return the color's blue component\r
+ */\r
+\r
+ public float blue() {\r
+ return rgb[2];\r
+ }\r
+\r
+ /**\r
+ * @return color HSV brightness (not luminance!)\r
+ */\r
+ public float brightness() {\r
+ return hsv[2];\r
+ }\r
+\r
+ /**\r
+ * @return ifself, as complementary color\r
+ */\r
+ public TColor complement() {\r
+ return rotateRYB(180);\r
+ }\r
+\r
+ public TColor copy() {\r
+ return new TColor(this);\r
+ }\r
+\r
+ /**\r
+ * @return the color's cyan component\r
+ */\r
+\r
+ public float cyan() {\r
+ return cmyk[0];\r
+ }\r
+\r
+ public TColor darken(float step) {\r
+ hsv[2] = MathUtils.clip(hsv[2] - step, 0, 1);\r
+ return setHSV(hsv);\r
+ }\r
+\r
+ public TColor desaturate(float step) {\r
+ hsv[1] = MathUtils.clip(hsv[1] - step, 0, 1);\r
+ return setHSV(hsv);\r
+ }\r
+\r
+ /**\r
+ * Calculates the CMYK distance to the given color.\r
+ * \r
+ * @param c\r
+ * target color\r
+ * @return distance\r
+ */\r
+ public float distanceToCMYK(TColor c) {\r
+ float dc = cmyk[0] - c.cmyk[0];\r
+ float dm = cmyk[1] - c.cmyk[1];\r
+ float dy = cmyk[2] - c.cmyk[2];\r
+ float dk = cmyk[3] - c.cmyk[3];\r
+ return (float) Math.sqrt(dc * dc + dm * dm + dy * dy + dk * dk);\r
+ }\r
+\r
+ /**\r
+ * Calculates the HSV distance to the given color.\r
+ * \r
+ * @param c\r
+ * target color\r
+ * @return distance\r
+ */\r
+ public float distanceToHSV(TColor c) {\r
+ float hue = hsv[0] * MathUtils.TWO_PI;\r
+ float hue2 = c.hsv[0] * MathUtils.TWO_PI;\r
+ Vec3D v1 = new Vec3D((float) (Math.cos(hue) * hsv[1]), (float) (Math\r
+ .sin(hue) * hsv[1]), hsv[2]);\r
+ Vec3D v2 = new Vec3D((float) (Math.cos(hue2) * c.hsv[1]), (float) (Math\r
+ .sin(hue2) * c.hsv[1]), c.hsv[2]);\r
+ return v1.distanceTo(v2);\r
+ }\r
+\r
+ /**\r
+ * Calculates the RGB distance to the given color.\r
+ * \r
+ * @param c\r
+ * target color\r
+ * @return distance\r
+ */\r
+ public float distanceToRGB(TColor c) {\r
+ float dr = rgb[0] - c.rgb[0];\r
+ float dg = rgb[1] - c.rgb[1];\r
+ float db = rgb[2] - c.rgb[2];\r
+ return (float) Math.sqrt(dr * dr + dg * dg + db * db);\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object o) {\r
+ if (o != null && o instanceof TColor) {\r
+ TColor c = (TColor) o;\r
+ float dr = c.rgb[0] - rgb[0];\r
+ float dg = c.rgb[1] - rgb[1];\r
+ float db = c.rgb[2] - rgb[2];\r
+ float da = c.alpha - alpha;\r
+ double d = Math.sqrt(dr * dr + dg * dg + db * db + da * da);\r
+ return d < EPS;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ public TColor getAnalog(float theta, float delta) {\r
+ return new TColor(this).analog(theta, delta);\r
+ }\r
+\r
+ public TColor getAnalog(int angle, float delta) {\r
+ return new TColor(this).analog(angle, delta);\r
+ }\r
+\r
+ public TColor getBlended(TColor c, float t) {\r
+ return new TColor(this).blend(c, t);\r
+ }\r
+\r
+ /**\r
+ * @return an instance of the closest named hue to this color.\r
+ */\r
+ public Hue getClosestHue() {\r
+ return Hue.getClosest(hsv[0], false);\r
+ }\r
+\r
+ /**\r
+ * @param primaryOnly\r
+ * if true, only primary color hues are considered\r
+ * @return an instance of the closest named (primary) hue to this color.\r
+ */\r
+ public Hue getClosestHue(boolean primaryOnly) {\r
+ return Hue.getClosest(hsv[0], primaryOnly);\r
+ }\r
+\r
+ public TColor getComplement() {\r
+ return new TColor(this).complement();\r
+ }\r
+\r
+ public float getComponentValue(AccessCriteria criteria) {\r
+ switch (criteria.getMode()) {\r
+ case HSV:\r
+ return hsv[criteria.getComponent()];\r
+ case RGB:\r
+ return rgb[criteria.getComponent()];\r
+ case CMYK:\r
+ return cmyk[criteria.getComponent()];\r
+ case ALPHA:\r
+ return alpha;\r
+ }\r
+ return 0;\r
+ }\r
+\r
+ /**\r
+ * @param step\r
+ * @return a darkened copy\r
+ */\r
+ public TColor getDarkened(float step) {\r
+ return new TColor(this).darken(step);\r
+ }\r
+\r
+ /**\r
+ * @param step\r
+ * @return a desaturated copy\r
+ */\r
+ public TColor getDesaturated(float step) {\r
+ return new TColor(this).desaturate(step);\r
+ }\r
+\r
+ /**\r
+ * @param step\r
+ * @return a lightened copy\r
+ */\r
+ public TColor getLightened(float step) {\r
+ return new TColor(this).lighten(step);\r
+ }\r
+\r
+ /**\r
+ * @param theta\r
+ * rotation angle in radians\r
+ * @return a RYB rotated copy\r
+ */\r
+ public TColor getRotatedRYB(float theta) {\r
+ return new TColor(this).rotateRYB(theta);\r
+ }\r
+\r
+ /**\r
+ * @param angle\r
+ * rotation angle in degrees\r
+ * @return a RYB rotated copy\r
+ */\r
+ public TColor getRotatedRYB(int angle) {\r
+ return new TColor(this).rotateRYB(angle);\r
+ }\r
+\r
+ /**\r
+ * @param step\r
+ * @return a saturated copy\r
+ */\r
+ public TColor getSaturated(float step) {\r
+ return new TColor(this).saturate(step);\r
+ }\r
+\r
+ /**\r
+ * @return the color's green component\r
+ */\r
+\r
+ public float green() {\r
+ return rgb[1];\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return (int) (rgb[0] * 1000000 + rgb[1] * 100000 + rgb[2] * 10000 + alpha * 1000);\r
+ }\r
+\r
+ /**\r
+ * @return the color's hue\r
+ */\r
+ public float hue() {\r
+ return hsv[0];\r
+ }\r
+\r
+ /**\r
+ * Inverts the color.\r
+ * \r
+ * @return itself\r
+ */\r
+ public TColor invert() {\r
+ rgb[0] = 1 - rgb[0];\r
+ rgb[1] = 1 - rgb[1];\r
+ rgb[2] = 1 - rgb[2];\r
+ return setRGB(rgb);\r
+ }\r
+\r
+ /**\r
+ * @return true, if all rgb component values are equal and less than\r
+ * {@link #BLACK_POINT}\r
+ */\r
+ public boolean isBlack() {\r
+ return (rgb[0] <= BLACK_POINT && Float.compare(rgb[0], rgb[1]) == 0 && Float\r
+ .compare(rgb[0], rgb[2]) == 0);\r
+ }\r
+\r
+ /**\r
+ * @return true, if the saturation component value is less than\r
+ * {@link #GREY_THRESHOLD}\r
+ */\r
+ public boolean isGrey() {\r
+ return hsv[1] < GREY_THRESHOLD;\r
+ }\r
+\r
+ /**\r
+ * @return true, if this colors hue is matching one of the 7 defined\r
+ * primary hues.\r
+ */\r
+ public boolean isPrimary() {\r
+ return Hue.isPrimary(hsv[0]);\r
+ }\r
+\r
+ /**\r
+ * @return true, if all rgb component values are equal and greater than\r
+ * {@link #WHITE_POINT}\r
+ */\r
+ public boolean isWhite() {\r
+ return (rgb[0] >= WHITE_POINT && Float.compare(rgb[0], rgb[1]) == 0 && Float\r
+ .compare(rgb[0], rgb[2]) == 0);\r
+ }\r
+\r
+ /**\r
+ * Lightens the color by stated amount.\r
+ * \r
+ * @param step\r
+ * lighten amount\r
+ * @return itself\r
+ */\r
+ public TColor lighten(float step) {\r
+ hsv[2] = MathUtils.clip(hsv[2] + step, 0, 1);\r
+ return setHSV(hsv);\r
+ }\r
+\r
+ /**\r
+ * Computes the color's luminance using this formula: lum=0.299*red +\r
+ * 0.587*green + 0.114 *blue\r
+ * \r
+ * @return luminance\r
+ */\r
+ public float luminance() {\r
+ return rgb[0] * 0.299f + rgb[1] * 0.587f + rgb[2] * 0.114f;\r
+ }\r
+\r
+ /**\r
+ * @return the color's magenta component\r
+ */\r
+\r
+ public float magenta() {\r
+ return cmyk[0];\r
+ }\r
+\r
+ /**\r
+ * @return the color's red component\r
+ */\r
+\r
+ public float red() {\r
+ return rgb[0];\r
+ }\r
+\r
+ public TColor rotateRYB(float theta) {\r
+ return rotateRYB((int) MathUtils.degrees(theta));\r
+ }\r
+\r
+ public TColor rotateRYB(int theta) {\r
+ float h = hsv[0] * 360;\r
+ theta %= 360;\r
+\r
+ float resultHue = 0;\r
+ for (int i = 0; i < RYB_WHEEL.length - 1; i++) {\r
+ Vec2D p = RYB_WHEEL[i];\r
+ Vec2D q = RYB_WHEEL[i + 1];\r
+ if (q.y < p.y) {\r
+ q.y += 360;\r
+ }\r
+ if (p.y <= h && h <= q.y) {\r
+ resultHue = p.x + (q.x - p.x) * (h - p.y) / (q.y - p.y);\r
+ break;\r
+ }\r
+ }\r
+\r
+ // And the user-given angle (e.g. complement).\r
+ resultHue = (resultHue + theta) % 360;\r
+\r
+ // For the given angle, find out what hue is\r
+ // located there on the artistic color wheel.\r
+ for (int i = 0; i < RYB_WHEEL.length - 1; i++) {\r
+ Vec2D p = RYB_WHEEL[i];\r
+ Vec2D q = RYB_WHEEL[i + 1];\r
+ if (q.y < p.y) {\r
+ q.y += 360;\r
+ }\r
+ if (p.x <= resultHue && resultHue <= q.x) {\r
+ h = p.y + (q.y - p.y) * (resultHue - p.x) / (q.x - p.x);\r
+ break;\r
+ }\r
+ }\r
+\r
+ hsv[0] = (h % 360) / 360.0f;\r
+ return setHSV(hsv);\r
+ }\r
+\r
+ public TColor saturate(float step) {\r
+ hsv[1] = MathUtils.clip(hsv[1] + step, 0, 1);\r
+ return setHSV(hsv);\r
+ }\r
+\r
+ public float saturation() {\r
+ return hsv[1];\r
+ }\r
+\r
+ public TColor setAlpha(float alpha) {\r
+ this.alpha = alpha;\r
+ return this;\r
+ }\r
+\r
+ public TColor setBlack(float val) {\r
+ cmyk[3] = val;\r
+ return setCMYK(cmyk);\r
+ }\r
+\r
+ public TColor setBlue(float blue) {\r
+ rgb[2] = blue;\r
+ return setRGB(rgb);\r
+ }\r
+\r
+ public TColor setBrightness(float brightness) {\r
+ hsv[2] = MathUtils.clip(brightness, 0, 1);\r
+ return setHSV(hsv);\r
+ }\r
+\r
+ public TColor setCMYK(float[] newCMYK) {\r
+ cmyk[0] = MathUtils.clip(newCMYK[0], 0, 1);\r
+ cmyk[1] = MathUtils.clip(newCMYK[1], 0, 1);\r
+ cmyk[2] = MathUtils.clip(newCMYK[2], 0, 1);\r
+ cmyk[3] = MathUtils.clip(newCMYK[3], 0, 1);\r
+ cmykToRGB(cmyk[0], cmyk[1], cmyk[2], cmyk[3], rgb);\r
+ rgbToHSV(rgb[0], rgb[1], rgb[2], hsv);\r
+ return this;\r
+ }\r
+\r
+ public TColor setComponent(AccessCriteria criteria, float val) {\r
+ switch (criteria.getMode()) {\r
+ case RGB:\r
+ rgb[criteria.getComponent()] = val;\r
+ return setRGB(rgb);\r
+ case HSV:\r
+ hsv[criteria.getComponent()] = val;\r
+ return setHSV(hsv);\r
+ case ALPHA:\r
+ return setAlpha(val);\r
+ case CMYK:\r
+ cmyk[criteria.getComponent()] = val;\r
+ return setCMYK(cmyk);\r
+ default:\r
+ throw new IllegalArgumentException(\r
+ "Invalid ColorAccessCriteria mode used");\r
+ }\r
+ }\r
+\r
+ public TColor setCyan(float val) {\r
+ cmyk[0] = val;\r
+ return setCMYK(cmyk);\r
+ }\r
+\r
+ public TColor setGreen(float green) {\r
+ rgb[1] = green;\r
+ return setRGB(rgb);\r
+ }\r
+\r
+ public TColor setHSV(float[] newHSV) {\r
+ hsv[0] = newHSV[0] % 1;\r
+ if (hsv[0] < 0)\r
+ hsv[0]++;\r
+ hsv[1] = MathUtils.clip(newHSV[1], 0, 1);\r
+ hsv[2] = MathUtils.clip(newHSV[2], 0, 1);\r
+ hsvToRGB(hsv[0], hsv[1], hsv[2], rgb);\r
+ rgbToCMYK(rgb[0], rgb[1], rgb[2], cmyk);\r
+ return this;\r
+ }\r
+\r
+ public void setHue(float hue) {\r
+ hue %= 1.0;\r
+ if (hue < 0.0) {\r
+ hue++;\r
+ }\r
+ hsv[0] = hue;\r
+ setHSV(hsv);\r
+ }\r
+\r
+ public TColor setMagenta(float val) {\r
+ cmyk[1] = val;\r
+ return setCMYK(cmyk);\r
+ }\r
+\r
+ public TColor setRed(float red) {\r
+ rgb[0] = red;\r
+ return setRGB(rgb);\r
+ }\r
+\r
+ public TColor setRGB(float[] newRGB) {\r
+ rgb[0] = MathUtils.clip(newRGB[0], 0, 1);\r
+ rgb[1] = MathUtils.clip(newRGB[1], 0, 1);\r
+ rgb[2] = MathUtils.clip(newRGB[2], 0, 1);\r
+ rgbToCMYK(rgb[0], rgb[1], rgb[2], cmyk);\r
+ rgbToHSV(rgb[0], rgb[1], rgb[2], hsv);\r
+ return this;\r
+ }\r
+\r
+ public TColor setSaturation(float saturation) {\r
+ hsv[1] = MathUtils.clip(saturation, 0, 1);\r
+ return setHSV(hsv);\r
+ }\r
+\r
+ public TColor setYellow(float val) {\r
+ cmyk[2] = val;\r
+ return setCMYK(cmyk);\r
+ }\r
+\r
+ /**\r
+ * Converts the color into a packed ARGB int.\r
+ * \r
+ * @return color as int\r
+ */\r
+ public int toARGB() {\r
+ return (int) (rgb[0] * 255) << 16 | (int) (rgb[1] * 255) << 8\r
+ | (int) (rgb[2] * 255) | (int) (alpha * 255) << 24;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "TColor: rgb: " + rgb[0] + "," + rgb[1] + "," + rgb[2]\r
+ + " hsv: " + hsv[0] + "," + hsv[1] + "," + hsv[2] + " cmyk: "\r
+ + cmyk[0] + "," + cmyk[1] + "," + cmyk[2] + "," + cmyk[3]\r
+ + " alpha: " + alpha;\r
+ }\r
+\r
+ /**\r
+ * @return the color's yellow component\r
+ */\r
+ public float yellow() {\r
+ return cmyk[0];\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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<String, ColorTheoryStrategy> implementations = new HashMap<String, ColorTheoryStrategy>();
+
+ 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<String> getRegisteredNames() {
+ return new ArrayList<String>(implementations.keySet());
+ }
+
+ public static ArrayList<ColorTheoryStrategy> getRegisteredStrategies() {
+ return new ArrayList<ColorTheoryStrategy>(implementations.values());
+ }
+
+ public static ColorTheoryStrategy getStrategyForName(String id) {
+ return implementations.get(id);
+ }
+
+ public static void registerImplementation(ColorTheoryStrategy impl) {
+ implementations.put(impl.getName().toUpperCase(), impl);
+ }
+}
--- /dev/null
+/*\r
+ * The classes in this package have been partly inspired by & bits ported from\r
+ * Python code written by Tom De Smedt & Frederik De Bleser for the "colors" library\r
+ * of Nodebox.net.\r
+ * \r
+ * http://nodebox.net/code/index.php/Colors\r
+ * \r
+ * Copyright (c) 2006-2008 Karsten Schmidt <info at postspectacular.com>\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.color.theory;\r
+\r
+import toxi.color.ColorList;\r
+import toxi.color.TColor;\r
+\r
+/**\r
+ * A simple interface for implementing rules used to create color palettes. The\r
+ * library currently comes with 10 default implementations realising different\r
+ * color theory approaches.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public interface ColorTheoryStrategy {\r
+\r
+ /**\r
+ * Creates a new {@link ColorList} of colors for the supplied source color\r
+ * based on the strategy. The number of colors returned is unspecified and\r
+ * depends on the strategy.\r
+ * \r
+ * @param src\r
+ * source color\r
+ * @return list of matching colors created by the strategy.\r
+ */\r
+ ColorList createListFromColor(TColor src);\r
+\r
+ /**\r
+ * Returns the unique name of the strategy.\r
+ * \r
+ * @return name\r
+ */\r
+ String getName();\r
+}\r
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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:
+ * <ul>
+ * <li>a contrasting color: much darker or lighter than the original.</li>
+ * <li>a soft supporting color: lighter and less saturated.</li>
+ * <li>a contrasting complement: very dark or very light.</li>
+ * <li>the complement and</li>
+ * <li>a light supporting variant.</li>
+ * </ul>
+ *
+ * @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;
+ }
+}
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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" : "");
+ }
+}
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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 <a href="http://www.tigercolor.com/color-lab/color-theory/color-theory-intro.htm#complementary"
+ * >single complementary color scheme</a> 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;
+ }
+}
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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 <a href="http://www.tigercolor.com/color-lab/color-theory/color-theory-intro.htm#split-complementary"
+ * >split-complementary color scheme</a> 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;
+ }
+}
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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 <a href="http://www.tigercolor.com/color-lab/color-theory/color-theory-intro.htm#rectangle"
+ * >tetradic color scheme</a> 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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 <info at postspectacular.com>
+ *
+ * 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 <a href="http://www.tigercolor.com/color-lab/color-theory/color-theory-intro.htm#triadic"
+ * >triadic color scheme</a> 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;
+ }
+
+}
--- /dev/null
+/* \r
+ * Copyright (c) 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.geom;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+/**\r
+ * Axis-aligned bounding box with basic intersection features for Ray, AABB and\r
+ * Sphere classes.\r
+ */\r
+\r
+public class AABB extends Vec3D {\r
+\r
+ private Vec3D extent;\r
+\r
+ private Vec3D min, max;\r
+\r
+ /**\r
+ * Creates a new instance from two vectors specifying opposite corners of\r
+ * the box\r
+ * \r
+ * @param min\r
+ * first corner point\r
+ * @param max\r
+ * second corner point\r
+ * @return new AABB with centre at the half point between the 2 input\r
+ * vectors\r
+ */\r
+ public static final AABB fromMinMax(Vec3D min, Vec3D max) {\r
+ Vec3D a = Vec3D.min(min, max);\r
+ Vec3D b = Vec3D.max(min, max);\r
+ return new AABB(a.interpolateTo(b, 0.5f), b.sub(a).scaleSelf(0.5f));\r
+ }\r
+\r
+ /**\r
+ * Creates a new instance from centre point and extent\r
+ * \r
+ * @param pos\r
+ * @param extent\r
+ * box dimensions (the box will be double the size in each\r
+ * direction)\r
+ */\r
+ public AABB(Vec3D pos, Vec3D extent) {\r
+ super(pos);\r
+ setExtent(extent);\r
+ }\r
+\r
+ /**\r
+ * Creates an independent copy of the passed in box\r
+ * \r
+ * @param box\r
+ */\r
+ public AABB(AABB box) {\r
+ this(box, box.getExtent());\r
+ }\r
+\r
+ /**\r
+ * Updates the position of the box in space and calls\r
+ * {@link #updateBounds()} immediately\r
+ * \r
+ * @see toxi.geom.Vec3D#set(float, float, float)\r
+ */\r
+ public Vec3D set(float x, float y, float z) {\r
+ this.x = x;\r
+ this.y = y;\r
+ this.z = z;\r
+ updateBounds();\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Updates the position of the box in space and calls\r
+ * {@link #updateBounds()} immediately\r
+ * \r
+ * @see toxi.geom.Vec3D#set(toxi.geom.Vec3D)\r
+ */\r
+ public Vec3D set(Vec3D v) {\r
+ x = v.x;\r
+ y = v.y;\r
+ z = v.z;\r
+ updateBounds();\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Updates the size of the box and calls {@link #updateBounds()} immediately\r
+ * \r
+ * @param extent\r
+ * new box size\r
+ * @return itself, for method chaining\r
+ */\r
+ public AABB setExtent(Vec3D extent) {\r
+ this.extent = new Vec3D(extent);\r
+ return updateBounds();\r
+ }\r
+\r
+ /**\r
+ * Returns the current box size as new Vec3D instance (updating this vector\r
+ * will NOT update the box size! Use {@link #setExtent(Vec3D)} for those\r
+ * purposes)\r
+ * \r
+ * @return box size\r
+ */\r
+ public final Vec3D getExtent() {\r
+ return new Vec3D(extent);\r
+ }\r
+\r
+ /**\r
+ * Updates the min/max corner points of the box. MUST be called after moving\r
+ * the box in space by manipulating the public x,y,z coordinates directly.\r
+ * \r
+ * @return itself\r
+ */\r
+ public final AABB updateBounds() {\r
+ // this is check is necessary for the constructor\r
+ if (extent != null) {\r
+ this.min = this.sub(extent);\r
+ this.max = this.add(extent);\r
+ }\r
+ return this;\r
+ }\r
+\r
+ public final float minX() {\r
+ return x - extent.x;\r
+ }\r
+\r
+ public final float maxX() {\r
+ return x + extent.x;\r
+ }\r
+\r
+ public final float minY() {\r
+ return y - extent.y;\r
+ }\r
+\r
+ public final float maxY() {\r
+ return y + extent.y;\r
+ }\r
+\r
+ public final float minZ() {\r
+ return z - extent.z;\r
+ }\r
+\r
+ public final float maxZ() {\r
+ return z + extent.z;\r
+ }\r
+\r
+ public final Vec3D getMin() {\r
+ return this.sub(extent);\r
+ }\r
+\r
+ public final Vec3D getMax() {\r
+ return this.add(extent);\r
+ }\r
+\r
+ /**\r
+ * @param c\r
+ * sphere centre\r
+ * @param r\r
+ * sphere radius\r
+ * @return true, if AABB intersects with sphere\r
+ */\r
+ public boolean intersectsSphere(Vec3D c, float r) {\r
+ float s, d = 0;\r
+ // find the square of the distance\r
+ // from the sphere to the box\r
+ if (c.x < min.x) {\r
+ s = c.x - min.x;\r
+ d += s * s;\r
+ } else if (c.x > max.x) {\r
+ s = c.x - max.x;\r
+ d += s * s;\r
+ }\r
+\r
+ if (c.y < min.y) {\r
+ s = c.y - min.y;\r
+ d += s * s;\r
+ } else if (c.y > max.y) {\r
+ s = c.y - max.y;\r
+ d += s * s;\r
+ }\r
+\r
+ if (c.z < min.z) {\r
+ s = c.z - min.z;\r
+ d += s * s;\r
+ } else if (c.z > max.z) {\r
+ s = c.z - max.z;\r
+ d += s * s;\r
+ }\r
+\r
+ return d <= r * r;\r
+ }\r
+\r
+ public boolean intersectsSphere(Sphere s) {\r
+ return intersectsSphere(s, s.radius);\r
+ }\r
+\r
+ /**\r
+ * Checks if the box intersects the passed in one.\r
+ * \r
+ * @param box\r
+ * box to check\r
+ * @return true, if boxes overlap\r
+ */\r
+ public boolean intersectsBox(AABB box) {\r
+ Vec3D t = box.sub(this);\r
+ return MathUtils.abs(t.x) <= (extent.x + box.extent.x)\r
+ && MathUtils.abs(t.y) <= (extent.y + box.extent.y)\r
+ && MathUtils.abs(t.z) <= (extent.z + box.extent.z);\r
+ }\r
+\r
+ /**\r
+ * Calculates intersection with the given ray between a certain distance\r
+ * interval.\r
+ * \r
+ * Ray-box intersection is using IEEE numerical properties to ensure the\r
+ * test is both robust and efficient, as described in:\r
+ * \r
+ * Amy Williams, Steve Barrus, R. Keith Morley, and Peter Shirley: "An\r
+ * Efficient and Robust Ray-Box Intersection Algorithm" Journal of graphics\r
+ * tools, 10(1):49-54, 2005\r
+ * \r
+ * @param ray\r
+ * incident ray\r
+ * @param minDir\r
+ * @param maxDir\r
+ * @return intersection point on the bounding box (only the first is\r
+ * returned) or null if no intersection\r
+ */\r
+ public Vec3D intersectsRay(Ray3D ray, float minDir, float maxDir) {\r
+ Vec3D invDir = new Vec3D(1f / ray.dir.x, 1f / ray.dir.y, 1f / ray.dir.z);\r
+ boolean signDirX = invDir.x < 0;\r
+ boolean signDirY = invDir.y < 0;\r
+ boolean signDirZ = invDir.z < 0;\r
+ Vec3D min = getMin();\r
+ Vec3D max = getMax();\r
+ Vec3D bbox = signDirX ? max : min;\r
+ float tmin = (bbox.x - ray.x) * invDir.x;\r
+ bbox = signDirX ? min : max;\r
+ float tmax = (bbox.x - ray.x) * invDir.x;\r
+ bbox = signDirY ? max : min;\r
+ float tymin = (bbox.y - ray.y) * invDir.y;\r
+ bbox = signDirY ? min : max;\r
+ float tymax = (bbox.y - ray.y) * invDir.y;\r
+\r
+ if ((tmin > tymax) || (tymin > tmax))\r
+ return null;\r
+ if (tymin > tmin)\r
+ tmin = tymin;\r
+ if (tymax < tmax)\r
+ tmax = tymax;\r
+\r
+ bbox = signDirZ ? max : min;\r
+ float tzmin = (bbox.z - ray.z) * invDir.z;\r
+ bbox = signDirZ ? min : max;\r
+ float tzmax = (bbox.z - ray.z) * invDir.z;\r
+\r
+ if ((tmin > tzmax) || (tzmin > tmax))\r
+ return null;\r
+ if (tzmin > tmin)\r
+ tmin = tzmin;\r
+ if (tzmax < tmax)\r
+ tmax = tzmax;\r
+ if ((tmin < maxDir) && (tmax > minDir)) {\r
+ return ray.getPointAtDistance(tmin);\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see toxi.geom.Vec3D#toString()\r
+ */\r
+ public String toString() {\r
+ StringBuffer sb = new StringBuffer();\r
+ sb.append("<aabb> pos: ").append(super.toString()).append(" ext: ")\r
+ .append(extent);\r
+ return sb.toString();\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.geom;\r
+\r
+/**\r
+ * Generic interface for ray intersection with 3D geometry\r
+ */\r
+public interface Intersector {\r
+\r
+ /**\r
+ * Check if entity intersects with the given ray\r
+ * @param ray ray to check\r
+ * @return true, if ray hits the entity\r
+ */\r
+ public abstract boolean intersectsRay(Ray3D ray);\r
+\r
+ /**\r
+ * @return point of intersection on the entity surface\r
+ */\r
+ public abstract Vec3D getIntersectionPoint();\r
+\r
+ /**\r
+ * @return distance from ray origin to intersection point\r
+ */\r
+ public abstract float getIntersectionDistance();\r
+\r
+ /**\r
+ * @return entity's surface normal vector at intersection point\r
+ */\r
+ public Vec3D getNormalAtIntersection();\r
+ \r
+ /**\r
+ * @param normalized true, if a normalized version should be returned\r
+ * @return direction vector from ray origin to intersection point\r
+ */\r
+ public abstract Vec3D getIntersectionDir(boolean normalized);\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package toxi.geom;\r
+\r
+/**\r
+ * Implements a simple row-major 4x4 matrix class, all matrix operations are\r
+ * applied to new instances. Use {@link #transpose()} to convert from\r
+ * column-major formats...\r
+ */\r
+\r
+// FIXME considere OpenGL is using column-major ordering\r
+// TODO add methods to apply to current instance only\r
+// TODO still needs lots of refactoring\r
+public class Matrix4x4 {\r
+\r
+ public double[][] matrix;\r
+\r
+ static double[] retval = new double[3];\r
+\r
+ // Default constructor\r
+ // Sets matrix to identity\r
+ public Matrix4x4() {\r
+ init();\r
+ matrix[0][0] = 1;\r
+ matrix[1][1] = 1;\r
+ matrix[2][2] = 1;\r
+ matrix[3][3] = 1;\r
+ }\r
+\r
+ public Matrix4x4(Matrix4x4 m) {\r
+ for (int i = 0; i < 4; i++)\r
+ for (int j = 0; j < 4; j++)\r
+ matrix[i][j] = m.matrix[i][j];\r
+ }\r
+\r
+ public Matrix4x4(double v11, double v12, double v13, double v14,\r
+ double v21, double v22, double v23, double v24, double v31,\r
+ double v32, double v33, double v34, double v41, double v42,\r
+ double v43, double v44) {\r
+ init();\r
+\r
+ matrix[0][0] = v11;\r
+ matrix[0][1] = v12;\r
+ matrix[0][2] = v13;\r
+ matrix[0][3] = v14;\r
+\r
+ matrix[1][0] = v21;\r
+ matrix[1][1] = v22;\r
+ matrix[1][2] = v23;\r
+ matrix[1][3] = v24;\r
+\r
+ matrix[2][0] = v31;\r
+ matrix[2][1] = v32;\r
+ matrix[2][2] = v33;\r
+ matrix[2][3] = v34;\r
+\r
+ matrix[3][0] = v41;\r
+ matrix[3][1] = v42;\r
+ matrix[3][2] = v43;\r
+ matrix[3][3] = v44;\r
+ }\r
+\r
+ // Initializing constructor - single-dimensional array\r
+ // Assumes row-major ordering (column idx increases faster)\r
+ public Matrix4x4(double[] array) {\r
+ if (array.length != 9 && array.length != 16) {\r
+ throw new RuntimeException("Array.length must == 9 or 16");\r
+ }\r
+\r
+ init();\r
+\r
+ if (array.length == 16) {\r
+ matrix[0][0] = array[0];\r
+ matrix[0][1] = array[1];\r
+ matrix[0][2] = array[2];\r
+ matrix[0][3] = array[3];\r
+\r
+ matrix[1][0] = array[4];\r
+ matrix[1][1] = array[5];\r
+ matrix[1][2] = array[6];\r
+ matrix[1][3] = array[7];\r
+\r
+ matrix[2][0] = array[8];\r
+ matrix[2][1] = array[9];\r
+ matrix[2][2] = array[10];\r
+ matrix[2][3] = array[11];\r
+\r
+ matrix[3][0] = array[12];\r
+ matrix[3][1] = array[13];\r
+ matrix[3][2] = array[14];\r
+ matrix[3][3] = array[15];\r
+ } else if (array.length == 9) {\r
+ matrix[0][0] = array[0];\r
+ matrix[0][1] = array[1];\r
+ matrix[0][2] = array[2];\r
+\r
+ matrix[1][0] = array[3];\r
+ matrix[1][1] = array[4];\r
+ matrix[1][2] = array[5];\r
+\r
+ matrix[2][0] = array[6];\r
+ matrix[2][1] = array[7];\r
+ matrix[2][2] = array[8];\r
+\r
+ matrix[3][0] = array[9];\r
+ matrix[3][1] = array[10];\r
+ matrix[3][2] = array[11];\r
+ matrix[3][3] = 1;\r
+ }\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see java.lang.Object#toString()\r
+ */\r
+ public String toString() {\r
+ return "| " + matrix[0][0] + " " + matrix[0][1] + " " + matrix[0][2]\r
+ + " " + matrix[0][3] + " |\n" + "| " + matrix[1][0] + " "\r
+ + matrix[1][1] + " " + matrix[1][2] + " " + matrix[1][3]\r
+ + " |\n" + "| " + matrix[2][0] + " " + matrix[2][1] + " "\r
+ + matrix[2][2] + " " + matrix[2][3] + " |\n" + "| "\r
+ + matrix[3][0] + " " + matrix[3][1] + " " + matrix[3][2] + " "\r
+ + matrix[3][3] + " |";\r
+ }\r
+\r
+ public Matrix4x4 identity() {\r
+ for (int i = 0; i < 4; i++)\r
+ for (int j = 0; j < 4; j++)\r
+ matrix[i][j] = 0;\r
+ matrix[0][0] = 1;\r
+ matrix[1][1] = 1;\r
+ matrix[2][2] = 1;\r
+ matrix[3][3] = 1;\r
+ return this;\r
+ }\r
+\r
+ public Matrix4x4 add(Matrix4x4 rhs) {\r
+ Matrix4x4 retval = new Matrix4x4();\r
+\r
+ for (int i = 0; i < 4; i++)\r
+ for (int j = 0; j < 4; j++)\r
+ retval.matrix[i][j] = matrix[i][j] + rhs.matrix[i][j];\r
+\r
+ return retval;\r
+ }\r
+\r
+ // Matrix-Matrix Subtraction\r
+ public Matrix4x4 sub(Matrix4x4 rhs) {\r
+ Matrix4x4 retval = new Matrix4x4();\r
+ for (int i = 0; i < 4; i++)\r
+ for (int j = 0; j < 4; j++)\r
+ retval.matrix[i][j] = matrix[i][j] - rhs.matrix[i][j];\r
+\r
+ return retval;\r
+ }\r
+\r
+ // Matrix-Scalar Multiplication\r
+ public Matrix4x4 multiply(double c) {\r
+ Matrix4x4 retval = new Matrix4x4();\r
+ for (int i = 0; i < 4; i++)\r
+ for (int j = 0; j < 4; j++)\r
+ retval.matrix[i][j] = c * matrix[i][j];\r
+ return retval;\r
+ }\r
+\r
+ // Matrix-Vector Multiplication (Application)\r
+ public Vec3D apply(Vec3D vec) {\r
+ // Create a new vector and make it 4d homogenous\r
+ double vectorOut[] = new double[4];\r
+ double vectorIn[] = new double[4];\r
+ vectorIn[0] = vec.x;\r
+ vectorIn[1] = vec.y;\r
+ vectorIn[2] = vec.z;\r
+ vectorIn[3] = 1;\r
+\r
+ for (int i = 0; i < 4; i++) {\r
+ for (int j = 0; j < 4; j++)\r
+ vectorOut[i] += vectorIn[j] * matrix[i][j];\r
+ }\r
+\r
+ return new Vec3D((float) vectorOut[0], (float) vectorOut[1],\r
+ (float) vectorOut[2]).scaleSelf((float) (1 / vectorOut[3]));\r
+ }\r
+\r
+ // Matrix-Matrix Left-multiplication\r
+ // Given matrix this, mat; performs mat*this\r
+ public Matrix4x4 leftMultiply(Matrix4x4 mat) {\r
+ return mat.multiply(this);\r
+ }\r
+\r
+ // Matrix-Matrix Right-multiplication\r
+ public Matrix4x4 multiply(Matrix4x4 mat) {\r
+ Matrix4x4 retval = new Matrix4x4();\r
+ for (int i = 0; i < 4; i++) {\r
+ for (int j = 0; j < 4; j++) {\r
+ retval.matrix[i][j] = 0;\r
+ for (int k = 0; k < 4; k++) {\r
+ retval.matrix[i][j] += matrix[i][k] * mat.matrix[k][j];\r
+ }\r
+ }\r
+ }\r
+ return retval;\r
+ }\r
+\r
+ // Apply Translation to Matrix\r
+ public Matrix4x4 translate(double dx, double dy, double dz) {\r
+ Matrix4x4 trn = new Matrix4x4();\r
+ trn.matrix[0][3] = dx;\r
+ trn.matrix[1][3] = dy;\r
+ trn.matrix[2][3] = dz;\r
+ // return trn.mul(this);\r
+ return trn.leftMultiply(this);\r
+ }\r
+\r
+ // Apply Scale to Matrix\r
+ public Matrix4x4 scale(double scaleX, double scaleY, double scaleZ) {\r
+ Matrix4x4 scl = new Matrix4x4();\r
+ scl.matrix[0][0] = scaleX;\r
+ scl.matrix[1][1] = scaleY;\r
+ scl.matrix[2][2] = scaleZ;\r
+ // return scl.mul(this);\r
+ return scl.leftMultiply(this);\r
+ }\r
+\r
+ // Apply Rotation about X to Matrix\r
+ public Matrix4x4 rotateX(double theta) {\r
+ Matrix4x4 rot = new Matrix4x4();\r
+ rot.matrix[1][1] = rot.matrix[2][2] = Math.cos(theta);\r
+ rot.matrix[2][1] = Math.sin(theta);\r
+ rot.matrix[1][2] = -rot.matrix[2][1];\r
+ return rot.leftMultiply(this);\r
+ }\r
+\r
+ // Apply Rotation about Y to Matrix\r
+ public Matrix4x4 rotateY(double theta) {\r
+ Matrix4x4 rot = new Matrix4x4();\r
+ rot.matrix[0][0] = rot.matrix[2][2] = Math.cos(theta);\r
+ rot.matrix[0][2] = Math.sin(theta);\r
+ rot.matrix[2][0] = -rot.matrix[0][2];\r
+ return rot.leftMultiply(this);\r
+ }\r
+\r
+ // Apply Rotation about Z to Matrix\r
+ public Matrix4x4 rotateZ(double theta) {\r
+ Matrix4x4 rot = new Matrix4x4();\r
+ rot.matrix[0][0] = rot.matrix[1][1] = Math.cos(theta);\r
+ rot.matrix[1][0] = Math.sin(theta);\r
+ rot.matrix[0][1] = -rot.matrix[1][0];\r
+ return rot.leftMultiply(this);\r
+ }\r
+\r
+ // Apply Rotation about arbitrary axis to Matrix\r
+ public Matrix4x4 rotate(Vec3D axis, double theta) {\r
+ double x, y, z, s, c, t;\r
+ x = axis.x;\r
+ y = axis.y;\r
+ z = axis.z;\r
+ s = Math.sin(theta);\r
+ c = Math.cos(theta);\r
+ // c = Math.sqrt(1 - s * s); // this may be faster than the previous\r
+ // line\r
+ t = 1 - c;\r
+\r
+ return (new Matrix4x4(t * x * x + c, t * x * y + s * z, t * x * z - s\r
+ * y, 0, t * x * y - s * z, t * y * y + c, t * y * z + s * x, 0,\r
+ t * x * z + s * y, t * y * z - s * x, t * z * z + c, 0, 0, 0,\r
+ 0, 1)).leftMultiply(this);\r
+ }\r
+\r
+ // Matrix Transpose\r
+ public Matrix4x4 transpose() {\r
+ return new Matrix4x4(matrix[0][0], matrix[1][0], matrix[2][0],\r
+ matrix[3][0], matrix[0][1], matrix[1][1], matrix[2][1],\r
+ matrix[3][1], matrix[0][2], matrix[1][2], matrix[2][2],\r
+ matrix[3][2], matrix[0][3], matrix[1][3], matrix[2][3],\r
+ matrix[3][3]);\r
+ }\r
+\r
+ // Matrix Inversion using Cramer's Method\r
+ // Computes Adjoint matrix divided by determinant\r
+ // Code modified from\r
+ // http://www.intel.com/design/pentiumiii/sml/24504301.pdf\r
+ // Turns out we don't need this after all\r
+ public Matrix4x4 inverse() {\r
+ double[] mat = new double[16];\r
+ double[] dst = new double[16];\r
+\r
+ // Copy all of the elements into the linear array\r
+ for (int i = 0, k = 0; i < 4; i++)\r
+ for (int j = 0; j < 4; j++)\r
+ mat[k++] = matrix[i][j];\r
+\r
+ double[] tmp = new double[12];\r
+ double src[] = new double[16];\r
+ double det;\r
+\r
+ for (int i = 0; i < 4; i++) {\r
+ int i4 = i << 2;\r
+ src[i] = mat[i4];\r
+ src[i + 4] = mat[i4 + 1];\r
+ src[i + 8] = mat[i4 + 2];\r
+ src[i + 12] = mat[i4 + 3];\r
+ }\r
+\r
+ /* calculatepairsforfirst8elements(cofactors) */\r
+ tmp[0] = src[10] * src[15];\r
+ tmp[1] = src[11] * src[14];\r
+ tmp[2] = src[9] * src[15];\r
+ tmp[3] = src[11] * src[13];\r
+ tmp[4] = src[9] * src[14];\r
+ tmp[5] = src[10] * src[13];\r
+ tmp[6] = src[8] * src[15];\r
+ tmp[7] = src[11] * src[12];\r
+ tmp[8] = src[8] * src[14];\r
+ tmp[9] = src[10] * src[12];\r
+ tmp[10] = src[8] * src[13];\r
+ tmp[11] = src[9] * src[12];\r
+\r
+ /* calculatefirst8elements(cofactors) */\r
+ dst[0] = tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7];\r
+ dst[0] -= tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7];\r
+ dst[1] = tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7];\r
+ dst[1] -= tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7];\r
+ dst[2] = tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7];\r
+ dst[2] -= tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7];\r
+ dst[3] = tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6];\r
+ dst[3] -= tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6];\r
+ dst[4] = tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3];\r
+ dst[4] -= tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3];\r
+ dst[5] = tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3];\r
+ dst[5] -= tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3];\r
+ dst[6] = tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3];\r
+ dst[6] -= tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3];\r
+ dst[7] = tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2];\r
+ dst[7] -= tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2];\r
+\r
+ /* calculatepairsforsecond8elements(cofactors) */\r
+ tmp[0] = src[2] * src[7];\r
+ tmp[1] = src[3] * src[6];\r
+ tmp[2] = src[1] * src[7];\r
+ tmp[3] = src[3] * src[5];\r
+ tmp[4] = src[1] * src[6];\r
+ tmp[5] = src[2] * src[5];\r
+ tmp[6] = src[0] * src[7];\r
+ tmp[7] = src[3] * src[4];\r
+ tmp[8] = src[0] * src[6];\r
+ tmp[9] = src[2] * src[4];\r
+ tmp[10] = src[0] * src[5];\r
+ tmp[11] = src[1] * src[4];\r
+\r
+ /* calculatesecond8elements(cofactors) */\r
+ dst[8] = tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15];\r
+ dst[8] -= tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15];\r
+ dst[9] = tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15];\r
+ dst[9] -= tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15];\r
+ dst[10] = tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15];\r
+ dst[10] -= tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15];\r
+ dst[11] = tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14];\r
+ dst[11] -= tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14];\r
+ dst[12] = tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9];\r
+ dst[12] -= tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10];\r
+ dst[13] = tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10];\r
+ dst[13] -= tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8];\r
+ dst[14] = tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8];\r
+ dst[14] -= tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9];\r
+ dst[15] = tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9];\r
+ dst[15] -= tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8];\r
+\r
+ det = src[0] * dst[0] + src[1] * dst[1] + src[2] * dst[2] + src[3]\r
+ * dst[3];\r
+\r
+ det = 1 / det;\r
+\r
+ Matrix4x4 ret = new Matrix4x4();\r
+ for (int i = 0, k = 0; i < 4; i++)\r
+ for (int j = 0; j < 4; j++)\r
+ ret.matrix[i][j] = dst[k++] * det;\r
+ return ret;\r
+ }\r
+\r
+ private final void init() {\r
+ matrix = new double[4][];\r
+ matrix[0] = new double[4];\r
+ matrix[1] = new double[4];\r
+ matrix[2] = new double[4];\r
+ matrix[3] = new double[4];\r
+ }\r
+}\r
--- /dev/null
+package toxi.geom;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+/**\r
+ * Class to describe and work with infinite generic 3D planes. Useful for\r
+ * intersection problems and classifying points.\r
+ * \r
+ * @author Karsten Schmidt\r
+ * \r
+ */\r
+public class Plane extends Vec3D {\r
+\r
+ public static final Plane XY = new Plane(new Vec3D(), Vec3D.Z_AXIS);\r
+ public static final Plane XZ = new Plane(new Vec3D(), Vec3D.Y_AXIS);\r
+ public static final Plane YZ = new Plane(new Vec3D(), Vec3D.X_AXIS);\r
+\r
+ /**\r
+ * Classifier constant for {@link #classifyPoint(Vec3D)}\r
+ */\r
+ public static final int PLANE_FRONT = -1;\r
+\r
+ /**\r
+ * Classifier constant for {@link #classifyPoint(Vec3D)}\r
+ */\r
+ public static final int PLANE_BACK = 1;\r
+\r
+ /**\r
+ * Classifier constant for {@link #classifyPoint(Vec3D)}\r
+ */\r
+ public static final int ON_PLANE = 0;\r
+\r
+ public Vec3D normal;\r
+\r
+ public Plane(Vec3D origin, Vec3D norm) {\r
+ super(origin);\r
+ normal = norm.getNormalized();\r
+ }\r
+\r
+ // TODO add constructor for creating a plane from a Triangle or 3 Vec3D's\r
+\r
+ /**\r
+ * Calculates distance from the plane to point P.\r
+ * \r
+ * @param p\r
+ * @return distance\r
+ */\r
+ public float getDistanceToPoint(Vec3D p) {\r
+ float sn = -normal.dot(p.sub(this));\r
+ float sd = normal.magSquared();\r
+ Vec3D isec = p.add(normal.scale(sn / sd));\r
+ return isec.distanceTo(p);\r
+ }\r
+\r
+ /**\r
+ * Calculates the intersection point between plane and ray (line).\r
+ * \r
+ * @param r\r
+ * @return intersection point or null if ray doesn't intersect plane\r
+ */\r
+ public Vec3D getIntersectionWithRay(Ray3D r) {\r
+ float denom = normal.dot(r.getDirection());\r
+ if (denom > MathUtils.EPS) {\r
+ float u = normal.dot(this.sub(r)) / denom;\r
+ return r.getPointAtDistance(u);\r
+ } else\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Classifies the relative position of the given point to the plane.\r
+ * \r
+ * @return One of the 3 integer classification codes: PLANE_FRONT,\r
+ * PLANE_BACK, ON_PLANE\r
+ */\r
+ public int classifyPoint(Vec3D p) {\r
+ float d = this.sub(p).dot(normal);\r
+ if (d < -MathUtils.EPS)\r
+ return PLANE_FRONT;\r
+ else if (d > MathUtils.EPS)\r
+ return PLANE_BACK;\r
+ return ON_PLANE;\r
+ }\r
+\r
+ public String toString() {\r
+ StringBuffer sb = new StringBuffer();\r
+ sb.append("origin: ").append(super.toString()).append(" norm: ")\r
+ .append(normal.toString());\r
+ return sb.toString();\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006-2008 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.geom;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+\r
+/**\r
+ * This class enables group/set operations of multiple Vec3D's at once.\r
+ * \r
+ * @author Karsten Schmidt\r
+ *\r
+ */\r
+\r
+@SuppressWarnings("unchecked")\r
+public class PointList extends ArrayList {\r
+ \r
+ public PointList() {\r
+ super();\r
+ }\r
+ \r
+ public PointList addSelf(Vec3D offset) {\r
+ Iterator i=iterator();\r
+ while(i.hasNext()) {\r
+ ((Vec3D)i.next()).addSelf(offset);\r
+ }\r
+ return this;\r
+ }\r
+ \r
+ public PointList subSelf(Vec3D offset) {\r
+ Iterator i=iterator();\r
+ while(i.hasNext()) {\r
+ ((Vec3D)i.next()).subSelf(offset);\r
+ }\r
+ return this;\r
+ }\r
+ \r
+ public PointList scaleSelf(Vec3D factor) {\r
+ Iterator i=iterator();\r
+ while(i.hasNext()) {\r
+ ((Vec3D)i.next()).scaleSelf(factor);\r
+ }\r
+ return this;\r
+ }\r
+ \r
+ public PointList scaleSelf(float factor) {\r
+ Iterator i=iterator();\r
+ while(i.hasNext()) {\r
+ ((Vec3D)i.next()).scaleSelf(factor);\r
+ }\r
+ return this;\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.geom;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Iterator;\r
+\r
+/**\r
+ * Implements a spatial subdivision tree to work efficiently with large numbers\r
+ * of 3D particles. This octree can only be used for particle type objects and\r
+ * does NOT support 3D mesh geometry as other forms of Octrees do.\r
+ * \r
+ * For further reference also see the OctreeDemo in the /examples folder.\r
+ * \r
+ */\r
+public class PointOctree extends AABB {\r
+\r
+ /**\r
+ * alternative tree recursion limit, number of world units when cells are\r
+ * not subdivided any further\r
+ */\r
+ protected float minNodeSize = 4;\r
+\r
+ /**\r
+ * \r
+ */\r
+ protected PointOctree parent;\r
+\r
+ protected PointOctree[] children;\r
+\r
+ protected byte numChildren;\r
+\r
+ protected ArrayList<Vec3D> data;\r
+\r
+ protected float dim, dim2;\r
+\r
+ protected Vec3D offset;\r
+\r
+ protected int depth = 0;\r
+\r
+ private boolean isAutoReducing = false;\r
+\r
+ /**\r
+ * Constructs a new PointOctree node within the AABB cube volume: {o.x, o.y,\r
+ * o.z} ... {o.x+size, o.y+size, o.z+size}\r
+ * \r
+ * @param o\r
+ * tree origin\r
+ * @param size\r
+ * size of the tree volume along a single axis\r
+ */\r
+ public PointOctree(Vec3D o, float size) {\r
+ this(null, o, size / 2);\r
+ }\r
+\r
+ /**\r
+ * Constructs a new PointOctree node within the AABB cube volume: {o.x, o.y,\r
+ * o.z} ... {o.x+size, o.y+size, o.z+size}\r
+ * \r
+ * @param p\r
+ * parent node\r
+ * @param o\r
+ * tree origin\r
+ * @param halfSize\r
+ * half length of the tree volume along a single axis\r
+ */\r
+ private PointOctree(PointOctree p, Vec3D o, float halfSize) {\r
+ super(o.add(halfSize, halfSize, halfSize), new Vec3D(halfSize,\r
+ halfSize, halfSize));\r
+ parent = p;\r
+ if (parent != null)\r
+ depth = parent.depth + 1;\r
+ dim = halfSize * 2;\r
+ dim2 = halfSize;\r
+ offset = o;\r
+ numChildren = 0;\r
+ }\r
+\r
+ /**\r
+ * Computes the local child octant/cube index for the given point\r
+ * \r
+ * @param plocal\r
+ * point in the node-local coordinate system\r
+ * @return octant index\r
+ */\r
+ protected final int getOctantID(Vec3D plocal) {\r
+ return (plocal.x >= dim2 ? 1 : 0) + (plocal.y >= dim2 ? 2 : 0)\r
+ + (plocal.z >= dim2 ? 4 : 0);\r
+ }\r
+\r
+ /**\r
+ * Adds a new point/particle to the tree structure. All points are stored\r
+ * within leaf nodes only. The tree implementation is using lazy\r
+ * instantiation for all intermediate tree levels.\r
+ * \r
+ * @param p\r
+ * @return true, if point has been added successfully\r
+ */\r
+ public boolean addPoint(Vec3D p) {\r
+ // check if point is inside cube\r
+ if (p.isInAABB(this)) {\r
+ // only add data to leaves for now\r
+ if (dim2 <= minNodeSize) {\r
+ if (data == null) {\r
+ data = new ArrayList<Vec3D>();\r
+ }\r
+ data.add(p);\r
+ return true;\r
+ } else {\r
+ Vec3D plocal = p.sub(offset);\r
+ if (children == null) {\r
+ children = new PointOctree[8];\r
+ }\r
+ int octant = getOctantID(plocal);\r
+ if (children[octant] == null) {\r
+ Vec3D off = offset.add(new Vec3D((octant & 1) != 0 ? dim2\r
+ : 0, (octant & 2) != 0 ? dim2 : 0,\r
+ (octant & 4) != 0 ? dim2 : 0));\r
+ children[octant] = new PointOctree(this, off, dim2 / 2);\r
+ numChildren++;\r
+ }\r
+ return children[octant].addPoint(p);\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Adds all points of the collection to the octree. IMPORTANT: Points need\r
+ * be of type Vec3D or have subclassed it.\r
+ * \r
+ * @param points\r
+ * point collection\r
+ * @return true, if all points have been added successfully.\r
+ */\r
+ public boolean addAll(Collection<Vec3D> points) {\r
+ Iterator<Vec3D> i = points.iterator();\r
+ boolean addedAll = true;\r
+ while (i.hasNext()) {\r
+ addedAll &= addPoint((Vec3D) i.next());\r
+ }\r
+ return addedAll;\r
+ }\r
+\r
+ /**\r
+ * Enables/disables auto reduction of branches after points have been\r
+ * deleted from the tree. Turned off by default.\r
+ * \r
+ * @param state\r
+ * true, to enable feature\r
+ */\r
+ public void setTreeAutoReduction(boolean state) {\r
+ isAutoReducing = state;\r
+ }\r
+\r
+ /**\r
+ * Removes a point from the tree and (optionally) tries to release memory by\r
+ * reducing now empty sub-branches.\r
+ * \r
+ * @param p\r
+ * point to delete\r
+ * @return true, if the point was found & removed\r
+ */\r
+ public boolean remove(Vec3D p) {\r
+ boolean found = false;\r
+ PointOctree leaf = getLeafForPoint(p);\r
+ if (leaf != null) {\r
+ if (leaf.data.remove(p)) {\r
+ found = true;\r
+ if (isAutoReducing && leaf.data.size() == 0) {\r
+ leaf.reduceBranch();\r
+ }\r
+ }\r
+ }\r
+ return found;\r
+ }\r
+\r
+ public void removeAll(Collection<Vec3D> points) {\r
+ Iterator<Vec3D> i = points.iterator();\r
+ while (i.hasNext()) {\r
+ remove((Vec3D) i.next());\r
+ }\r
+ }\r
+\r
+ private void reduceBranch() {\r
+ if (data != null && data.size() == 0)\r
+ data = null;\r
+ if (numChildren > 0) {\r
+ for (int i = 0; i < 8; i++) {\r
+ if (children[i] != null && children[i].data == null)\r
+ children[i] = null;\r
+ }\r
+ }\r
+ if (parent != null) {\r
+ parent.reduceBranch();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Finds the leaf node which spatially relates to the given point\r
+ * \r
+ * @param p\r
+ * point to check\r
+ * @return leaf node or null if point is outside the tree dimensions\r
+ */\r
+ protected PointOctree getLeafForPoint(Vec3D p) {\r
+ // if not a leaf node...\r
+ if (p.isInAABB(this)) {\r
+ if (numChildren > 0) {\r
+ int octant = getOctantID(p.sub(offset));\r
+ if (children[octant] != null) {\r
+ return children[octant].getLeafForPoint(p);\r
+ }\r
+ } else if (data != null) {\r
+ return this;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Selects all stored points within the given sphere volume\r
+ * \r
+ * @param sphereOrigin\r
+ * @param clipRadius\r
+ * @return selected points\r
+ */\r
+ public ArrayList<Vec3D> getPointsWithinSphere(Vec3D sphereOrigin,\r
+ float clipRadius) {\r
+ return getPointsWithinSphere(new Sphere(sphereOrigin, clipRadius));\r
+ }\r
+\r
+ /**\r
+ * Selects all stored points within the given sphere volume\r
+ * \r
+ * @param s\r
+ * sphere\r
+ * @return selected points\r
+ */\r
+ public ArrayList<Vec3D> getPointsWithinSphere(Sphere s) {\r
+ ArrayList<Vec3D> results = null;\r
+ if (this.intersectsSphere(s)) {\r
+ if (data != null) {\r
+ for (int i = data.size() - 1; i >= 0; i--) {\r
+ Vec3D q = (Vec3D) data.get(i);\r
+ if (q.isInSphere(s)) {\r
+ if (results == null) {\r
+ results = new ArrayList<Vec3D>();\r
+ }\r
+ results.add(q);\r
+ }\r
+ }\r
+ } else if (numChildren > 0) {\r
+ for (int i = 0; i < 8; i++) {\r
+ if (children[i] != null) {\r
+ ArrayList<Vec3D> points = children[i]\r
+ .getPointsWithinSphere(s);\r
+ if (points != null) {\r
+ if (results == null) {\r
+ results = new ArrayList<Vec3D>();\r
+ }\r
+ results.addAll(points);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return results;\r
+ }\r
+\r
+ /**\r
+ * Selects all stored points within the given axis-aligned bounding box.\r
+ * \r
+ * @param b\r
+ * AABB\r
+ * @return all points with the box volume\r
+ */\r
+ public ArrayList<Vec3D> getPointsWithinBox(AABB b) {\r
+ ArrayList<Vec3D> results = null;\r
+ if (this.intersectsBox(b)) {\r
+ if (data != null) {\r
+ for (int i = data.size() - 1; i >= 0; i--) {\r
+ Vec3D q = (Vec3D) data.get(i);\r
+ if (q.isInAABB(b)) {\r
+ if (results == null) {\r
+ results = new ArrayList<Vec3D>();\r
+ }\r
+ results.add(q);\r
+ }\r
+ }\r
+ } else if (numChildren > 0) {\r
+ for (int i = 0; i < 8; i++) {\r
+ if (children[i] != null) {\r
+ ArrayList<Vec3D> points = children[i]\r
+ .getPointsWithinBox(b);\r
+ if (points != null) {\r
+ if (results == null) {\r
+ results = new ArrayList<Vec3D>();\r
+ }\r
+ results.addAll(points);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return results;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see toxi.geom.AABB#toString()\r
+ */\r
+ public String toString() {\r
+ return "<octree> offset: " + super.toString() + " size: " + dim;\r
+ }\r
+\r
+ /**\r
+ * Returns the minimum size of nodes (in world units). This value acts as\r
+ * tree recursion limit since nodes smaller than this size are not\r
+ * subdivided further. Leaf node are always smaller or equal to this size.\r
+ * \r
+ * @return the minimum size of tree nodes\r
+ */\r
+ public float getMinNodeSize() {\r
+ return minNodeSize;\r
+ }\r
+\r
+ /**\r
+ * @param minNodeSize\r
+ */\r
+ public void setMinNodeSize(float minNodeSize) {\r
+ this.minNodeSize = minNodeSize;\r
+ }\r
+\r
+ public float getNodeSize() {\r
+ return dim;\r
+ }\r
+\r
+ /**\r
+ * @return the number of child nodes (max. 8)\r
+ */\r
+ public int getNumChildren() {\r
+ return numChildren;\r
+ }\r
+\r
+ /**\r
+ * @return a copy of the child nodes array\r
+ */\r
+ public PointOctree[] getChildren() {\r
+ PointOctree[] clones = new PointOctree[8];\r
+ System.arraycopy(children, 0, clones, 0, 8);\r
+ return clones;\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.geom;\r
+\r
+import toxi.geom.Vec3D;\r
+\r
+public class Quad {\r
+ public Vec3D[] vertices;\r
+\r
+ public Quad(Vec3D[] vertices, int vertOffset) {\r
+ this.vertices = new Vec3D[4];\r
+ System.arraycopy(vertices, vertOffset, this.vertices, 0, 4);\r
+ }\r
+\r
+ public Quad(Quad q) {\r
+ vertices = new Vec3D[4];\r
+ for (int i = 0; i < 4; i++) {\r
+ vertices[i] = new Vec3D(q.vertices[i]);\r
+ }\r
+ }\r
+\r
+ public Quad(float x1, float y1, float x2, float y2, float x3, float y3,\r
+ float x4, float y4) {\r
+ vertices = new Vec3D[] { new Vec3D(x1, y1, 0), new Vec3D(x2, y2, 0),\r
+ new Vec3D(x3, y3, 0), new Vec3D(x4, y4, 0) };\r
+ }\r
+}\r
--- /dev/null
+package toxi.geom;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+/**\r
+ * Quaternion implementation with SLERP based on http://is.gd/2n9s\r
+ * \r
+ */\r
+public class Quaternion {\r
+\r
+ public static final float DOT_THRESHOLD = 0.9995f;\r
+\r
+ float x, y, z, w;\r
+\r
+ Quaternion() {\r
+ reset();\r
+ }\r
+\r
+ Quaternion(float w, float x, float y, float z) {\r
+ this.w = w;\r
+ this.x = x;\r
+ this.y = y;\r
+ this.z = z;\r
+ }\r
+\r
+ public Quaternion(float w, Vec3D v) {\r
+ this.x = v.x;\r
+ this.y = v.y;\r
+ this.z = v.z;\r
+ this.w = w;\r
+ }\r
+\r
+ public Quaternion(Quaternion q) {\r
+ this.w = q.w;\r
+ this.x = q.x;\r
+ this.y = q.y;\r
+ this.z = q.z;\r
+ }\r
+\r
+ public Quaternion reset() {\r
+ w = 1.0f;\r
+ x = 0.0f;\r
+ y = 0.0f;\r
+ z = 0.0f;\r
+ return this;\r
+ }\r
+\r
+ public Quaternion set(float w, Vec3D v) {\r
+ this.w = w;\r
+ x = v.x;\r
+ y = v.y;\r
+ z = v.z;\r
+ return this;\r
+ }\r
+\r
+ public Quaternion set(Quaternion q) {\r
+ w = q.w;\r
+ x = q.x;\r
+ y = q.y;\r
+ z = q.z;\r
+ return this;\r
+ }\r
+\r
+ public Quaternion multiply(Quaternion q2) {\r
+ Quaternion res = new Quaternion();\r
+ res.w = w * q2.w - x * q2.x - y * q2.y - z * q2.z;\r
+ res.x = w * q2.x + x * q2.w + y * q2.z - z * q2.y;\r
+ res.y = w * q2.y + y * q2.w + z * q2.x - x * q2.z;\r
+ res.z = w * q2.z + z * q2.w + x * q2.y - y * q2.x;\r
+ return res;\r
+ }\r
+\r
+ public float[] getValue() {\r
+ float[] res = new float[4];\r
+ float sa = (float) Math.sqrt(1.0f - w * w);\r
+ if (sa < MathUtils.EPS)\r
+ sa = 1.0f;\r
+ else\r
+ sa = 1.0f / sa;\r
+ res[0] = (float) Math.acos(w) * 2.0f;\r
+ res[1] = x * sa;\r
+ res[2] = y * sa;\r
+ res[3] = z * sa;\r
+ return res;\r
+ }\r
+\r
+ public float magnitude() {\r
+ return (float) Math.sqrt(x * x + y * y + z * z + w * w);\r
+ }\r
+\r
+ public Quaternion normalize() {\r
+ float mag = (float) Math.sqrt(x * x + y * y + z * z + w * w);\r
+ if (mag > 0) {\r
+ mag = 1f / mag;\r
+ x *= mag;\r
+ y *= mag;\r
+ z *= mag;\r
+ w *= mag;\r
+ }\r
+ return this;\r
+ }\r
+\r
+ public Quaternion getNormalized() {\r
+ return new Quaternion(this).normalize();\r
+ }\r
+\r
+ public Quaternion scale(float t) {\r
+ return new Quaternion(x * t, y * t, z * t, w * t);\r
+ }\r
+\r
+ public Quaternion scaleSelf(float t) {\r
+ x *= t;\r
+ y *= t;\r
+ z *= t;\r
+ w *= t;\r
+ return this;\r
+ }\r
+\r
+ public Quaternion sub(Quaternion q) {\r
+ return new Quaternion(x - q.x, y - q.y, z - q.z, w - q.w);\r
+ }\r
+\r
+ public Quaternion subSelf(Quaternion q) {\r
+ x -= q.x;\r
+ y -= q.y;\r
+ z -= q.z;\r
+ w -= q.w;\r
+ return this;\r
+ }\r
+\r
+ public Quaternion add(Quaternion q) {\r
+ return new Quaternion(x + q.x, y + q.y, z - q.z, w + q.w);\r
+ }\r
+\r
+ public Quaternion addSelf(Quaternion q) {\r
+ x += q.x;\r
+ y += q.y;\r
+ z += q.z;\r
+ w += q.w;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Spherical interpolation to target quat (code ported from\r
+ * http://is.gd/2n7t)\r
+ * \r
+ * @param target\r
+ * quaternion\r
+ * @param t\r
+ * interpolation factor (0..1)\r
+ * @return new interpolated quat\r
+ */\r
+ public Quaternion interpolateTo(Quaternion target, float t) {\r
+ float dot = new Vec3D(x, y, z).dot(new Vec3D(target.x, target.y,\r
+ target.z));\r
+ if (dot > DOT_THRESHOLD) {\r
+ Quaternion result = new Quaternion(w + (target.w - w) * t, x\r
+ + (target.x - x) * t, y + (target.y - y) * t, z\r
+ + (target.z - z) * t);\r
+ result.normalize();\r
+ return result;\r
+ }\r
+ dot = MathUtils.clip(dot, -1, 1);\r
+ double theta = Math.acos(dot) * t;\r
+ Quaternion q = target.sub(scale(dot));\r
+ q.normalize();\r
+ return scale((float) Math.cos(theta)).addSelf(\r
+ q.scaleSelf((float) Math.sin(theta)));\r
+ }\r
+\r
+ /**\r
+ * Converts the quat into a 4x4 Matrix. Assumes the quat is currently\r
+ * normalized (if not, you'll need to call {@link #normalize()} first). This\r
+ * calculation would be a lot more complicated for non-unit length\r
+ * quaternions Note: The constructor of Matrix4 expects the Matrix in\r
+ * column-major format like expected by OpenGL\r
+ * \r
+ * @return result matrix\r
+ */\r
+ public Matrix4x4 getMatrix() {\r
+ float x2 = x * x;\r
+ float y2 = y * y;\r
+ float z2 = z * z;\r
+ float xy = x * y;\r
+ float xz = x * z;\r
+ float yz = y * z;\r
+ float wx = w * x;\r
+ float wy = w * y;\r
+ float wz = w * z;\r
+\r
+ return new Matrix4x4(1.0f - 2.0f * (y2 + z2), 2.0f * (xy - wz),\r
+ 2.0f * (xz + wy), 0.0f, 2.0f * (xy + wz),\r
+ 1.0f - 2.0f * (x2 + z2), 2.0f * (yz - wx), 0.0f,\r
+ 2.0f * (xz - wy), 2.0f * (yz + wx), 1.0f - 2.0f * (x2 + y2),\r
+ 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);\r
+ }\r
+\r
+ public String toString() {\r
+ StringBuffer sb = new StringBuffer(48);\r
+ sb.append("{axis: [").append(x).append(",").append(y).append(",")\r
+ .append(z).append("], w: ").append(w).append("}");\r
+ return sb.toString();\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.geom;\r
+\r
+/**\r
+ * A simple 3D ray datatype\r
+ */\r
+public class Ray3D extends Vec3D {\r
+ protected Vec3D dir;\r
+ \r
+ public Ray3D(float x, float y, float z, Vec3D d) {\r
+ super(x,y,z);\r
+ dir=d.getNormalized();\r
+ }\r
+ \r
+ public Ray3D(Vec3D o, Vec3D d) {\r
+ super(o);\r
+ dir=d.getNormalized();\r
+ }\r
+\r
+ public Vec3D getDirection() {\r
+ return dir;\r
+ }\r
+ \r
+ public Vec3D getPointAtDistance(float dist) {\r
+ return add(dir.scale(dist));\r
+ }\r
+ \r
+ public String toString() {\r
+ StringBuffer sb=new StringBuffer();\r
+ sb.append("origin: ").append(super.toString()).append(" dir: ").append(dir);\r
+ return sb.toString();\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.geom;\r
+\r
+/**\r
+ * Generic interface for ray reflection with 3D geometry\r
+ */\r
+public interface Reflector extends Intersector {\r
+\r
+ /**\r
+ * Reflects given ray on the entity's surface\r
+ * \r
+ * @param ray incident ray\r
+ * @return reflected ray starting from intersection point\r
+ */\r
+ public Ray3D reflectRay(Ray3D ray);\r
+\r
+ /**\r
+ * @return angle between incident ray and surface normal\r
+ */\r
+ public float getReflectionAngle();\r
+\r
+ /**\r
+ * Returns the point on the reflected ray at given distance from the\r
+ * intersection point\r
+ * \r
+ * @param dist distance from isect position\r
+ * @return point on reflected ray\r
+ */\r
+ public Vec3D getReflectedRayPointAtDistance(float dist);\r
+}
\ No newline at end of file
--- /dev/null
+/* \r
+ * Copyright (c) 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.geom;\r
+\r
+public class Sphere extends Vec3D {\r
+\r
+ float radius;\r
+ \r
+ public Sphere() {\r
+ radius=1;\r
+ }\r
+\r
+ public Sphere(Vec3D v, float r) {\r
+ super(v);\r
+ radius = r;\r
+ }\r
+ \r
+ public Sphere(Sphere s) {\r
+ this(s,s.radius);\r
+ }\r
+}\r
--- /dev/null
+package toxi.geom;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+public class SphereIntersectorReflector implements Intersector, Reflector {\r
+ protected Vec3D sOrigin, sRadius;\r
+\r
+ protected Vec3D isectPos, isectDir;\r
+\r
+ protected float isectDist;\r
+\r
+ protected Vec3D sphereNormal;\r
+\r
+ protected float reflectTheta;\r
+\r
+ protected Vec3D reflectedDir, reflectedPos;\r
+\r
+ public SphereIntersectorReflector(Vec3D o, float r) {\r
+ sOrigin = new Vec3D(o);\r
+ sRadius = new Vec3D(r, r, r);\r
+ }\r
+\r
+ public SphereIntersectorReflector(Sphere s) {\r
+ this(s, s.radius);\r
+ }\r
+\r
+ public boolean intersectsRay(Ray3D ray) {\r
+ isectDist = ray.intersectRaySphere(ray.getDirection(), sOrigin,\r
+ sRadius.x);\r
+ if (isectDist >= 0) {\r
+ // get the intersection point\r
+ isectPos = ray.add(ray.getDirection().scale(isectDist));\r
+ // calculate the direction from our point to the intersection pos\r
+ isectDir = isectPos.sub(ray);\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see toxi.geom.Reflector#reflectRay(toxi.geom.Vec3D, toxi.geom.Vec3D)\r
+ */\r
+ public Ray3D reflectRay(Ray3D ray) {\r
+ if (intersectsRay(ray)) {\r
+ // compute the normal vector of the sphere at the intersection\r
+ // position\r
+ sphereNormal = isectPos.tangentPlaneNormalOfEllipsoid(sOrigin,\r
+ sRadius);\r
+ // compute the reflection angle\r
+ reflectTheta = isectDir.angleBetween(sphereNormal, true) * 2\r
+ + MathUtils.PI;\r
+ // then form a perpendicular vector standing on the plane spanned by\r
+ // isectDir and sphereNormal\r
+ // this vector will be used to mirror the ray around the\r
+ // intersection point\r
+ Vec3D reflectNormal = isectDir.getNormalized().cross(sphereNormal)\r
+ .normalize();\r
+ if (!reflectNormal.isZeroVector()) {\r
+ // compute the reflected ray direction\r
+ reflectedDir = isectDir.getNormalized().rotateAroundAxis(\r
+ reflectNormal, reflectTheta);\r
+ } else {\r
+ reflectedDir = isectDir.getInverted();\r
+ }\r
+ return new Ray3D(isectPos, reflectedDir);\r
+ } else {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see toxi.geom.Reflector#getReflectionAngle()\r
+ */\r
+ public float getReflectionAngle() {\r
+ return reflectTheta;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see toxi.geom.Reflector#getReflectedRayPointAtDistance(float)\r
+ */\r
+ public Vec3D getReflectedRayPointAtDistance(float dist) {\r
+ if (reflectedDir != null) {\r
+ return isectPos.add(reflectedDir.scale(dist));\r
+ } else\r
+ return null;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see toxi.geom.Intersector#getIntersectionPoint()\r
+ */\r
+ public Vec3D getIntersectionPoint() {\r
+ return isectPos;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see toxi.geom.Intersector#getIntersectionDistance()\r
+ */\r
+ public float getIntersectionDistance() {\r
+ return isectDist;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see toxi.geom.Intersector#getIntersectionDir(boolean)\r
+ */\r
+ public Vec3D getIntersectionDir(boolean normalized) {\r
+ if (isectDir != null) {\r
+ if (normalized) {\r
+ return isectDir.getNormalized();\r
+ }\r
+ return isectDir;\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see toxi.geom.Intersector#getNormalAtIntersection()\r
+ */\r
+ public Vec3D getNormalAtIntersection() {\r
+ return sphereNormal;\r
+ }\r
+}\r
--- /dev/null
+package toxi.geom;\r
+\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * This is a generic 3D B-Spline class for curves of arbitrary length, control\r
+ * handles and patches are created and joined automatically as described here:\r
+ * http://www.ibiblio.org/e-notes/Splines/Bint.htm\r
+ */\r
+public class Spline3D {\r
+\r
+ public Vec3D[] points;\r
+ public Vec3D[] delta;\r
+\r
+ protected Vec3D[] coeffA;\r
+ protected float[] bi;\r
+\r
+ private int numP;\r
+ public ArrayList<Vec3D> vertices;\r
+\r
+ public BernsteinPolynomial bernstein;\r
+\r
+ public Spline3D(Vec3D[] p) {\r
+ points = p;\r
+ numP = points.length;\r
+ coeffA = new Vec3D[numP];\r
+ delta = new Vec3D[numP];\r
+ bi = new float[numP];\r
+ for (int i = 0; i < numP; i++) {\r
+ coeffA[i] = new Vec3D();\r
+ delta[i] = new Vec3D();\r
+ bi[i] = 0;\r
+ }\r
+ }\r
+\r
+ protected void findCPoints() {\r
+ bi[1] = -.25f;\r
+ coeffA[1].set((points[2].x - points[0].x - delta[0].x) * 0.25f,\r
+ (points[2].y - points[0].y - delta[0].y) * 0.25f, (points[2].z\r
+ - points[0].z - delta[0].z) * 0.25f);\r
+ for (int i = 2; i < numP - 1; i++) {\r
+ bi[i] = -1 / (4 + bi[i - 1]);\r
+ coeffA[i].set(\r
+ -(points[i + 1].x - points[i - 1].x - coeffA[i - 1].x)\r
+ * bi[i],\r
+ -(points[i + 1].y - points[i - 1].y - coeffA[i - 1].y)\r
+ * bi[i],\r
+ -(points[i + 1].z - points[i - 1].z - coeffA[i - 1].z)\r
+ * bi[i]);\r
+ }\r
+ for (int i = numP - 2; i > 0; i--) {\r
+ delta[i].set(coeffA[i].x + delta[i + 1].x * bi[i], coeffA[i].y\r
+ + delta[i + 1].y * bi[i], coeffA[i].z + delta[i + 1].z\r
+ * bi[i]);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Computes all curve vertices based on the resolution/number of\r
+ * subdivisions requested. The higher, the more vertices are computed:\r
+ * (number of control points - 1) * resolution\r
+ * \r
+ * @param res\r
+ * resolution\r
+ * @return list of Vec3D vertices along the curve\r
+ */\r
+ public ArrayList<Vec3D> computeVertices(int res) {\r
+ if (bernstein == null || bernstein.resolution != res) {\r
+ bernstein = new BernsteinPolynomial(res);\r
+ }\r
+ if (vertices == null)\r
+ vertices = new ArrayList<Vec3D>();\r
+ else\r
+ vertices.clear();\r
+ findCPoints();\r
+ Vec3D deltaP = new Vec3D();\r
+ Vec3D deltaQ = new Vec3D();\r
+ for (int i = 0; i < numP - 1; i++) {\r
+ Vec3D p = points[i];\r
+ Vec3D q = points[i + 1];\r
+ deltaP.set(delta[i]).addSelf(p);\r
+ deltaQ.set(q).subSelf(delta[i + 1]);\r
+ for (int k = 0; k < bernstein.resolution; k++) {\r
+ float x = p.x * bernstein.b0[k] + deltaP.x * bernstein.b1[k]\r
+ + deltaQ.x * bernstein.b2[k] + q.x * bernstein.b3[k];\r
+ float y = p.y * bernstein.b0[k] + deltaP.y * bernstein.b1[k]\r
+ + deltaQ.y * bernstein.b2[k] + q.y * bernstein.b3[k];\r
+ float z = p.z * bernstein.b0[k] + deltaP.z * bernstein.b1[k]\r
+ + deltaQ.z * bernstein.b2[k] + q.z * bernstein.b3[k];\r
+ vertices.add(new Vec3D(x, y, z));\r
+ }\r
+ }\r
+ return vertices;\r
+ }\r
+\r
+ // FIXME this isn't generic enough & worked just for Nokia Friends project\r
+ public void createSymmetricEnds() {\r
+ delta[0] = points[1].scale(0.75f);\r
+ int lastIdx = numP - 2;\r
+ delta[lastIdx] = points[lastIdx].scale(-0.75f);\r
+ }\r
+\r
+ class BernsteinPolynomial {\r
+ float[] b0, b1, b2, b3;\r
+ int resolution;\r
+\r
+ public BernsteinPolynomial(int res) {\r
+ resolution = res;\r
+ b0 = new float[res];\r
+ b1 = new float[res];\r
+ b2 = new float[res];\r
+ b3 = new float[res];\r
+ float t = 0;\r
+ float dt = 1.0f / (resolution - 1);\r
+ for (int i = 0; i < resolution; i++) {\r
+ float t1 = 1 - t;\r
+ float t12 = t1 * t1, t2 = t * t;\r
+ b0[i] = t1 * t12;\r
+ b1[i] = 3 * t * t12;\r
+ b2[i] = 3 * t2 * t1;\r
+ b3[i] = t * t2;\r
+ t += dt;\r
+ }\r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/* \r
+ * Copyright (c) 2006-2008 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.geom;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+public class Triangle {\r
+\r
+ Vec3D a, b, c;\r
+ Vec3D normal;\r
+\r
+ public Triangle(Vec3D a, Vec3D b, Vec3D c) {\r
+ this.a = a;\r
+ this.b = b;\r
+ this.c = c;\r
+ }\r
+\r
+ public void computeNormal() {\r
+ normal = a.sub(b).cross(a.sub(c)).normalize();\r
+ }\r
+\r
+ /**\r
+ * Checks if point vector is inside the triangle created by the points a, b\r
+ * and c. These points will create a plane and the point checked will have\r
+ * to be on this plane in the region between a,b,c.\r
+ * \r
+ * Note: The triangle must be defined in clockwise order a,b,c\r
+ * \r
+ * @return true, if point is in triangle.\r
+ */\r
+ public boolean containsPoint(Vec3D p) {\r
+ Vec3D v1 = p.sub(a).normalize();\r
+ Vec3D v2 = p.sub(b).normalize();\r
+ Vec3D v3 = p.sub(c).normalize();\r
+\r
+ double total_angles = Math.acos(v1.dot(v2));\r
+ total_angles += Math.acos(v2.dot(v3));\r
+ total_angles += Math.acos(v3.dot(v1));\r
+\r
+ return (MathUtils.abs((float) total_angles - MathUtils.TWO_PI) <= 0.005f);\r
+ }\r
+\r
+ /**\r
+ * Finds and returns the closest point on any of the triangle edges to the\r
+ * point given.\r
+ * \r
+ * @param p\r
+ * point to check\r
+ * @return closest point\r
+ */\r
+\r
+ public Vec3D getClosestVertexTo(Vec3D p) {\r
+ Vec3D Rab = p.closestPointOnLine(a, b);\r
+ Vec3D Rbc = p.closestPointOnLine(b, c);\r
+ Vec3D Rca = p.closestPointOnLine(c, a);\r
+\r
+ float dAB = p.sub(Rab).magSquared();\r
+ float dBC = p.sub(Rbc).magSquared();\r
+ float dCA = p.sub(Rca).magSquared();\r
+\r
+ float min = dAB;\r
+ Vec3D result = Rab;\r
+\r
+ if (dBC < min) {\r
+ min = dBC;\r
+ result = Rbc;\r
+ }\r
+ if (dCA < min)\r
+ result = Rca;\r
+\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * @see #closestPointOnSurface(Vec3D)\r
+ * @deprecated\r
+ */\r
+ public Vec3D closedPoint(Vec3D p) {\r
+ return closestPointOnSurface(p);\r
+ }\r
+\r
+ /**\r
+ * Computes the the point closest to the current vector on the surface of\r
+ * triangle abc.\r
+ * \r
+ * From Real-Time Collision Detection by Christer Ericson, published by\r
+ * Morgan Kaufmann Publishers, Copyright 2005 Elsevier Inc\r
+ * \r
+ * @return closest point on triangle (result may also be one of a, b or c)\r
+ */\r
+ public Vec3D closestPointOnSurface(Vec3D p) {\r
+ Vec3D ab = b.sub(a);\r
+ Vec3D ac = c.sub(a);\r
+ Vec3D bc = c.sub(b);\r
+\r
+ Vec3D pa = p.sub(a);\r
+ Vec3D pb = p.sub(b);\r
+ Vec3D pc = p.sub(c);\r
+\r
+ Vec3D ap = a.sub(p);\r
+ Vec3D bp = b.sub(p);\r
+ Vec3D cp = c.sub(p);\r
+\r
+ // Compute parametric position s for projection P' of P on AB,\r
+ // P' = A + s*AB, s = snom/(snom+sdenom)\r
+ float snom = pa.dot(ab);\r
+ float sdenom = pb.dot(a.sub(b));\r
+\r
+ // Compute parametric position t for projection P' of P on AC,\r
+ // P' = A + t*AC, s = tnom/(tnom+tdenom)\r
+ float tnom = pa.dot(ac);\r
+ float tdenom = pc.dot(a.sub(c));\r
+\r
+ if (snom <= 0.0f && tnom <= 0.0f)\r
+ return a; // Vertex region early out\r
+\r
+ // Compute parametric position u for projection P' of P on BC,\r
+ // P' = B + u*BC, u = unom/(unom+udenom)\r
+ float unom = pb.dot(bc);\r
+ float udenom = pc.dot(b.sub(c));\r
+\r
+ if (sdenom <= 0.0f && unom <= 0.0f)\r
+ return b; // Vertex region early out\r
+ if (tdenom <= 0.0f && udenom <= 0.0f)\r
+ return c; // Vertex region early out\r
+\r
+ // P is outside (or on) AB if the triple scalar product [N PA PB] <= 0\r
+ Vec3D n = ab.cross(ac);\r
+ float vc = n.dot(ap.crossSelf(bp));\r
+\r
+ // If P outside AB and within feature region of AB,\r
+ // return projection of P onto AB\r
+ if (vc <= 0.0f && snom >= 0.0f && sdenom >= 0.0f) {\r
+ // return a + snom / (snom + sdenom) * ab;\r
+ return a.add(ab.scaleSelf(snom / (snom + sdenom)));\r
+ }\r
+\r
+ // P is outside (or on) BC if the triple scalar product [N PB PC] <= 0\r
+ float va = n.dot(bp.crossSelf(cp));\r
+ // If P outside BC and within feature region of BC,\r
+ // return projection of P onto BC\r
+ if (va <= 0.0f && unom >= 0.0f && udenom >= 0.0f) {\r
+ // return b + unom / (unom + udenom) * bc;\r
+ return b.add(bc.scaleSelf(unom / (unom + udenom)));\r
+ }\r
+\r
+ // P is outside (or on) CA if the triple scalar product [N PC PA] <= 0\r
+ float vb = n.dot(cp.crossSelf(ap));\r
+ // If P outside CA and within feature region of CA,\r
+ // return projection of P onto CA\r
+ if (vb <= 0.0f && tnom >= 0.0f && tdenom >= 0.0f) {\r
+ // return a + tnom / (tnom + tdenom) * ac;\r
+ return a.add(ac.scaleSelf(tnom / (tnom + tdenom)));\r
+ }\r
+\r
+ // P must project inside face region. Compute Q using barycentric\r
+ // coordinates\r
+ float u = va / (va + vb + vc);\r
+ float v = vb / (va + vb + vc);\r
+ float w = 1.0f - u - v; // = vc / (va + vb + vc)\r
+ // return u * a + v * b + w * c;\r
+ return a.scale(u).addSelf(b.scale(v)).addSelf(c.scale(w));\r
+ }\r
+\r
+ public boolean isClockwiseInXY() {\r
+ return Triangle.isClockwiseInXY(a, b, c);\r
+ }\r
+\r
+ public boolean isClockwiseInXZ() {\r
+ return Triangle.isClockwiseInXY(a, b, c);\r
+ }\r
+\r
+ public boolean isClockwiseInYZ() {\r
+ return Triangle.isClockwiseInXY(a, b, c);\r
+ }\r
+\r
+ public static boolean isClockwiseInXY(Vec3D a, Vec3D b, Vec3D c) {\r
+ float determ = (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);\r
+ return (determ < 0.0);\r
+ }\r
+\r
+ public static boolean isClockwiseInXZ(Vec3D a, Vec3D b, Vec3D c) {\r
+ float determ = (b.x - a.x) * (c.z - a.z) - (c.x - a.x) * (b.z - a.z);\r
+ return (determ < 0.0);\r
+ }\r
+\r
+ public static boolean isClockwiseInYZ(Vec3D a, Vec3D b, Vec3D c) {\r
+ float determ = (b.y - a.y) * (c.z - a.z) - (c.y - a.y) * (b.z - a.z);\r
+ return (determ < 0.0);\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006-2008 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.geom;\r
+\r
+import java.awt.Rectangle;\r
+import java.util.Random;\r
+\r
+import toxi.math.InterpolateStrategy;\r
+import toxi.math.MathUtils;\r
+\r
+/**\r
+ * Comprehensive 2D vector class with additional basic intersection and\r
+ * collision detection features.\r
+ * \r
+ * @author Karsten Schmidt\r
+ * \r
+ */\r
+public class Vec2D {\r
+\r
+ /**\r
+ * Defines positive X axis\r
+ */\r
+ public static final Vec2D X_AXIS = new Vec2D(1, 0);\r
+\r
+ /**\r
+ * Defines positive Y axis\r
+ */\r
+ public static final Vec2D Y_AXIS = new Vec2D(0, 1);\r
+\r
+ /**\r
+ * Creates a new vector from the given angle in the XY plane.\r
+ * \r
+ * The resulting vector for theta=0 is equal to the positive X axis.\r
+ * \r
+ * @param theta\r
+ * @return new vector pointing into the direction of the passed in angle\r
+ */\r
+ public static final Vec2D fromTheta(float theta) {\r
+ return new Vec2D((float) Math.cos(theta), (float) Math.sin(theta));\r
+ }\r
+\r
+ /**\r
+ * Static factory method. Creates a new random unit vector using the default\r
+ * Math.random() Random instance.\r
+ * \r
+ * @return a new random normalized unit vector.\r
+ */\r
+ public static final Vec2D randomVector() {\r
+ Vec2D rnd = new Vec2D((float) Math.random() * 2 - 1, (float) Math\r
+ .random() * 2 - 1);\r
+ return rnd.normalize();\r
+ }\r
+\r
+ /**\r
+ * Static factory method. Creates a new random unit vector using the given\r
+ * Random generator instance. I recommend to have a look at the\r
+ * https://uncommons-maths.dev.java.net library for a good choice of\r
+ * reliable and high quality random number generators.\r
+ * \r
+ * @return a new random normalized unit vector.\r
+ */\r
+ public static final Vec2D randomVector(Random rnd) {\r
+ Vec2D v = new Vec2D(rnd.nextFloat() * 2 - 1, rnd.nextFloat() * 2 - 1);\r
+ return v.normalize();\r
+ }\r
+\r
+ /**\r
+ * X coordinate\r
+ */\r
+ public float x;\r
+\r
+ /**\r
+ * Y coordinate\r
+ */\r
+ public float y;\r
+\r
+ /**\r
+ * Creates a new zero vector\r
+ */\r
+ public Vec2D() {\r
+ x = y = 0;\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector with the given coordinates\r
+ * \r
+ * @param x\r
+ * @param y\r
+ */\r
+ public Vec2D(float x, float y) {\r
+ this.x = x;\r
+ this.y = y;\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector with the coordinates of the given vector\r
+ * \r
+ * @param v\r
+ * vector to be copied\r
+ */\r
+ public Vec2D(Vec2D v) {\r
+ set(v);\r
+ }\r
+\r
+ public final Vec2D abs() {\r
+ x = MathUtils.abs(x);\r
+ y = MathUtils.abs(y);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Adds vector {a,b,c} and returns result as new vector.\r
+ * \r
+ * @param a\r
+ * X coordinate\r
+ * @param b\r
+ * Y coordinate\r
+ * @return result as new vector\r
+ */\r
+ public final Vec2D add(float a, float b) {\r
+ return new Vec2D(x + a, y + b);\r
+ }\r
+\r
+ /**\r
+ * Add vector v and returns result as new vector.\r
+ * \r
+ * @param v\r
+ * vector to add\r
+ * @return result as new vector\r
+ */\r
+ public final Vec2D add(Vec2D v) {\r
+ return new Vec2D(x + v.x, y + v.y);\r
+ }\r
+\r
+ /**\r
+ * Adds vector {a,b,c} and overrides coordinates with result.\r
+ * \r
+ * @param a\r
+ * X coordinate\r
+ * @param b\r
+ * Y coordinate\r
+ * @return itself\r
+ */\r
+ public final Vec2D addSelf(float a, float b) {\r
+ x += a;\r
+ y += b;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Adds vector v and overrides coordinates with result.\r
+ * \r
+ * @param v\r
+ * vector to add\r
+ * @return itself\r
+ */\r
+ public final Vec2D addSelf(Vec2D v) {\r
+ x += v.x;\r
+ y += v.y;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Computes the angle between this vector and vector V. This function\r
+ * assumes both vectors are normalized, if this can't be guaranteed, use the\r
+ * alternative implementation {@link #angleBetween(Vec2D, boolean)}\r
+ * \r
+ * @param v\r
+ * vector\r
+ * @return angle in radians, or NaN if vectors are parallel\r
+ */\r
+ public final float angleBetween(Vec2D v) {\r
+ return (float) Math.acos(dot(v));\r
+ }\r
+\r
+ /**\r
+ * Computes the angle between this vector and vector V\r
+ * \r
+ * @param v\r
+ * vector\r
+ * @param forceNormalize\r
+ * true, if normalized versions of the vectors are to be used\r
+ * (Note: only copies will be used, original vectors will not be\r
+ * altered by this method)\r
+ * @return angle in radians, or NaN if vectors are parallel\r
+ */\r
+ public final float angleBetween(Vec2D v, boolean forceNormalize) {\r
+ float theta;\r
+ if (forceNormalize) {\r
+ theta = getNormalized().dot(v.getNormalized());\r
+ } else {\r
+ theta = dot(v);\r
+ }\r
+ return (float) Math.acos(theta);\r
+ }\r
+\r
+ /**\r
+ * Compares the length of the vector with another one.\r
+ * \r
+ * @param vec\r
+ * vector to compare with\r
+ * @return -1 if other vector is longer, 0 if both are equal or else +1\r
+ */\r
+ public int compareTo(Object vec) {\r
+ Vec3D v = (Vec3D) vec;\r
+ if (Float.compare(x, v.x) == 0 && Float.compare(y, v.y) == 0)\r
+ return 0;\r
+ if (magSquared() < v.magSquared())\r
+ return -1;\r
+ return 1;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ Vec3D v = (Vec3D) obj;\r
+ return (Float.compare(x, v.x) == 0 && Float.compare(y, v.y) == 0);\r
+ }\r
+\r
+ /**\r
+ * Sets all vector components to 0.\r
+ * \r
+ * @return itself\r
+ */\r
+ public final Vec2D clear() {\r
+ x = y = 0;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * \r
+ * Helper function for {@link #closestPointOnTriangle(Vec2D, Vec2D, Vec2D)}\r
+ * \r
+ * @param a\r
+ * start point of line segment\r
+ * @param b\r
+ * end point of line segment\r
+ * @return closest point on the line segment a -> b\r
+ */\r
+\r
+ public Vec2D closestPointOnLine(Vec2D a, Vec2D b) {\r
+ // Determine t (the length of the vector from �a� to 'this')\r
+ Vec2D c = sub(a);\r
+ Vec2D v = b.sub(a);\r
+\r
+ float d = v.magnitude();\r
+ v.normalize();\r
+\r
+ float t = v.dot(c);\r
+\r
+ // Check to see if point is beyond the extents of the line segment\r
+ if (t < 0.0f)\r
+ return a;\r
+ if (t > d)\r
+ return b;\r
+\r
+ // Return the point between 'a' and 'b'\r
+ // set length of V to t. V is normalized so this is easy\r
+ v.scaleSelf(t);\r
+\r
+ return a.add(v);\r
+ }\r
+\r
+ /**\r
+ * Finds and returns the closest point on any of the edges of the given\r
+ * triangle.\r
+ * \r
+ * @param a\r
+ * triangle vertex\r
+ * @param b\r
+ * triangle vertex\r
+ * @param c\r
+ * triangle vertex\r
+ * @return closest point\r
+ */\r
+\r
+ public Vec2D closestPointOnTriangle(Vec2D a, Vec2D b, Vec2D c) {\r
+ Vec2D Rab = closestPointOnLine(a, b);\r
+ Vec2D Rbc = closestPointOnLine(b, c);\r
+ Vec2D Rca = closestPointOnLine(c, a);\r
+\r
+ float dAB = sub(Rab).magnitude();\r
+ float dBC = sub(Rbc).magnitude();\r
+ float dCA = sub(Rca).magnitude();\r
+\r
+ float min = dAB;\r
+ Vec2D result = Rab;\r
+\r
+ if (dBC < min) {\r
+ min = dBC;\r
+ result = Rbc;\r
+ }\r
+ if (dCA < min)\r
+ result = Rca;\r
+\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Forcefully fits the vector in the given rectangle.\r
+ * \r
+ * @param r\r
+ * @return itself\r
+ */\r
+\r
+ public final Vec2D constrain(Rectangle r) {\r
+ x = MathUtils.clip(x, r.x, r.x + r.width);\r
+ y = MathUtils.clip(y, r.y, r.y + r.height);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * @return a new independent instance/copy of a given vector\r
+ */\r
+ public final Vec2D copy() {\r
+ return new Vec2D(this);\r
+ }\r
+\r
+ /**\r
+ * Calculates distance to another vector\r
+ * \r
+ * @param v\r
+ * non-null vector\r
+ * @return distance or Float.NaN if v=null\r
+ */\r
+ public final float distanceTo(Vec2D v) {\r
+ if (v != null) {\r
+ float dx = x - v.x;\r
+ float dy = y - v.y;\r
+ return (float) Math.sqrt(dx * dx + dy * dy);\r
+ } else {\r
+ return Float.NaN;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Calculates the squared distance to another vector\r
+ * \r
+ * @see #magSquared()\r
+ * @param v\r
+ * non-null vector\r
+ * @return distance or NaN if v=null\r
+ */\r
+ public final float distanceToSquared(Vec2D v) {\r
+ if (v != null) {\r
+ float dx = x - v.x;\r
+ float dy = y - v.y;\r
+ return dx * dx + dy * dy;\r
+ } else {\r
+ return Float.NaN;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Computes the scalar product (dot product) with the given vector.\r
+ * \r
+ * @see <a href="http://en.wikipedia.org/wiki/Dot_product">Wikipedia entry<\r
+ * /a>\r
+ * \r
+ * @param v\r
+ * @return dot product\r
+ */\r
+ public final float dot(Vec2D v) {\r
+ return x * v.x + y * v.y;\r
+ }\r
+\r
+ /**\r
+ * Replaces the vector components with integer values of their current\r
+ * values\r
+ * \r
+ * @return itself\r
+ */\r
+ public final Vec2D floor() {\r
+ x = MathUtils.floor(x);\r
+ y = MathUtils.floor(y);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Replaces the vector components with the fractional part of their current\r
+ * values\r
+ * \r
+ * @return itself\r
+ */\r
+ public final Vec2D frac() {\r
+ x -= MathUtils.floor(x);\r
+ y -= MathUtils.floor(y);\r
+ return this;\r
+ }\r
+\r
+ public final Vec2D getAbs() {\r
+ return new Vec2D(this).abs();\r
+ }\r
+\r
+ /**\r
+ * Creates a copy of the vector which forcefully fits in the given\r
+ * rectangle.\r
+ * \r
+ * @param r\r
+ * @return fitted vector\r
+ */\r
+ public final Vec2D getConstrained(Rectangle r) {\r
+ return new Vec2D(this).constrain(r);\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector whose components are the integer value of their\r
+ * current values\r
+ * \r
+ * @return result as new vector\r
+ */\r
+ public final Vec2D getFloored() {\r
+ return new Vec2D(this).floor();\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector whose components are the fractional part of their\r
+ * current values\r
+ * \r
+ * @return result as new vector\r
+ */\r
+ public final Vec2D getFrac() {\r
+ return new Vec2D(this).frac();\r
+ }\r
+\r
+ /**\r
+ * Scales vector uniformly by factor -1 ( v = -v )\r
+ * \r
+ * @return result as new vector\r
+ */\r
+ public final Vec2D getInverted() {\r
+ return new Vec2D(-x, -y);\r
+ }\r
+\r
+ /**\r
+ * Creates a copy of the vector with its magnitude limited to the length\r
+ * given\r
+ * \r
+ * @param lim\r
+ * new maximum magnitude\r
+ * @return result as new vector\r
+ */\r
+ public final Vec2D getLimited(float lim) {\r
+ if (magSquared() > lim * lim) {\r
+ return getNormalized().scaleSelf(lim);\r
+ }\r
+ return new Vec2D(this);\r
+ }\r
+\r
+ /**\r
+ * Produces the normalized version as a new vector\r
+ * \r
+ * @return new vector\r
+ */\r
+ public final Vec2D getNormalized() {\r
+ return new Vec2D(this).normalize();\r
+ }\r
+\r
+ public final Vec2D getPerpendicular() {\r
+ return new Vec2D(this).perpendicular();\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector rotated by the given angle around the Z axis.\r
+ * \r
+ * @param theta\r
+ * @return rotated vector\r
+ */\r
+ public final Vec2D getRotated(float theta) {\r
+ return new Vec2D(this).rotate(theta);\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector in which all components are replaced with the signum\r
+ * of their original values. In other words if a components value was\r
+ * negative its new value will be -1, if zero => 0, if positive => +1\r
+ * \r
+ * @return result vector\r
+ */\r
+ public Vec2D getSignum() {\r
+ return new Vec2D(this).signum();\r
+ }\r
+\r
+ /**\r
+ * Computes the vector's direction in the XY plane (for example for 2D\r
+ * points). The positive X axis equals 0 degrees.\r
+ * \r
+ * @return rotation angle\r
+ */\r
+ public final float heading() {\r
+ return (float) Math.atan2(y, x);\r
+ }\r
+\r
+ /**\r
+ * Interpolates the vector towards the given target vector, using linear\r
+ * interpolation\r
+ * \r
+ * @param v\r
+ * target vector\r
+ * @param f\r
+ * interpolation factor (should be in the range 0..1)\r
+ * @return result as new vector\r
+ */\r
+ public final Vec2D interpolateTo(Vec2D v, float f) {\r
+ return new Vec2D(x + (v.x - x) * f, y + (v.y - y) * f);\r
+ }\r
+\r
+ /**\r
+ * Interpolates the vector towards the given target vector, using the given\r
+ * {@link InterpolateStrategy}\r
+ * \r
+ * @param v\r
+ * target vector\r
+ * @param f\r
+ * interpolation factor (should be in the range 0..1)\r
+ * @param s\r
+ * InterpolateStrategy instance\r
+ * @return result as new vector\r
+ */\r
+ public Vec2D interpolateTo(Vec2D v, float f, InterpolateStrategy s) {\r
+ return new Vec2D(s.interpolate(x, v.x, f), s.interpolate(y, v.y, f));\r
+ }\r
+\r
+ /**\r
+ * Interpolates the vector towards the given target vector, using linear\r
+ * interpolation\r
+ * \r
+ * @param v\r
+ * target vector\r
+ * @param f\r
+ * interpolation factor (should be in the range 0..1)\r
+ * @return itself, result overrides current vector\r
+ */\r
+ public final Vec2D interpolateToSelf(Vec2D v, float f) {\r
+ x += (v.x - x) * f;\r
+ y += (v.y - y) * f;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Interpolates the vector towards the given target vector, using the given\r
+ * {@link InterpolateStrategy}\r
+ * \r
+ * @param v\r
+ * target vector\r
+ * @param f\r
+ * interpolation factor (should be in the range 0..1)\r
+ * @param s\r
+ * InterpolateStrategy instance\r
+ * @return itself, result overrides current vector\r
+ */\r
+ public Vec2D interpolateToSelf(Vec2D v, float f, InterpolateStrategy s) {\r
+ x = s.interpolate(x, v.x, f);\r
+ y = s.interpolate(y, v.y, f);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Calculates the distance of the vector to the given sphere in the\r
+ * specified direction. A sphere is defined by a 3D point and a radius.\r
+ * Normalized directional vectors expected.\r
+ * \r
+ * @param rayDir\r
+ * intersection direction\r
+ * @param circleOrigin\r
+ * @param circleRadius\r
+ * @return distance to sphere in world units, -1 if no intersection.\r
+ */\r
+\r
+ public float intersectRayCircle(Vec2D rayDir, Vec2D circleOrigin,\r
+ float circleRadius) {\r
+ Vec2D q = circleOrigin.sub(this);\r
+ float distSquared = q.magSquared();\r
+ float v = q.dot(rayDir);\r
+ float d = circleRadius * circleRadius - (distSquared - v * v);\r
+\r
+ // If there was no intersection, return -1\r
+ if (d < 0.0)\r
+ return -1;\r
+\r
+ // Return the distance to the [first] intersecting point\r
+ return v - (float) Math.sqrt(d);\r
+ }\r
+\r
+ /**\r
+ * Scales vector uniformly by factor -1 ( v = -v ), overrides coordinates\r
+ * with result\r
+ * \r
+ * @return itself\r
+ */\r
+ public final Vec2D invert() {\r
+ x *= -1;\r
+ y *= -1;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Checks if the point is inside the given sphere.\r
+ * \r
+ * @param sO\r
+ * circle origin/centre\r
+ * @param sR\r
+ * circle radius\r
+ * @return true, if point is in sphere\r
+ */\r
+\r
+ public boolean isInCircle(Vec2D sO, float sR) {\r
+ float d = sub(sO).magSquared();\r
+ return (d <= sR * sR);\r
+ }\r
+\r
+ /**\r
+ * Checks if the point is inside the given rectangle.\r
+ * \r
+ * @param r\r
+ * bounding rectangle\r
+ * @return true, if point is inside\r
+ */\r
+ public boolean isInRecangle(Rectangle r) {\r
+ if (x < r.x || x > r.x + r.width)\r
+ return false;\r
+ if (y < r.y || y > r.y + r.height)\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Checks if point vector is inside the triangle created by the points a, b\r
+ * and c. These points will create a plane and the point checked will have\r
+ * to be on this plane in the region between a,b,c.\r
+ * \r
+ * Note: The triangle must be defined in clockwise order a,b,c\r
+ * \r
+ * @return true, if point is in triangle.\r
+ */\r
+\r
+ public boolean isInTriangle(Vec2D a, Vec2D b, Vec2D c) {\r
+ Vec2D v1 = sub(a).normalize();\r
+ Vec2D v2 = sub(b).normalize();\r
+ Vec2D v3 = sub(c).normalize();\r
+\r
+ double total_angles = Math.acos(v1.dot(v2));\r
+ total_angles += Math.acos(v2.dot(v3));\r
+ total_angles += Math.acos(v3.dot(v1));\r
+\r
+ return (MathUtils.abs((float) total_angles - MathUtils.TWO_PI) <= 0.005f);\r
+ }\r
+\r
+ /**\r
+ * Checks if vector has a magnitude of 0\r
+ * \r
+ * @return true, if vector = {0,0,0}\r
+ */\r
+ public final boolean isZeroVector() {\r
+ return x == 0 && y == 0;\r
+ // return magnitude()<FastMath.EPS;\r
+ }\r
+\r
+ public final Vec2D jitter(float j) {\r
+ return jitter(j, j);\r
+ }\r
+\r
+ /**\r
+ * Adds random jitter to the vector.\r
+ * \r
+ * @param jx\r
+ * maximum x jitter\r
+ * @param jy\r
+ * maximum y jitter\r
+ * @return itself\r
+ */\r
+ public final Vec2D jitter(float jx, float jy) {\r
+ x += MathUtils.normalizedRandom() * jx;\r
+ y += MathUtils.normalizedRandom() * jy;\r
+ return this;\r
+ }\r
+\r
+ public final Vec2D jitter(Vec2D jv) {\r
+ return jitter(jv.x, jv.y);\r
+ }\r
+\r
+ /**\r
+ * Limits the vector's magnitude to the length given\r
+ * \r
+ * @param lim\r
+ * new maximum magnitude\r
+ * @return itself\r
+ */\r
+ public final Vec2D limit(float lim) {\r
+ if (magSquared() > lim * lim) {\r
+ return normalize().scaleSelf(lim);\r
+ }\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Calculates the magnitude/eucledian length of the vector\r
+ * \r
+ * @return vector length\r
+ */\r
+ public final float magnitude() {\r
+ return (float) Math.sqrt(x * x + y * y);\r
+ }\r
+\r
+ /**\r
+ * Calculates only the squared magnitude/length of the vector. Useful for\r
+ * inverse square law applications and/or for speed reasons or if the real\r
+ * eucledian distance is not required (e.g. sorting).\r
+ * \r
+ * @return squared magnitude (x^2 + y^2)\r
+ */\r
+ public final float magSquared() {\r
+ return x * x + y * y;\r
+ }\r
+\r
+ /**\r
+ * Constructs a new vector consisting of the largest components of both\r
+ * vectors.\r
+ * \r
+ * @param v\r
+ * @return result as new vector\r
+ */\r
+ public final Vec2D max(Vec2D v) {\r
+ return new Vec2D(MathUtils.max(x, v.x), MathUtils.max(y, v.y));\r
+ }\r
+\r
+ /**\r
+ * Adjusts the vector components to the maximum values of both vectors\r
+ * \r
+ * @param v\r
+ * @return itself\r
+ */\r
+ public final Vec2D maxSelf(Vec2D v) {\r
+ x = MathUtils.max(x, v.x);\r
+ y = MathUtils.max(y, v.y);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Constructs a new vector consisting of the smallest components of both\r
+ * vectors.\r
+ * \r
+ * @param v\r
+ * comparing vector\r
+ * @return result as new vector\r
+ */\r
+ public final Vec2D min(Vec2D v) {\r
+ return new Vec2D(MathUtils.min(x, v.x), MathUtils.min(y, v.y));\r
+ }\r
+\r
+ /**\r
+ * Adjusts the vector components to the minimum values of both vectors\r
+ * \r
+ * @param v\r
+ * @return itself\r
+ */\r
+ public final Vec2D minSelf(Vec2D v) {\r
+ x = MathUtils.min(x, v.x);\r
+ y = MathUtils.min(y, v.y);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Normalizes the vector so that its magnitude = 1\r
+ * \r
+ * @return itself\r
+ */\r
+ public final Vec2D normalize() {\r
+ float mag = (float) Math.sqrt(x * x + y * y);\r
+ if (mag > 0) {\r
+ x /= mag;\r
+ y /= mag;\r
+ }\r
+ return this;\r
+ }\r
+\r
+ public final Vec2D perpendicular() {\r
+ float t = x;\r
+ x = -y;\r
+ y = t;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Rotates the vector by the given angle around the Z axis.\r
+ * \r
+ * @param theta\r
+ * @return itself\r
+ */\r
+ public final Vec2D rotate(float theta) {\r
+ float co = (float) Math.cos(theta);\r
+ float si = (float) Math.sin(theta);\r
+ float xx = co * x - si * y;\r
+ y = si * x + co * y;\r
+ x = xx;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Scales vector uniformly and returns result as new vector.\r
+ * \r
+ * @param s\r
+ * scale factor\r
+ * @return new vector\r
+ */\r
+ public final Vec2D scale(float s) {\r
+ return new Vec2D(x * s, y * s);\r
+ }\r
+\r
+ /**\r
+ * Scales vector non-uniformly and returns result as new vector.\r
+ * \r
+ * @param a\r
+ * scale factor for X coordinate\r
+ * @param b\r
+ * scale factor for Y coordinate\r
+ * @return new vector\r
+ */\r
+ public final Vec2D scale(float a, float b) {\r
+ return new Vec2D(x * a, y * b);\r
+ }\r
+\r
+ /**\r
+ * Scales vector non-uniformly by vector v and returns result as new vector\r
+ * \r
+ * @param s\r
+ * scale vector\r
+ * @return new vector\r
+ */\r
+ public final Vec2D scale(Vec2D s) {\r
+ return new Vec2D(x * s.x, y * s.y);\r
+ }\r
+\r
+ /**\r
+ * Scales vector uniformly and overrides coordinates with result\r
+ * \r
+ * @param s\r
+ * scale factor\r
+ * @return itself\r
+ */\r
+ public final Vec2D scaleSelf(float s) {\r
+ x *= s;\r
+ y *= s;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Scales vector non-uniformly by vector {a,b,c} and overrides coordinates\r
+ * with result\r
+ * \r
+ * @param a\r
+ * scale factor for X coordinate\r
+ * @param b\r
+ * scale factor for Y coordinate\r
+ * @return itself\r
+ */\r
+ public final Vec2D scaleSelf(float a, float b) {\r
+ x *= a;\r
+ y *= b;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Scales vector non-uniformly by vector v and overrides coordinates with\r
+ * result\r
+ * \r
+ * @param s\r
+ * scale vector\r
+ * @return itself\r
+ */\r
+\r
+ public final Vec2D scaleSelf(Vec2D s) {\r
+ x *= s.x;\r
+ y *= s.y;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Overrides coordinates with the given values\r
+ * \r
+ * @param x\r
+ * @param y\r
+ * @return itself\r
+ */\r
+ public final Vec2D set(float x, float y) {\r
+ this.x = x;\r
+ this.y = y;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Overrides coordinates with the ones of the given vector\r
+ * \r
+ * @param v\r
+ * vector to be copied\r
+ * @return itself\r
+ */\r
+ public final Vec2D set(Vec2D v) {\r
+ x = v.x;\r
+ y = v.y;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Replaces all vector components with the signum of their original values.\r
+ * In other words if a components value was negative its new value will be\r
+ * -1, if zero => 0, if positive => +1\r
+ * \r
+ * @return itself\r
+ */\r
+ public Vec2D signum() {\r
+ x = (x < 0 ? -1 : x == 0 ? 0 : 1);\r
+ y = (y < 0 ? -1 : y == 0 ? 0 : 1);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Subtracts vector {a,b,c} and returns result as new vector.\r
+ * \r
+ * @param a\r
+ * X coordinate\r
+ * @param b\r
+ * Y coordinate\r
+ * @return result as new vector\r
+ */\r
+ public final Vec2D sub(float a, float b) {\r
+ return new Vec2D(x - a, y - b);\r
+ }\r
+\r
+ /**\r
+ * Subtracts vector v and returns result as new vector.\r
+ * \r
+ * @param v\r
+ * vector to be subtracted\r
+ * @return result as new vector\r
+ */\r
+ public final Vec2D sub(Vec2D v) {\r
+ return new Vec2D(x - v.x, y - v.y);\r
+ }\r
+\r
+ /**\r
+ * Subtracts vector {a,b,c} and overrides coordinates with result.\r
+ * \r
+ * @param a\r
+ * X coordinate\r
+ * @param b\r
+ * Y coordinate\r
+ * @return itself\r
+ */\r
+ public final Vec2D subSelf(float a, float b) {\r
+ x -= a;\r
+ y -= b;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Subtracts vector v and overrides coordinates with result.\r
+ * \r
+ * @param v\r
+ * vector to be subtracted\r
+ * @return itself\r
+ */\r
+ public final Vec2D subSelf(Vec2D v) {\r
+ x -= v.x;\r
+ y -= v.y;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Calculates the normal vector on the given ellipse in the direction of the\r
+ * current point.\r
+ * \r
+ * @param eO\r
+ * ellipse origin/centre\r
+ * @param eR\r
+ * ellipse radii\r
+ * @return a unit normal vector to the tangent plane of the ellipsoid in the\r
+ * point.\r
+ */\r
+\r
+ public Vec2D tangentNormalOfEllipse(Vec2D eO, Vec2D eR) {\r
+ Vec2D p = this.sub(eO);\r
+\r
+ float xr2 = eR.x * eR.x;\r
+ float yr2 = eR.y * eR.y;\r
+\r
+ return new Vec2D(p.x / xr2, p.y / yr2).normalize();\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see java.lang.Object#toString()\r
+ */\r
+ public String toString() {\r
+ StringBuffer sb = new StringBuffer(32);\r
+ sb.append("{x:").append(x).append(", y:").append(y).append("}");\r
+ return sb.toString();\r
+ }\r
+\r
+ public float[] toArray() {\r
+ return new float[] { x, y };\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.geom;\r
+\r
+import java.util.Random;\r
+\r
+import toxi.math.InterpolateStrategy;\r
+import toxi.math.MathUtils;\r
+\r
+/**\r
+ * Comprehensive 3D vector class with additional basic intersection and\r
+ * collision detection features.\r
+ * \r
+ * @author Karsten Schmidt\r
+ * \r
+ */\r
+@SuppressWarnings("unchecked")\r
+public class Vec3D implements Comparable {\r
+\r
+ /**\r
+ * Defines positive X axis\r
+ */\r
+ public static final Vec3D X_AXIS = new Vec3D(1, 0, 0);\r
+\r
+ /**\r
+ * Defines positive Y axis\r
+ */\r
+ public static final Vec3D Y_AXIS = new Vec3D(0, 1, 0);\r
+\r
+ /**\r
+ * Defines positive Z axis\r
+ */\r
+ public static final Vec3D Z_AXIS = new Vec3D(0, 0, 1);\r
+\r
+ /**\r
+ * Creates a new vector from the given angle in the XY plane. The Z\r
+ * component of the vector will be zero.\r
+ * \r
+ * The resulting vector for theta=0 is equal to the positive X axis.\r
+ * \r
+ * @param theta\r
+ * @return new vector in the XY plane\r
+ */\r
+ public static final Vec3D fromXYTheta(float theta) {\r
+ return new Vec3D((float) Math.cos(theta), (float) Math.sin(theta), 0);\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector from the given angle in the XZ plane. The Y\r
+ * component of the vector will be zero.\r
+ * \r
+ * The resulting vector for theta=0 is equal to the positive X axis.\r
+ * \r
+ * @param theta\r
+ * @return new vector in the XZ plane\r
+ */\r
+ public static final Vec3D fromXZTheta(float theta) {\r
+ return new Vec3D((float) Math.cos(theta), 0, (float) Math.sin(theta));\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector from the given angle in the YZ plane. The X\r
+ * component of the vector will be zero.\r
+ * \r
+ * The resulting vector for theta=0 is equal to the positive Y axis.\r
+ * \r
+ * @param theta\r
+ * @return new vector in the YZ plane\r
+ */\r
+ public static final Vec3D fromYZTheta(float theta) {\r
+ return new Vec3D(0, (float) Math.cos(theta), (float) Math.sin(theta));\r
+ }\r
+\r
+ /**\r
+ * Constructs a new vector consisting of the largest components of both\r
+ * vectors.\r
+ * \r
+ * @param b\r
+ * @return result as new vector\r
+ */\r
+ public static final Vec3D max(Vec3D a, Vec3D b) {\r
+ return new Vec3D(MathUtils.max(a.x, b.x), MathUtils.max(a.y, b.y),\r
+ MathUtils.max(a.z, b.z));\r
+ }\r
+\r
+ /**\r
+ * Constructs a new vector consisting of the smallest components of both\r
+ * vectors.\r
+ * \r
+ * @param b\r
+ * comparing vector\r
+ * @return result as new vector\r
+ */\r
+ public static final Vec3D min(Vec3D a, Vec3D b) {\r
+ return new Vec3D(MathUtils.min(a.x, b.x), MathUtils.min(a.y, b.y),\r
+ MathUtils.min(a.z, b.z));\r
+ }\r
+\r
+ /**\r
+ * Static factory method. Creates a new random unit vector using the default\r
+ * Math.random() Random instance.\r
+ * \r
+ * @return a new random normalized unit vector.\r
+ */\r
+ public static final Vec3D randomVector() {\r
+ Vec3D rnd = new Vec3D(MathUtils.normalizedRandom(), MathUtils\r
+ .normalizedRandom(), MathUtils.normalizedRandom());\r
+ return rnd.normalize();\r
+ }\r
+\r
+ /**\r
+ * Static factory method. Creates a new random unit vector using the given\r
+ * Random generator instance. I recommend to have a look at the\r
+ * https://uncommons-maths.dev.java.net library for a good choice of\r
+ * reliable and high quality random number generators.\r
+ * \r
+ * @return a new random normalized unit vector.\r
+ */\r
+ public static final Vec3D randomVector(Random rnd) {\r
+ Vec3D v = new Vec3D(MathUtils.normalizedRandom(rnd), MathUtils\r
+ .normalizedRandom(rnd), MathUtils.normalizedRandom(rnd));\r
+ return v.normalize();\r
+ }\r
+\r
+ /**\r
+ * X coordinate\r
+ */\r
+ public float x;\r
+\r
+ /**\r
+ * Y coordinate\r
+ */\r
+ public float y;\r
+\r
+ /**\r
+ * Z coordinate\r
+ */\r
+ public float z;\r
+\r
+ /**\r
+ * Creates a new zero vector\r
+ */\r
+ public Vec3D() {\r
+ x = y = z = 0;\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector with the given coordinates\r
+ * \r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ */\r
+ public Vec3D(float x, float y, float z) {\r
+ this.x = x;\r
+ this.y = y;\r
+ this.z = z;\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector with the coordinates of the given vector\r
+ * \r
+ * @param v\r
+ * vector to be copied\r
+ */\r
+ public Vec3D(Vec3D v) {\r
+ set(v);\r
+ }\r
+\r
+ public final Vec3D abs() {\r
+ x = MathUtils.abs(x);\r
+ y = MathUtils.abs(y);\r
+ z = MathUtils.abs(z);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Adds vector {a,b,c} and returns result as new vector.\r
+ * \r
+ * @param a\r
+ * X coordinate\r
+ * @param b\r
+ * Y coordinate\r
+ * @param c\r
+ * Z coordinate\r
+ * @return result as new vector\r
+ */\r
+ public final Vec3D add(float a, float b, float c) {\r
+ return new Vec3D(x + a, y + b, z + c);\r
+ }\r
+\r
+ /**\r
+ * Add vector v and returns result as new vector.\r
+ * \r
+ * @param v\r
+ * vector to add\r
+ * @return result as new vector\r
+ */\r
+ public final Vec3D add(Vec3D v) {\r
+ return new Vec3D(x + v.x, y + v.y, z + v.z);\r
+ }\r
+\r
+ /**\r
+ * Adds vector {a,b,c} and overrides coordinates with result.\r
+ * \r
+ * @param a\r
+ * X coordinate\r
+ * @param b\r
+ * Y coordinate\r
+ * @param c\r
+ * Z coordinate\r
+ * @return itself\r
+ */\r
+ public final Vec3D addSelf(float a, float b, float c) {\r
+ x += a;\r
+ y += b;\r
+ z += c;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Adds vector v and overrides coordinates with result.\r
+ * \r
+ * @param v\r
+ * vector to add\r
+ * @return itself\r
+ */\r
+ public final Vec3D addSelf(Vec3D v) {\r
+ x += v.x;\r
+ y += v.y;\r
+ z += v.z;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Computes the angle between this vector and vector V. This function\r
+ * assumes both vectors are normalized, if this can't be guaranteed, use the\r
+ * alternative implementation {@link #angleBetween(Vec3D, boolean)}\r
+ * \r
+ * @param v\r
+ * vector\r
+ * @return angle in radians, or NaN if vectors are parallel\r
+ */\r
+ public final float angleBetween(Vec3D v) {\r
+ return (float) Math.acos(dot(v));\r
+ }\r
+\r
+ /**\r
+ * Computes the angle between this vector and vector V\r
+ * \r
+ * @param v\r
+ * vector\r
+ * @param forceNormalize\r
+ * true, if normalized versions of the vectors are to be used\r
+ * (Note: only copies will be used, original vectors will not be\r
+ * altered by this method)\r
+ * @return angle in radians, or NaN if vectors are parallel\r
+ */\r
+ public final float angleBetween(Vec3D v, boolean forceNormalize) {\r
+ float theta;\r
+ if (forceNormalize) {\r
+ theta = getNormalized().dot(v.getNormalized());\r
+ } else {\r
+ theta = dot(v);\r
+ }\r
+ return (float) Math.acos(theta);\r
+ }\r
+\r
+ /**\r
+ * Sets all vector components to 0.\r
+ * \r
+ * @return itself\r
+ */\r
+ public final Vec3D clear() {\r
+ x = y = z = 0;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * \r
+ * Helper function for {@link toxi.geom.Triangle#closedPoint(Vec3D)}\r
+ * \r
+ * @param a\r
+ * start point of line segment\r
+ * @param b\r
+ * end point of line segment\r
+ * @return closest point on the line segment a -> b\r
+ */\r
+\r
+ public Vec3D closestPointOnLine(Vec3D a, Vec3D b) {\r
+ Vec3D c = sub(a);\r
+ Vec3D v = b.sub(a);\r
+\r
+ float d = v.magnitude();\r
+ v.normalize();\r
+\r
+ float t = v.dot(c);\r
+\r
+ // Check to see if t is beyond the extents of the line segment\r
+ if (t < 0.0f)\r
+ return a;\r
+ if (t > d)\r
+ return b;\r
+\r
+ // Return the point between 'a' and 'b'\r
+ // set length of V to t. V is normalized so this is easy\r
+ v.scaleSelf(t);\r
+\r
+ return a.add(v);\r
+ }\r
+\r
+ /**\r
+ * Compares the length of the vector with another one.\r
+ * \r
+ * @param vec\r
+ * vector to compare with\r
+ * @return -1 if other vector is longer, 0 if both are equal or else +1\r
+ */\r
+ public int compareTo(Object vec) {\r
+ Vec3D v = (Vec3D) vec;\r
+ if (Float.compare(x, v.x) == 0 && Float.compare(y, v.y) == 0\r
+ && Float.compare(z, v.z) == 0)\r
+ return 0;\r
+ if (magSquared() < v.magSquared())\r
+ return -1;\r
+ return 1;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ Vec3D v = (Vec3D) obj;\r
+ return (Float.compare(x, v.x) == 0 && Float.compare(y, v.y) == 0 && Float\r
+ .compare(z, v.z) == 0);\r
+ }\r
+\r
+ /**\r
+ * Forcefully fits the vector in the given AABB.\r
+ * \r
+ * @param box\r
+ * @return itself\r
+ */\r
+ public final Vec3D constrain(AABB box) {\r
+ x = MathUtils.clip(x, box.minX(), box.maxX());\r
+ y = MathUtils.clip(y, box.minY(), box.maxY());\r
+ z = MathUtils.clip(z, box.minZ(), box.maxZ());\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * @return a new independent instance/copy of a given vector\r
+ */\r
+ public final Vec3D copy() {\r
+ return new Vec3D(this);\r
+ }\r
+\r
+ /**\r
+ * Calculates cross-product with vector v. The resulting vector is\r
+ * perpendicular to both the current and supplied vector.\r
+ * \r
+ * @param v\r
+ * vector to cross\r
+ * @return cross-product as new vector\r
+ */\r
+ public final Vec3D cross(Vec3D v) {\r
+ return new Vec3D(y * v.z - v.y * z, z * v.x - v.z * x, x * v.y - v.x\r
+ * y);\r
+ }\r
+\r
+ /**\r
+ * Calculates cross-product with vector v. The resulting vector is\r
+ * perpendicular to both the current and supplied vector and stored in the\r
+ * supplied result vector.\r
+ * \r
+ * @param v\r
+ * vector to cross\r
+ * @param result\r
+ * result vector\r
+ * @return result vector\r
+ */\r
+ public final Vec3D crossInto(Vec3D v, Vec3D result) {\r
+ float rx = y * v.z - v.y * z;\r
+ float ry = z * v.x - v.z * x;\r
+ float rz = x * v.y - v.x * y;\r
+ result.set(rx, ry, rz);\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Calculates cross-product with vector v. The resulting vector is\r
+ * perpendicular to both the current and supplied vector and overrides the\r
+ * current.\r
+ * \r
+ * @param v\r
+ * @return itself\r
+ */\r
+ public final Vec3D crossSelf(Vec3D v) {\r
+ float cx = y * v.z - v.y * z;\r
+ float cy = z * v.x - v.z * x;\r
+ z = x * v.y - v.x * y;\r
+ y = cy;\r
+ x = cx;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Calculates distance to another vector\r
+ * \r
+ * @param v\r
+ * non-null vector\r
+ * @return distance or Float.NaN if v=null\r
+ */\r
+ public final float distanceTo(Vec3D v) {\r
+ if (v != null) {\r
+ float dx = x - v.x;\r
+ float dy = y - v.y;\r
+ float dz = z - v.z;\r
+ return (float) Math.sqrt(dx * dx + dy * dy + dz * dz);\r
+ } else {\r
+ return Float.NaN;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Calculates the squared distance to another vector\r
+ * \r
+ * @see #magSquared()\r
+ * @param v\r
+ * non-null vector\r
+ * @return distance or NaN if v=null\r
+ */\r
+ public final float distanceToSquared(Vec3D v) {\r
+ if (v != null) {\r
+ float dx = x - v.x;\r
+ float dy = y - v.y;\r
+ float dz = z - v.z;\r
+ return dx * dx + dy * dy + dz * dz;\r
+ } else {\r
+ return Float.NaN;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Computes the scalar product (dot product) with the given vector.\r
+ * \r
+ * @see <a href="http://en.wikipedia.org/wiki/Dot_product">Wikipedia entry<\r
+ * /a>\r
+ * \r
+ * @param v\r
+ * @return dot product\r
+ */\r
+ public final float dot(Vec3D v) {\r
+ return x * v.x + y * v.y + z * v.z;\r
+ }\r
+\r
+ /**\r
+ * Replaces the vector components with integer values of their current\r
+ * values\r
+ * \r
+ * @return itself\r
+ */\r
+ public final Vec3D floor() {\r
+ x = MathUtils.floor(x);\r
+ y = MathUtils.floor(y);\r
+ z = MathUtils.floor(z);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Replaces the vector components with the fractional part of their current\r
+ * values\r
+ * \r
+ * @return itself\r
+ */\r
+ public final Vec3D frac() {\r
+ x -= MathUtils.floor(x);\r
+ y -= MathUtils.floor(y);\r
+ z -= MathUtils.floor(z);\r
+ return this;\r
+ }\r
+\r
+ public final Vec3D getAbs() {\r
+ return new Vec3D(this).abs();\r
+ }\r
+\r
+ /**\r
+ * Creates a copy of the vector which forcefully fits in the given AABB.\r
+ * \r
+ * @param box\r
+ * @return fitted vector\r
+ */\r
+ public final Vec3D getConstrained(AABB box) {\r
+ return new Vec3D(this).constrain(box);\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector whose components are the integer value of their\r
+ * current values\r
+ * \r
+ * @return result as new vector\r
+ */\r
+ public final Vec3D getFloored() {\r
+ return new Vec3D(this).floor();\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector whose components are the fractional part of their\r
+ * current values\r
+ * \r
+ * @return result as new vector\r
+ */\r
+ public final Vec3D getFrac() {\r
+ return new Vec3D(this).frac();\r
+ }\r
+\r
+ /**\r
+ * Scales vector uniformly by factor -1 ( v = -v )\r
+ * \r
+ * @return result as new vector\r
+ */\r
+ public final Vec3D getInverted() {\r
+ return new Vec3D(-x, -y, -z);\r
+ }\r
+\r
+ /**\r
+ * Creates a copy of the vector with its magnitude limited to the length\r
+ * given\r
+ * \r
+ * @param lim\r
+ * new maximum magnitude\r
+ * @return result as new vector\r
+ */\r
+ public final Vec3D getLimited(float lim) {\r
+ if (magSquared() > lim * lim) {\r
+ return getNormalized().scaleSelf(lim);\r
+ }\r
+ return new Vec3D(this);\r
+ }\r
+\r
+ /**\r
+ * Produces the normalized version as a new vector\r
+ * \r
+ * @return new vector\r
+ */\r
+ public Vec3D getNormalized() {\r
+ return new Vec3D(this).normalize();\r
+ }\r
+\r
+ /**\r
+ * @see #rotateAroundAxis(Vec3D, float)\r
+ * @return new result vector\r
+ */\r
+ public final Vec3D getRotatedAroundAxis(Vec3D axis, float theta) {\r
+ return new Vec3D(this).rotateAroundAxis(axis, theta);\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector rotated by the given angle around the X axis.\r
+ * \r
+ * @param theta\r
+ * @return rotated vector\r
+ */\r
+ public final Vec3D getRotatedX(float theta) {\r
+ return new Vec3D(this).rotateX(theta);\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector rotated by the given angle around the Y axis.\r
+ * \r
+ * @param theta\r
+ * @return rotated vector\r
+ */\r
+ public final Vec3D getRotatedY(float theta) {\r
+ return new Vec3D(this).rotateY(theta);\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector rotated by the given angle around the Z axis.\r
+ * \r
+ * @param theta\r
+ * @return rotated vector\r
+ */\r
+ public final Vec3D getRotatedZ(float theta) {\r
+ return new Vec3D(this).rotateZ(theta);\r
+ }\r
+\r
+ /**\r
+ * Creates a new vector in which all components are replaced with the signum\r
+ * of their original values. In other words if a components value was\r
+ * negative its new value will be -1, if zero => 0, if positive => +1\r
+ * \r
+ * @return result vector\r
+ */\r
+ public Vec3D getSignum() {\r
+ return new Vec3D(this).signum();\r
+ }\r
+\r
+ /**\r
+ * Computes the vector's direction in the XY plane (for example for 2D\r
+ * points). The positive X axis equals 0 degrees.\r
+ * \r
+ * @return rotation angle\r
+ */\r
+ public final float headingXY() {\r
+ return (float) Math.atan2(y, x);\r
+ }\r
+\r
+ /**\r
+ * Computes the vector's direction in the XZ plane. The positive X axis\r
+ * equals 0 degrees.\r
+ * \r
+ * @return rotation angle\r
+ */\r
+ public final float headingXZ() {\r
+ return (float) Math.atan2(z, x);\r
+ }\r
+\r
+ /**\r
+ * Computes the vector's direction in the YZ plane. The positive Z axis\r
+ * equals 0 degrees.\r
+ * \r
+ * @return rotation angle\r
+ */\r
+ public final float headingYZ() {\r
+ return (float) Math.atan2(y, z);\r
+ }\r
+\r
+ /**\r
+ * Interpolates the vector towards the given target vector, using linear\r
+ * interpolation\r
+ * \r
+ * @param v\r
+ * target vector\r
+ * @param f\r
+ * interpolation factor (should be in the range 0..1)\r
+ * @return result as new vector\r
+ */\r
+ public final Vec3D interpolateTo(Vec3D v, float f) {\r
+ return new Vec3D(x + (v.x - x) * f, y + (v.y - y) * f, z + (v.z - z)\r
+ * f);\r
+ }\r
+\r
+ /**\r
+ * Interpolates the vector towards the given target vector, using the given\r
+ * {@link InterpolateStrategy}\r
+ * \r
+ * @param v\r
+ * target vector\r
+ * @param f\r
+ * interpolation factor (should be in the range 0..1)\r
+ * @param s\r
+ * InterpolateStrategy instance\r
+ * @return result as new vector\r
+ */\r
+ public Vec3D interpolateTo(Vec3D v, float f, InterpolateStrategy s) {\r
+ return new Vec3D(s.interpolate(x, v.x, f), s.interpolate(y, v.y, f), s\r
+ .interpolate(z, v.z, f));\r
+ }\r
+\r
+ /**\r
+ * Interpolates the vector towards the given target vector, using linear\r
+ * interpolation\r
+ * \r
+ * @param v\r
+ * target vector\r
+ * @param f\r
+ * interpolation factor (should be in the range 0..1)\r
+ * @return itself, result overrides current vector\r
+ */\r
+ public final Vec3D interpolateToSelf(Vec3D v, float f) {\r
+ x += (v.x - x) * f;\r
+ y += (v.y - y) * f;\r
+ z += (v.z - z) * f;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Interpolates the vector towards the given target vector, using the given\r
+ * {@link InterpolateStrategy}\r
+ * \r
+ * @param v\r
+ * target vector\r
+ * @param f\r
+ * interpolation factor (should be in the range 0..1)\r
+ * @param s\r
+ * InterpolateStrategy instance\r
+ * @return itself, result overrides current vector\r
+ */\r
+ public Vec3D interpolateToSelf(Vec3D v, float f, InterpolateStrategy s) {\r
+ x = s.interpolate(x, v.x, f);\r
+ y = s.interpolate(y, v.y, f);\r
+ z = s.interpolate(z, v.z, f);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Calculates the distance of the vector to the given plane in the specified\r
+ * direction. A plane is specified by a 3D point and a normal vector\r
+ * perpendicular to the plane. Normalized directional vectors expected (for\r
+ * rayDir and planeNormal).\r
+ * \r
+ * @param rayDir\r
+ * intersection direction\r
+ * @param planeOrigin\r
+ * @param planeNormal\r
+ * @return distance to plane in world units, -1 if no intersection.\r
+ * @deprecated\r
+ */\r
+ // FIXME this is kind of obsolete since the arrival of the Plane class, but\r
+ // needs amends to reflector code\r
+ public float intersectRayPlane(Vec3D rayDir, Vec3D planeOrigin,\r
+ Vec3D planeNormal) {\r
+ float d = -planeNormal.dot(planeOrigin);\r
+ float numer = planeNormal.dot(this) + d;\r
+ float denom = planeNormal.dot(rayDir);\r
+\r
+ // normal is orthogonal to vector, cant intersect\r
+ if (MathUtils.abs(denom) < MathUtils.EPS)\r
+ return -1;\r
+\r
+ return -(numer / denom);\r
+ }\r
+\r
+ /**\r
+ * Calculates the distance of the vector to the given sphere in the\r
+ * specified direction. A sphere is defined by a 3D point and a radius.\r
+ * Normalized directional vectors expected.\r
+ * \r
+ * @param rayDir\r
+ * intersection direction\r
+ * @param sphereOrigin\r
+ * @param sphereRadius\r
+ * @return distance to sphere in world units, -1 if no intersection.\r
+ */\r
+\r
+ // FIXME this really should be part of either Sphere or\r
+ // SphereIntersectorReflector\r
+ public float intersectRaySphere(Vec3D rayDir, Vec3D sphereOrigin,\r
+ float sphereRadius) {\r
+ Vec3D q = sphereOrigin.sub(this);\r
+ float distSquared = q.magSquared();\r
+ float v = q.dot(rayDir);\r
+ float d = sphereRadius * sphereRadius - (distSquared - v * v);\r
+\r
+ // If there was no intersection, return -1\r
+ if (d < 0.0) {\r
+ return -1;\r
+ }\r
+\r
+ // Return the distance to the [first] intersecting point\r
+ return v - (float) Math.sqrt(d);\r
+ }\r
+\r
+ /**\r
+ * Considers the current vector as centre of a collision sphere with radius\r
+ * r and checks if the triangle abc intersects with this sphere. The Vec3D p\r
+ * The point on abc closest to the sphere center is returned via the\r
+ * supplied result vector argument.\r
+ * \r
+ * @param r\r
+ * collision sphere radius\r
+ * @param a\r
+ * triangle vertex\r
+ * @param b\r
+ * triangle vertex\r
+ * @param c\r
+ * triangle vertex\r
+ * @param result\r
+ * a non-null vector for storing the result\r
+ * @return true, if sphere intersects triangle ABC\r
+ */\r
+ // FIXME this needs to be moved out from Vec3D in either\r
+ public boolean intersectSphereTriangle(float r, Vec3D a, Vec3D b, Vec3D c,\r
+ Vec3D result) {\r
+ // Find Vec3D P on triangle ABC closest to sphere center\r
+ result.set(new Triangle(a, b, c).closestPointOnSurface(this));\r
+\r
+ // Sphere and triangle intersect if the (squared) distance from sphere\r
+ // center to Vec3D p is less than the (squared) sphere radius\r
+ Vec3D v = result.sub(this);\r
+ return v.magSquared() <= r * r;\r
+ }\r
+\r
+ /**\r
+ * Scales vector uniformly by factor -1 ( v = -v ), overrides coordinates\r
+ * with result\r
+ * \r
+ * @return itself\r
+ */\r
+ public final Vec3D invert() {\r
+ x *= -1;\r
+ y *= -1;\r
+ z *= -1;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Checks if the point is inside the given AABB.\r
+ * \r
+ * @param box\r
+ * bounding box to check\r
+ * @return true, if point is inside\r
+ */\r
+ public boolean isInAABB(AABB box) {\r
+ Vec3D min = box.getMin();\r
+ Vec3D max = box.getMax();\r
+ if (x < min.x || x > max.x)\r
+ return false;\r
+ if (y < min.y || y > max.y)\r
+ return false;\r
+ if (z < min.z || z > max.z)\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Checks if the point is inside the given axis-aligned bounding box.\r
+ * \r
+ * @param bO\r
+ * bounding box origin/center\r
+ * @param bDim\r
+ * bounding box extends (half measure)\r
+ * @return true, if point is inside the box\r
+ */\r
+\r
+ public boolean isInAABB(Vec3D bO, Vec3D bDim) {\r
+ float w = bDim.x;\r
+ if (x < bO.x - w || x > bO.x + w)\r
+ return false;\r
+ w = bDim.y;\r
+ if (y < bO.y - w || y > bO.y + w)\r
+ return false;\r
+ w = bDim.z;\r
+ if (z < bO.z - w || z > bO.z + w)\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Checks if the point is inside the given sphere.\r
+ * \r
+ * @param s\r
+ * bounding sphere to check\r
+ * @return true, if point is inside\r
+ */\r
+ // FIXME move to Sphere\r
+ public boolean isInSphere(Sphere s) {\r
+ float d = this.sub(s).magSquared();\r
+ return (d <= s.radius * s.radius);\r
+ }\r
+\r
+ /**\r
+ * Checks if the point is inside the given sphere.\r
+ * \r
+ * @param sO\r
+ * sphere origin/centre\r
+ * @param sR\r
+ * sphere radius\r
+ * @return true, if point is in sphere\r
+ */\r
+ // FIXME move to Sphere\r
+ public boolean isInSphere(Vec3D sO, float sR) {\r
+ float d = this.sub(sO).magSquared();\r
+ return (d <= sR * sR);\r
+ }\r
+\r
+ /**\r
+ * Checks if vector has a magnitude of 0\r
+ * \r
+ * @return true, if vector = {0,0,0}\r
+ */\r
+ public final boolean isZeroVector() {\r
+ return x == 0 && y == 0 && z == 0;\r
+ // return magnitude()<FastMath.EPS;\r
+ }\r
+\r
+ public final Vec3D jitter(float j) {\r
+ return jitter(j, j, j);\r
+ }\r
+\r
+ /**\r
+ * Adds random jitter to the vector.\r
+ * \r
+ * @param jx\r
+ * maximum x jitter\r
+ * @param jy\r
+ * maximum y jitter\r
+ * @param jz\r
+ * maximum z jitter\r
+ * @return itself\r
+ */\r
+ public final Vec3D jitter(float jx, float jy, float jz) {\r
+ x += MathUtils.normalizedRandom() * jx;\r
+ y += MathUtils.normalizedRandom() * jy;\r
+ z += MathUtils.normalizedRandom() * jz;\r
+ return this;\r
+ }\r
+\r
+ public final Vec3D jitter(Vec3D jitterVec) {\r
+ return jitter(jitterVec.x, jitterVec.y, jitterVec.z);\r
+ }\r
+\r
+ /**\r
+ * Limits the vector's magnitude to the length given\r
+ * \r
+ * @param lim\r
+ * new maximum magnitude\r
+ * @return itself\r
+ */\r
+ public final Vec3D limit(float lim) {\r
+ if (magSquared() > lim * lim) {\r
+ return normalize().scaleSelf(lim);\r
+ }\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Calculates the magnitude/eucledian length of the vector\r
+ * \r
+ * @return vector length\r
+ */\r
+ public float magnitude() {\r
+ return (float) Math.sqrt(x * x + y * y + z * z);\r
+ }\r
+\r
+ /**\r
+ * Calculates only the squared magnitude/length of the vector. Useful for\r
+ * inverse square law applications and/or for speed reasons or if the real\r
+ * eucledian distance is not required (e.g. sorting).\r
+ * \r
+ * @return squared magnitude (x^2 + y^2 + z^2)\r
+ */\r
+ public float magSquared() {\r
+ return x * x + y * y + z * z;\r
+ }\r
+\r
+ public final Vec3D maxSelf(Vec3D b) {\r
+ x = MathUtils.max(x, b.x);\r
+ y = MathUtils.max(y, b.y);\r
+ z = MathUtils.max(z, b.z);\r
+ return this;\r
+ }\r
+\r
+ public final Vec3D minSelf(Vec3D b) {\r
+ x = MathUtils.min(x, b.x);\r
+ y = MathUtils.min(y, b.y);\r
+ z = MathUtils.min(z, b.z);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Applies a uniform modulo operation to the vector, using the same base for\r
+ * all components.\r
+ * \r
+ * @param base\r
+ * @return itself\r
+ */\r
+ public final Vec3D modSelf(float base) {\r
+ x %= base;\r
+ y %= base;\r
+ z %= base;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Calculates modulo operation for each vector component separately.\r
+ * \r
+ * @param bx\r
+ * @param by\r
+ * @param bz\r
+ * @return itself\r
+ */\r
+\r
+ public final Vec3D modSelf(float bx, float by, float bz) {\r
+ x %= bx;\r
+ y %= by;\r
+ z %= bz;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Normalizes the vector so that its magnitude = 1\r
+ * \r
+ * @return itself\r
+ */\r
+ public Vec3D normalize() {\r
+ float mag = (float) Math.sqrt(x * x + y * y + z * z);\r
+ if (mag > 0) {\r
+ mag = 1f / mag;\r
+ x *= mag;\r
+ y *= mag;\r
+ z *= mag;\r
+ }\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Rotates the vector around the giving axis\r
+ * \r
+ * @param axis\r
+ * rotation axis vector\r
+ * @param theta\r
+ * rotation angle (in radians)\r
+ * @return itself\r
+ */\r
+ public final Vec3D rotateAroundAxis(Vec3D axis, float theta) {\r
+ float ux = axis.x * x;\r
+ float uy = axis.x * y;\r
+ float uz = axis.x * z;\r
+ float vx = axis.y * x;\r
+ float vy = axis.y * y;\r
+ float vz = axis.y * z;\r
+ float wx = axis.z * x;\r
+ float wy = axis.z * y;\r
+ float wz = axis.z * z;\r
+ double si = Math.sin(theta);\r
+ double co = Math.cos(theta);\r
+ float xx = (float) (axis.x\r
+ * (ux + vy + wz)\r
+ + (x * (axis.y * axis.y + axis.z * axis.z) - axis.x * (vy + wz))\r
+ * co + (-wy + vz) * si);\r
+ float yy = (float) (axis.y\r
+ * (ux + vy + wz)\r
+ + (y * (axis.x * axis.x + axis.z * axis.z) - axis.y * (ux + wz))\r
+ * co + (wx - uz) * si);\r
+ float zz = (float) (axis.z\r
+ * (ux + vy + wz)\r
+ + (z * (axis.x * axis.x + axis.y * axis.y) - axis.z * (ux + vy))\r
+ * co + (-vx + uy) * si);\r
+ x = xx;\r
+ y = yy;\r
+ z = zz;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Rotates the vector by the given angle around the X axis.\r
+ * \r
+ * @param theta\r
+ * @return itself\r
+ */\r
+ public final Vec3D rotateX(float theta) {\r
+ float co = (float) Math.cos(theta);\r
+ float si = (float) Math.sin(theta);\r
+ float yy = co * z - si * y;\r
+ z = si * z + co * y;\r
+ y = yy;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Rotates the vector by the given angle around the Y axis.\r
+ * \r
+ * @param theta\r
+ * @return itself\r
+ */\r
+ public final Vec3D rotateY(float theta) {\r
+ float co = (float) Math.cos(theta);\r
+ float si = (float) Math.sin(theta);\r
+ float xx = co * x - si * z;\r
+ z = si * x + co * z;\r
+ x = xx;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Rotates the vector by the given angle around the Z axis.\r
+ * \r
+ * @param theta\r
+ * @return itself\r
+ */\r
+ public final Vec3D rotateZ(float theta) {\r
+ float co = (float) Math.cos(theta);\r
+ float si = (float) Math.sin(theta);\r
+ float xx = co * x - si * y;\r
+ y = si * x + co * y;\r
+ x = xx;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Scales vector uniformly and returns result as new vector.\r
+ * \r
+ * @param s\r
+ * scale factor\r
+ * @return new vector\r
+ */\r
+ public Vec3D scale(float s) {\r
+ return new Vec3D(x * s, y * s, z * s);\r
+ }\r
+\r
+ /**\r
+ * Scales vector non-uniformly and returns result as new vector.\r
+ * \r
+ * @param a\r
+ * scale factor for X coordinate\r
+ * @param b\r
+ * scale factor for Y coordinate\r
+ * @param c\r
+ * scale factor for Z coordinate\r
+ * @return new vector\r
+ */\r
+ public Vec3D scale(float a, float b, float c) {\r
+ return new Vec3D(x * a, y * b, z * c);\r
+ }\r
+\r
+ /**\r
+ * Scales vector non-uniformly by vector v and returns result as new vector\r
+ * \r
+ * @param s\r
+ * scale vector\r
+ * @return new vector\r
+ */\r
+ public Vec3D scale(Vec3D s) {\r
+ return new Vec3D(x * s.x, y * s.y, z * s.z);\r
+ }\r
+\r
+ /**\r
+ * Scales vector uniformly and overrides coordinates with result\r
+ * \r
+ * @param s\r
+ * scale factor\r
+ * @return itself\r
+ */\r
+ public Vec3D scaleSelf(float s) {\r
+ x *= s;\r
+ y *= s;\r
+ z *= s;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Scales vector non-uniformly by vector {a,b,c} and overrides coordinates\r
+ * with result\r
+ * \r
+ * @param a\r
+ * scale factor for X coordinate\r
+ * @param b\r
+ * scale factor for Y coordinate\r
+ * @param c\r
+ * scale factor for Z coordinate\r
+ * @return itself\r
+ */\r
+ public Vec3D scaleSelf(float a, float b, float c) {\r
+ x *= a;\r
+ y *= b;\r
+ z *= c;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Scales vector non-uniformly by vector v and overrides coordinates with\r
+ * result\r
+ * \r
+ * @param s\r
+ * scale vector\r
+ * @return itself\r
+ */\r
+\r
+ public Vec3D scaleSelf(Vec3D s) {\r
+ x *= s.x;\r
+ y *= s.y;\r
+ z *= s.z;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Overrides coordinates with the given values\r
+ * \r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ * @return itself\r
+ */\r
+ public Vec3D set(float x, float y, float z) {\r
+ this.x = x;\r
+ this.y = y;\r
+ this.z = z;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Overrides coordinates with the ones of the given vector\r
+ * \r
+ * @param v\r
+ * vector to be copied\r
+ * @return itself\r
+ */\r
+ public Vec3D set(Vec3D v) {\r
+ x = v.x;\r
+ y = v.y;\r
+ z = v.z;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Replaces all vector components with the signum of their original values.\r
+ * In other words if a components value was negative its new value will be\r
+ * -1, if zero => 0, if positive => +1\r
+ * \r
+ * @return itself\r
+ */\r
+ public Vec3D signum() {\r
+ x = (x < 0 ? -1 : x == 0 ? 0 : 1);\r
+ y = (y < 0 ? -1 : y == 0 ? 0 : 1);\r
+ z = (z < 0 ? -1 : z == 0 ? 0 : 1);\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Subtracts vector {a,b,c} and returns result as new vector.\r
+ * \r
+ * @param a\r
+ * X coordinate\r
+ * @param b\r
+ * Y coordinate\r
+ * @param c\r
+ * Z coordinate\r
+ * @return result as new vector\r
+ */\r
+ public final Vec3D sub(float a, float b, float c) {\r
+ return new Vec3D(x - a, y - b, z - c);\r
+ }\r
+\r
+ /**\r
+ * Subtracts vector v and returns result as new vector.\r
+ * \r
+ * @param v\r
+ * vector to be subtracted\r
+ * @return result as new vector\r
+ */\r
+ public final Vec3D sub(Vec3D v) {\r
+ return new Vec3D(x - v.x, y - v.y, z - v.z);\r
+ }\r
+\r
+ /**\r
+ * Subtracts vector {a,b,c} and overrides coordinates with result.\r
+ * \r
+ * @param a\r
+ * X coordinate\r
+ * @param b\r
+ * Y coordinate\r
+ * @param c\r
+ * Z coordinate\r
+ * @return itself\r
+ */\r
+ public final Vec3D subSelf(float a, float b, float c) {\r
+ x -= a;\r
+ y -= b;\r
+ z -= c;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Subtracts vector v and overrides coordinates with result.\r
+ * \r
+ * @param v\r
+ * vector to be subtracted\r
+ * @return itself\r
+ */\r
+ public final Vec3D subSelf(Vec3D v) {\r
+ x -= v.x;\r
+ y -= v.y;\r
+ z -= v.z;\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Calculates the normal vector on the given ellipsoid in the direction of\r
+ * the current point.\r
+ * \r
+ * @param eO\r
+ * ellipsoid origin/centre\r
+ * @param eR\r
+ * ellipsoid radius\r
+ * @return a unit normal vector to the tangent plane of the ellipsoid in the\r
+ * point.\r
+ */\r
+\r
+ public Vec3D tangentPlaneNormalOfEllipsoid(Vec3D eO, Vec3D eR) {\r
+ Vec3D p = this.sub(eO);\r
+\r
+ float xr2 = eR.x * eR.x;\r
+ float yr2 = eR.y * eR.y;\r
+ float zr2 = eR.z * eR.z;\r
+\r
+ return new Vec3D(p.x / xr2, p.y / yr2, p.z / zr2).normalize();\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see java.lang.Object#toString()\r
+ */\r
+ public String toString() {\r
+ StringBuffer sb = new StringBuffer(48);\r
+ sb.append("{x:").append(x).append(", y:").append(y).append(", z:")\r
+ .append(z).append("}");\r
+ return sb.toString();\r
+ }\r
+\r
+ public float[] toArray() {\r
+ return new float[] { x, y, z };\r
+ }\r
+}\r
--- /dev/null
+package toxi.geom.util;\r
+\r
+public class DefaultSTLColorModel implements STLColorModel {\r
+\r
+ public void formatHeader(byte[] header) {\r
+ }\r
+\r
+ public int formatRGB(int rgb) {\r
+ int col15bits = (rgb >> 3 & 0x1f);\r
+ col15bits |= (rgb >> 11 & 0x1f) << 5;\r
+ col15bits |= (rgb >> 19 & 0x1f) << 10;\r
+ col15bits |= 0x8000;\r
+ return col15bits;\r
+ }\r
+\r
+}\r
--- /dev/null
+package toxi.geom.util;\r
+\r
+public class MaterialiseSTLColorModel implements STLColorModel {\r
+\r
+ int basecolor;\r
+\r
+ public MaterialiseSTLColorModel(int rgb) {\r
+ basecolor = rgb;\r
+ }\r
+\r
+ public void formatHeader(byte[] header) {\r
+ char[] col = new char[] { 'C', 'O', 'L', 'O', 'R', '=' };\r
+ for (int i = 0; i < col.length; i++) {\r
+ header[i] = (byte) col[i];\r
+ }\r
+ header[6] = (byte) (basecolor >> 16 & 0xff);\r
+ header[7] = (byte) (basecolor >> 8 & 0xff);\r
+ header[8] = (byte) (basecolor & 0xff);\r
+ header[9] = (byte) (basecolor >>> 24);\r
+ }\r
+\r
+ public int formatRGB(int rgb) {\r
+ int col15bits = (rgb >> 19 & 0x1f);\r
+ col15bits |= (rgb >> 11 & 0x1f) << 5;\r
+ col15bits |= (rgb >> 3 & 0x1f) << 10;\r
+ col15bits |= 0x8000;\r
+ return col15bits;\r
+ }\r
+}\r
--- /dev/null
+package toxi.geom.util;\r
+\r
+import java.io.FileOutputStream;\r
+import java.io.PrintWriter;\r
+\r
+import toxi.geom.Vec3D;\r
+\r
+/**\r
+ * Extremely bare bones Wavefront OBJ 3D format exporter. Purely handles the\r
+ * writing of data to the .obj file, but does not have any form of mesh\r
+ * management.\r
+ * \r
+ * Might get some more TLC in future versions.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public class OBJWriter {\r
+\r
+ public final String VERSION = "0.25";\r
+\r
+ protected FileOutputStream objStream;\r
+ protected PrintWriter objWriter;\r
+\r
+ public void beginSave(String fn) {\r
+ try {\r
+ objStream = new FileOutputStream(fn);\r
+ objWriter = new PrintWriter(objStream);\r
+ objWriter.println("# generated by OBJExport v" + VERSION);\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+ public void endSave() {\r
+ try {\r
+ objWriter.flush();\r
+ objWriter.close();\r
+ objStream.flush();\r
+ objStream.close();\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+ public void newObject(String name) {\r
+ objWriter.println("o " + name);\r
+ }\r
+\r
+ public void vertex(Vec3D v) {\r
+ objWriter.println("v " + v.x + " " + v.y + " " + v.z);\r
+ }\r
+\r
+ public void face(int a, int b, int c) {\r
+ objWriter.println("f " + a + " " + b + " " + c);\r
+ }\r
+\r
+ public void faceList() {\r
+ objWriter.println("s off");\r
+ }\r
+}\r
--- /dev/null
+package toxi.geom.util;\r
+\r
+public interface STLColorModel {\r
+ void formatHeader(byte[] header);\r
+ \r
+ int formatRGB(int rgb);\r
+}\r
--- /dev/null
+package toxi.geom.util;\r
+\r
+import java.io.DataOutputStream;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+\r
+import toxi.geom.Vec3D;\r
+\r
+/**\r
+ * A simple, but flexible and memory efficient exporter for binary STL files.\r
+ * Custom color support is implemented via the STLcolorModel interface and the\r
+ * exporter comes with the 2 most common format variations defined by the\r
+ * DEFAULT and MATERIALISE constants.\r
+ * \r
+ * The minimal design of this exporter means it does not build an extra list of\r
+ * faces in RAM and so is able to easily export models with millions of faces.\r
+ * \r
+ * http://en.wikipedia.org/wiki/STL_(file_format)\r
+ * \r
+ * @author kschmidt\r
+ * \r
+ */\r
+public class STLWriter {\r
+\r
+ public static final STLColorModel DEFAULT = new DefaultSTLColorModel();\r
+\r
+ public static final STLColorModel MATERIALISE = new MaterialiseSTLColorModel(\r
+ 0xffffffff);\r
+\r
+ protected DataOutputStream ds;\r
+\r
+ protected Vec3D scale = new Vec3D(1, 1, 1);\r
+\r
+ protected boolean useInvertedNormals = false;\r
+\r
+ protected byte[] buf = new byte[4];\r
+\r
+ protected STLColorModel colorModel;\r
+\r
+ public STLWriter() {\r
+ this(DEFAULT);\r
+ }\r
+\r
+ public STLWriter(STLColorModel cm) {\r
+ colorModel = cm;\r
+ }\r
+\r
+ public void beginSave(String fn, int numFaces) {\r
+ try {\r
+ ds = new DataOutputStream(new FileOutputStream(fn));\r
+ writeHeader(numFaces);\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+ public void endSave() {\r
+ try {\r
+ ds.flush();\r
+ ds.close();\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+ public void face(Vec3D a, Vec3D b, Vec3D c) {\r
+ face(a, b, c, 0);\r
+ }\r
+\r
+ public void face(Vec3D a, Vec3D b, Vec3D c, int rgb) {\r
+ try {\r
+ // normal\r
+ Vec3D normal = b.sub(a).cross(c.sub(a)).normalize();\r
+ if (useInvertedNormals) {\r
+ normal.invert();\r
+ }\r
+ writeVector(normal);\r
+ // vertices\r
+ writeVector(a);\r
+ writeVector(b);\r
+ writeVector(c);\r
+ // vertex attrib (color)\r
+ if (rgb != 0) {\r
+ writeShort(colorModel.formatRGB(rgb));\r
+ } else {\r
+ writeShort(0);\r
+ }\r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+ private final void prepareBuffer(int a) {\r
+ buf[0] = (byte) (a >>> 24);\r
+ buf[1] = (byte) (a >> 16 & 0xff);\r
+ buf[2] = (byte) (a >> 8 & 0xff);\r
+ buf[3] = (byte) (a & 0xff);\r
+ }\r
+\r
+ public void setScale(float s) {\r
+ scale.set(s, s, s);\r
+ }\r
+\r
+ public void setScale(Vec3D s) {\r
+ scale.set(s);\r
+ }\r
+\r
+ public void useInvertedNormals(boolean state) {\r
+ useInvertedNormals = state;\r
+ }\r
+\r
+ protected void writeFloat(float a) throws IOException {\r
+ prepareBuffer(Float.floatToRawIntBits(a));\r
+ ds.write(buf, 0, 4);\r
+ }\r
+\r
+ protected void writeHeader(int num) throws IOException {\r
+ byte[] header = new byte[80];\r
+ colorModel.formatHeader(header);\r
+ ds.write(header, 0, 80);\r
+ writeInt(num);\r
+ }\r
+\r
+ protected void writeInt(int a) throws IOException {\r
+ prepareBuffer(a);\r
+ ds.write(buf, 0, 4);\r
+ }\r
+\r
+ protected void writeShort(int a) throws IOException {\r
+ ds.writeByte(a & 0xff);\r
+ ds.writeByte(a >> 8 & 0xff);\r
+ }\r
+\r
+ protected void writeVector(Vec3D v) {\r
+ try {\r
+ writeFloat(v.x * scale.x);\r
+ writeFloat(v.y * scale.y);\r
+ writeFloat(v.z * scale.z);\r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+}\r
--- /dev/null
+package toxi.math;\r
+\r
+/**\r
+ * Implementation of the circular interpolation function.\r
+ * \r
+ * i = a-(b-a) * (sqrt(1 - (1 - f) * (1 - f) ))\r
+ * \r
+ * @author kschmidt\r
+ * \r
+ */\r
+public class CircularInterpolation implements InterpolateStrategy {\r
+\r
+ boolean isFlipped;\r
+\r
+ public CircularInterpolation() {\r
+ this(false);\r
+ }\r
+\r
+ /**\r
+ * The interpolation slope can be flipped to have its steepest ascent\r
+ * towards the end value, rather than at the beginning in the default\r
+ * configuration.\r
+ * \r
+ * @param isFlipped true, if slope is inverted\r
+ */\r
+ public CircularInterpolation(boolean isFlipped) {\r
+ this.isFlipped = isFlipped;\r
+ }\r
+\r
+ public float interpolate(float a, float b, float f) {\r
+ if (isFlipped) {\r
+ return a - (b - a) * ((float) Math.sqrt(1 - f * f) - 1);\r
+ } else {\r
+ f = 1 - f;\r
+ return a + (b - a) * ((float) Math.sqrt(1 - f * f));\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.math;\r
+\r
+/**\r
+ * Implementation of the cosine interpolation function:\r
+ * \r
+ * i = b+(a-b)*(0.5+0.5*cos(f*PI))\r
+ *\r
+ * @author toxi\r
+ *\r
+ */\r
+public class CosineInterpolation implements InterpolateStrategy {\r
+\r
+ /* (non-Javadoc)\r
+ * @see toxi.math.InterpolateStrategy#interpolate(float, float, float)\r
+ */\r
+ public final float interpolate(float a, float b, float f) {\r
+ return b+(a-b)*(float)(0.5+0.5*Math.cos(f*MathUtils.PI));\r
+ }\r
+\r
+}
\ No newline at end of file
--- /dev/null
+package toxi.math;\r
+\r
+/**\r
+ * Defines a generic function to interpolate 2 float values.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+public interface InterpolateStrategy {\r
+\r
+ /**\r
+ * Implements an interpolation equation.\r
+ * \r
+ * @param a\r
+ * current value\r
+ * @param b\r
+ * target value\r
+ * @param f\r
+ * normalized interpolation factor (0.0 .. 1.0)\r
+ * @return interpolated value\r
+ */\r
+ public float interpolate(float a, float b, float f);\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.math;\r
+\r
+/**\r
+ * Implementation of the linear interpolation function\r
+ * \r
+ * i = a+(b-a)*f\r
+ *\r
+ * @author toxi\r
+ *\r
+ */\r
+public class LinearInterpolation implements InterpolateStrategy {\r
+\r
+ /* (non-Javadoc)\r
+ * @see toxi.math.InterpolateStrategy#interpolate(float, float, float)\r
+ */\r
+ public final float interpolate(float a, float b, float f) {\r
+ return a+(b-a)*f;\r
+ }\r
+\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.math;\r
+\r
+import java.util.Random;\r
+\r
+/**\r
+ * Miscellaneous math utilities.\r
+ */\r
+public class MathUtils {\r
+\r
+ /**\r
+ * Log(2)\r
+ */\r
+ public static final float LOG2 = (float) Math.log(2);\r
+\r
+ /**\r
+ * PI\r
+ */\r
+ public static final float PI = 3.14159265358979323846f;\r
+\r
+ /**\r
+ * PI/2\r
+ */\r
+ public static final float HALF_PI = PI / 2;\r
+\r
+ /**\r
+ * PI/3\r
+ */\r
+ public static final float THIRD_PI = PI / 3;\r
+\r
+ /**\r
+ * PI/4\r
+ */\r
+ public static final float QUARTER_PI = PI / 4;\r
+\r
+ /**\r
+ * PI*2\r
+ */\r
+ public static final float TWO_PI = PI * 2;\r
+\r
+ /**\r
+ * Epsilon value, approx. 1.19E-7\r
+ */\r
+ public static final float EPS = 1.1920928955078125E-7f;\r
+\r
+ /**\r
+ * Degrees to radians conversion factor\r
+ */\r
+ public static final float DEG2RAD = PI / 180;\r
+\r
+ /**\r
+ * Radians to degrees conversion factor\r
+ */\r
+ public static final float RAD2DEG = 180 / PI;\r
+\r
+ /**\r
+ * @param x\r
+ * @return absolute value of x\r
+ */\r
+ public static final float abs(float x) {\r
+ return x < 0 ? -x : x;\r
+ }\r
+\r
+ /**\r
+ * @param x\r
+ * @return absolute value of x\r
+ */\r
+ public static final int abs(int x) {\r
+ int y = x >> 31;\r
+ return (x ^ y) - y;\r
+ }\r
+\r
+ /**\r
+ * Rounds up the value to the nearest higher power^2 value.\r
+ * \r
+ * @param x\r
+ * @return power^2 value\r
+ */\r
+ public static final int ceilPowerOf2(int x) {\r
+ return (int) Math.pow(2, (int) (Math.log(x) / LOG2 + 0.5));\r
+ }\r
+\r
+ public static final float clip(float a, float min, float max) {\r
+ return a < min ? min : (a > max ? max : a);\r
+ }\r
+\r
+ public static final int clip(int a, int min, int max) {\r
+ return a < min ? min : (a > max ? max : a);\r
+ }\r
+\r
+ /**\r
+ * Clips the value to the 0.0 .. 1.0 interval.\r
+ * \r
+ * @param a\r
+ * @return clipped value\r
+ * @since 0012\r
+ */\r
+ public static final float clipNormalized(float a) {\r
+ if (a < 0) {\r
+ return 0;\r
+ } else if (a > 1) {\r
+ return 1;\r
+ }\r
+ return a;\r
+ }\r
+\r
+ public static final float degrees(float radians) {\r
+ return radians * RAD2DEG;\r
+ }\r
+\r
+ /**\r
+ * @deprecated renamed into {@link #floor(float)}\r
+ */\r
+ @Deprecated\r
+ public static final int fastFloor(float x) {\r
+ return floor(x);\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ */\r
+ @Deprecated\r
+ public static final float fastInverseSqrt(float x) {\r
+ float half = 0.5F * x;\r
+ int i = Float.floatToIntBits(x);\r
+ i = 0x5f375a86 - (i >> 1);\r
+ x = Float.intBitsToFloat(i);\r
+ return x * (1.5F - half * x * x);\r
+ }\r
+\r
+ /**\r
+ * Computes a fast approximation to <code>Math.pow(a, b)</code>. Adapted\r
+ * from <url>http://www.dctsystems.co.uk/Software/power.html</url>.\r
+ * \r
+ * @param a\r
+ * a positive number\r
+ * @param b\r
+ * a number\r
+ * @return a^b\r
+ * \r
+ * @deprecated\r
+ */\r
+ @Deprecated\r
+ public static final float fastPow(float a, float b) {\r
+ float x = Float.floatToRawIntBits(a);\r
+ x *= 1.0f / (1 << 23);\r
+ x -= 127;\r
+ float y = x - (int) Math.floor(x);\r
+ b *= x + (y - y * y) * 0.346607f;\r
+ y = b - (int) Math.floor(b);\r
+ y = (y - y * y) * 0.33971f;\r
+ return Float.intBitsToFloat((int) ((b + 127 - y) * (1 << 23)));\r
+ }\r
+\r
+ public static final boolean flipCoin() {\r
+ return Math.random() < 0.5;\r
+ }\r
+\r
+ public static final boolean flipCoin(Random rnd) {\r
+ return rnd.nextBoolean();\r
+ }\r
+\r
+ public static final int floor(double x) {\r
+ return x >= 0 ? (int) x : (int) x - 1;\r
+ }\r
+\r
+ /**\r
+ * This method is a *lot* faster than using (int)Math.floor(x).\r
+ * \r
+ * @param x\r
+ * value to be floored\r
+ * @return floored value as integer\r
+ * @since 0012\r
+ */\r
+ public static final int floor(float x) {\r
+ return x >= 0 ? (int) x : (int) x - 1;\r
+ }\r
+\r
+ /**\r
+ * Rounds down the value to the nearest lower power^2 value.\r
+ * \r
+ * @param x\r
+ * @return power^2 value\r
+ */\r
+ public static final int floorPowerOf2(int x) {\r
+ return (int) Math.pow(2, (int) (Math.log(x) / LOG2));\r
+ }\r
+\r
+ public static final float max(float a, float b) {\r
+ return a > b ? a : b;\r
+ }\r
+\r
+ /**\r
+ * Returns the maximum value of three floats.\r
+ * \r
+ * @param a\r
+ * @param b\r
+ * @param c\r
+ * @return max val\r
+ */\r
+ public static final float max(float a, float b, float c) {\r
+ return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);\r
+ }\r
+\r
+ public static final int max(int a, int b) {\r
+ return a > b ? a : b;\r
+ }\r
+\r
+ /**\r
+ * Returns the maximum value of three ints.\r
+ * \r
+ * @param a\r
+ * @param b\r
+ * @param c\r
+ * @return max val\r
+ */\r
+ public static final int max(int a, int b, int c) {\r
+ return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);\r
+ }\r
+\r
+ public static final float min(float a, float b) {\r
+ return a < b ? a : b;\r
+ }\r
+\r
+ /**\r
+ * Returns the minimum value of three floats.\r
+ * \r
+ * @param a\r
+ * @param b\r
+ * @param c\r
+ * @return min val\r
+ */\r
+ public static final float min(float a, float b, float c) {\r
+ return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c);\r
+ }\r
+\r
+ public static final int min(int a, int b) {\r
+ return a < b ? a : b;\r
+ }\r
+\r
+ /**\r
+ * Returns the minimum value of three ints.\r
+ * \r
+ * @param a\r
+ * @param b\r
+ * @param c\r
+ * @return min val\r
+ */\r
+ public static final int min(int a, int b, int c) {\r
+ return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c);\r
+ }\r
+\r
+ /**\r
+ * Returns a random number in the interval -1 .. +1.\r
+ * \r
+ * @return random float\r
+ */\r
+ public static final float normalizedRandom() {\r
+ return (float) Math.random() * 2 - 1;\r
+ }\r
+\r
+ /**\r
+ * Returns a random number in the interval -1 .. +1 using the {@link Random}\r
+ * instance provided.\r
+ * \r
+ * @return random float\r
+ */\r
+ public static final float normalizedRandom(Random rnd) {\r
+ return rnd.nextFloat() * 2 - 1;\r
+ }\r
+\r
+ public static final float radians(float degrees) {\r
+ return degrees * DEG2RAD;\r
+ }\r
+\r
+ public static final float random(float max) {\r
+ return (float) Math.random() * max;\r
+ }\r
+\r
+ public static final float random(float min, float max) {\r
+ return (float) Math.random() * (max - min) + min;\r
+ }\r
+\r
+ public static final int random(int max) {\r
+ return (int) (Math.random() * max);\r
+ }\r
+\r
+ public static final int random(int min, int max) {\r
+ return (int) (Math.random() * (max - min)) + min;\r
+ }\r
+\r
+ public static final float random(Random rnd, float max) {\r
+ return rnd.nextFloat() * max;\r
+ }\r
+\r
+ public static final float random(Random rnd, float min, float max) {\r
+ return rnd.nextFloat() * (max - min) + min;\r
+ }\r
+\r
+ public static final int random(Random rnd, int max) {\r
+ return (int) (rnd.nextDouble() * max);\r
+ }\r
+\r
+ public static final int random(Random rnd, int min, int max) {\r
+ return (int) (rnd.nextDouble() * (max - min)) + min;\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ */\r
+ @Deprecated\r
+ public static final float sqrt(float x) {\r
+ x = fastInverseSqrt(x);\r
+ if (x > 0) {\r
+ return 1.0f / x;\r
+ } else\r
+ return 0;\r
+ }\r
+}\r
--- /dev/null
+package toxi.math;\r
+\r
+\r
+/**\r
+ * Implements the sigmoid interpolation function with adjustable curve sharpness\r
+ * \r
+ * @author kschmidt\r
+ */\r
+public class SigmoidInterpolation implements InterpolateStrategy {\r
+\r
+ private float sharpness;\r
+\r
+ private float sharpPremult;\r
+\r
+ public SigmoidInterpolation() {\r
+ this(1f);\r
+ }\r
+\r
+ public SigmoidInterpolation(float s) {\r
+ setSharpness(s);\r
+ }\r
+\r
+ private void setSharpness(float s) {\r
+ sharpness = s;\r
+ sharpPremult = 5 * s;\r
+ }\r
+\r
+ public float getSharpness() {\r
+ return sharpness;\r
+ }\r
+ \r
+ public float interpolate(float a, float b, float f) {\r
+ f = (f * 2 - 1) * sharpPremult;\r
+ f = (float) (1.0f / (1.0f + Math.exp(-f)));\r
+ return a + (b - a) * f;\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.math;\r
+\r
+/**\r
+ * Lookup table for fast sine & cosine computations. The table currently has a\r
+ * fixed precision of 0.25 degrees to which input angles will be rounded to. All\r
+ * methods are static and can be used with both positive and negative input\r
+ * angles.\r
+ */\r
+public class SinCosLUT {\r
+\r
+ /**\r
+ * set table precision to 0.25 degrees\r
+ */\r
+ public static final float SC_PRECISION = 0.25f;\r
+\r
+ /**\r
+ * calculate reciprocal for conversions\r
+ */\r
+ public static final float SC_INV_PREC = 1.0f / SC_PRECISION;\r
+\r
+ /**\r
+ * compute required table length\r
+ */\r
+ public static final int SC_PERIOD = (int) (360f * SC_INV_PREC);\r
+\r
+ /**\r
+ * LUT for sine values\r
+ */\r
+ public static final float[] sinLUT = new float[SC_PERIOD];\r
+\r
+ /**\r
+ * LUT for cosine values\r
+ */\r
+ public static final float[] cosLUT = new float[SC_PERIOD];\r
+\r
+ /**\r
+ * Pre-multiplied degrees -> radians\r
+ */\r
+ private static final float DEG_TO_RAD = (float) (Math.PI / 180.0)\r
+ * SC_PRECISION;\r
+\r
+ /**\r
+ * Pre-multiplied radians - degrees\r
+ */\r
+ private static final float RAD_TO_DEG = (float) (180.0 / Math.PI)\r
+ / SC_PRECISION;\r
+\r
+ // init sin/cos tables with values\r
+ static {\r
+ for (int i = 0; i < SC_PERIOD; i++) {\r
+ sinLUT[i] = (float) Math.sin(i * DEG_TO_RAD);\r
+ cosLUT[i] = (float) Math.cos(i * DEG_TO_RAD);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Calculates sine for the passed angle in radians.\r
+ * \r
+ * @param theta\r
+ * @return sine value for theta\r
+ */\r
+ public static final float sin(float theta) {\r
+ while (theta < 0) {\r
+ theta += MathUtils.TWO_PI;\r
+ }\r
+ return sinLUT[(int) (theta * RAD_TO_DEG) % SC_PERIOD];\r
+ }\r
+\r
+ /**\r
+ * Calculate cosine for the passed in angle in radians.\r
+ * \r
+ * @param theta\r
+ * @return cosine value for theta\r
+ */\r
+ public static final float cos(float theta) {\r
+ while (theta < 0) {\r
+ theta += MathUtils.TWO_PI;\r
+ }\r
+ return cosLUT[(int) (theta * RAD_TO_DEG) % SC_PERIOD];\r
+ }\r
+}\r
--- /dev/null
+package toxi.math.conversion;\r
+\r
+public class UnitTranslator {\r
+\r
+ static final double INCH_MM = 25.4;\r
+\r
+ static final double POINT_POSTSCRIPT = 72.0;\r
+\r
+ double pixelsToInch(int pix, int dpi) {\r
+ return (double) pix / dpi;\r
+ }\r
+\r
+ double pixelsToPoints(int pix, int dpi) {\r
+ return pixelsToInch(pix, dpi) * POINT_POSTSCRIPT;\r
+ }\r
+\r
+ double pixelsToMillis(int pix, int dpi) {\r
+ return pixelsToInch(pix, dpi) * INCH_MM;\r
+ }\r
+\r
+ double millisToPoints(double mm) {\r
+ return mm / INCH_MM * POINT_POSTSCRIPT;\r
+ }\r
+\r
+ double pointsToMillis(double pt) {\r
+ return pt / POINT_POSTSCRIPT * INCH_MM;\r
+ }\r
+\r
+ int pointsToPixels(double pt, int dpi) {\r
+ return millisToPixels(pointsToMillis(pt), dpi);\r
+ }\r
+\r
+ int millisToPixels(double mm, int dpi) {\r
+ return (int) (mm / INCH_MM * dpi);\r
+ }\r
+\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.math.noise;\r
+\r
+import java.util.Random;\r
+\r
+import toxi.math.SinCosLUT;\r
+\r
+\r
+public class PerlinNoise {\r
+ // ////////////////////////////////////////////////////////////\r
+ // PERLIN NOISE taken from the\r
+ // [toxi 040903]\r
+ // octaves and amplitude amount per octave are now user controlled\r
+ // via the noiseDetail() function.\r
+ // [toxi 030902]\r
+ // cleaned up code and now using bagel's cosine table to speed up\r
+ // [toxi 030901]\r
+ // implementation by the german demo group farbrausch\r
+ // as used in their demo "art": http://www.farb-rausch.de/fr010src.zip\r
+\r
+ protected static final int PERLIN_YWRAPB = 4;\r
+\r
+ protected static final int PERLIN_YWRAP = 1 << PERLIN_YWRAPB;\r
+\r
+ protected static final int PERLIN_ZWRAPB = 8;\r
+\r
+ protected static final int PERLIN_ZWRAP = 1 << PERLIN_ZWRAPB;\r
+\r
+ protected static final int PERLIN_SIZE = 4095;\r
+\r
+ private static final float PERLIN_MIN_AMPLITUDE = 0.001f;\r
+\r
+ protected int perlin_octaves = 4; // default to medium smooth\r
+\r
+ protected float perlin_amp_falloff = 0.5f; // 50% reduction/octave\r
+\r
+ // [toxi 031112]\r
+ // new vars needed due to recent change of cos table in PGraphics\r
+ protected int perlin_TWOPI, perlin_PI;\r
+\r
+ protected float[] perlin_cosTable;\r
+\r
+ protected float perlin[];\r
+\r
+ protected Random perlinRandom;\r
+\r
+ public PerlinNoise() {\r
+ noiseSeed(System.nanoTime());\r
+ }\r
+\r
+ /**\r
+ * Computes the Perlin noise function value at point x.\r
+ */\r
+ public float noise(float x) {\r
+ // is this legit? it's a dumb way to do it (but repair it later)\r
+ return noise(x, 0f, 0f);\r
+ }\r
+\r
+ /**\r
+ * Computes the Perlin noise function value at the point x, y.\r
+ */\r
+ public float noise(float x, float y) {\r
+ return noise(x, y, 0f);\r
+ }\r
+\r
+ /**\r
+ * Computes the Perlin noise function value at x, y, z.\r
+ */\r
+ public float noise(float x, float y, float z) {\r
+ if (perlin == null) {\r
+ if (perlinRandom == null) {\r
+ perlinRandom = new Random();\r
+ }\r
+ perlin = new float[PERLIN_SIZE + 1];\r
+ for (int i = 0; i < PERLIN_SIZE + 1; i++) {\r
+ perlin[i] = perlinRandom.nextFloat(); // (float)Math.random();\r
+ }\r
+ // [toxi 031112]\r
+ // noise broke due to recent change of cos table in PGraphics\r
+ // this will take care of it\r
+ perlin_cosTable = SinCosLUT.cosLUT;\r
+ perlin_TWOPI = perlin_PI = SinCosLUT.SC_PERIOD;\r
+ perlin_PI >>= 1;\r
+ }\r
+\r
+ if (x < 0)\r
+ x = -x;\r
+ if (y < 0)\r
+ y = -y;\r
+ if (z < 0)\r
+ z = -z;\r
+\r
+ int xi = (int) x, yi = (int) y, zi = (int) z;\r
+ float xf = (float) (x - xi);\r
+ float yf = (float) (y - yi);\r
+ float zf = (float) (z - zi);\r
+ float rxf, ryf;\r
+\r
+ float r = 0;\r
+ float ampl = 0.5f;\r
+\r
+ float n1, n2, n3;\r
+\r
+ for (int i = 0; i < perlin_octaves; i++) {\r
+ int of = xi + (yi << PERLIN_YWRAPB) + (zi << PERLIN_ZWRAPB);\r
+\r
+ rxf = noise_fsc(xf);\r
+ ryf = noise_fsc(yf);\r
+\r
+ n1 = perlin[of & PERLIN_SIZE];\r
+ n1 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n1);\r
+ n2 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE];\r
+ n2 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n2);\r
+ n1 += ryf * (n2 - n1);\r
+\r
+ of += PERLIN_ZWRAP;\r
+ n2 = perlin[of & PERLIN_SIZE];\r
+ n2 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n2);\r
+ n3 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE];\r
+ n3 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n3);\r
+ n2 += ryf * (n3 - n2);\r
+\r
+ n1 += noise_fsc(zf) * (n2 - n1);\r
+\r
+ r += n1 * ampl;\r
+ ampl *= perlin_amp_falloff;\r
+ \r
+ // break if amp has no more impact\r
+ if (ampl<PERLIN_MIN_AMPLITUDE) break;\r
+ \r
+ xi <<= 1;\r
+ xf *= 2;\r
+ yi <<= 1;\r
+ yf *= 2;\r
+ zi <<= 1;\r
+ zf *= 2;\r
+\r
+ if (xf >= 1.0f) {\r
+ xi++;\r
+ xf--;\r
+ }\r
+ if (yf >= 1.0f) {\r
+ yi++;\r
+ yf--;\r
+ }\r
+ if (zf >= 1.0f) {\r
+ zi++;\r
+ zf--;\r
+ }\r
+ }\r
+ return r;\r
+ }\r
+\r
+ // [toxi 031112]\r
+ // now adjusts to the size of the cosLUT used via\r
+ // the new variables, defined above\r
+ private float noise_fsc(float i) {\r
+ // using bagel's cosine table instead\r
+ return 0.5f * (1.0f - perlin_cosTable[(int) (i * perlin_PI)\r
+ % perlin_TWOPI]);\r
+ }\r
+\r
+ // [toxi 040903]\r
+ // make perlin noise quality user controlled to allow\r
+ // for different levels of detail. lower values will produce\r
+ // smoother results as higher octaves are surpressed\r
+\r
+ public void noiseDetail(int lod) {\r
+ if (lod > 0)\r
+ perlin_octaves = lod;\r
+ }\r
+\r
+ public void noiseDetail(int lod, float falloff) {\r
+ if (lod > 0)\r
+ perlin_octaves = lod;\r
+ if (falloff > 0)\r
+ perlin_amp_falloff = falloff;\r
+ }\r
+\r
+ public void noiseSeed(long what) {\r
+ if (perlinRandom == null)\r
+ perlinRandom = new Random();\r
+ perlinRandom.setSeed(what);\r
+ perlin = null;\r
+ }\r
+}\r
--- /dev/null
+package toxi.math.noise;\r
+\r
+/**\r
+ * Simplex Noise in 2D, 3D and 4D. Based on the example code of this paper:\r
+ * http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf\r
+ * \r
+ * @author Stefan Gustavson, Linkping University, Sweden (stegu at itn dot liu\r
+ * dot se)\r
+ * \r
+ * Slight optimizations & restructuring by\r
+ * @author Karsten Schmidt (info at toxi dot co dot uk)\r
+ * \r
+ */\r
+\r
+public class SimplexNoise {\r
+\r
+ private static final double SQRT3 = Math.sqrt(3.0);\r
+ private static final double SQRT5 = Math.sqrt(5.0);\r
+\r
+ /**\r
+ * Skewing and unskewing factors for 2D, 3D and 4D, some of them\r
+ * pre-multiplied.\r
+ */\r
+ private static final double F2 = 0.5 * (SQRT3 - 1.0);\r
+ private static final double G2 = (3.0 - SQRT3) / 6.0;\r
+ private static final double G22 = G2 * 2.0 - 1;\r
+\r
+ private static final double F3 = 1.0 / 3.0;\r
+ private static final double G3 = 1.0 / 6.0;\r
+\r
+ private static final double F4 = (SQRT5 - 1.0) / 4.0;\r
+ private static final double G4 = (5.0 - SQRT5) / 20.0;\r
+ private static final double G42 = G4 * 2.0;\r
+ private static final double G43 = G4 * 3.0;\r
+ private static final double G44 = G4 * 4.0 - 1.0;\r
+\r
+ /**\r
+ * Gradient vectors for 3D (pointing to mid points of all edges of a unit\r
+ * cube)\r
+ */\r
+ private static final int[][] grad3 = { { 1, 1, 0 }, { -1, 1, 0 },\r
+ { 1, -1, 0 }, { -1, -1, 0 }, { 1, 0, 1 }, { -1, 0, 1 },\r
+ { 1, 0, -1 }, { -1, 0, -1 }, { 0, 1, 1 }, { 0, -1, 1 },\r
+ { 0, 1, -1 }, { 0, -1, -1 } };\r
+\r
+ /**\r
+ * Gradient vectors for 4D (pointing to mid points of all edges of a unit 4D\r
+ * hypercube)\r
+ */\r
+ private static final int[][] grad4 = { { 0, 1, 1, 1 }, { 0, 1, 1, -1 },\r
+ { 0, 1, -1, 1 }, { 0, 1, -1, -1 }, { 0, -1, 1, 1 },\r
+ { 0, -1, 1, -1 }, { 0, -1, -1, 1 }, { 0, -1, -1, -1 },\r
+ { 1, 0, 1, 1 }, { 1, 0, 1, -1 }, { 1, 0, -1, 1 }, { 1, 0, -1, -1 },\r
+ { -1, 0, 1, 1 }, { -1, 0, 1, -1 }, { -1, 0, -1, 1 },\r
+ { -1, 0, -1, -1 }, { 1, 1, 0, 1 }, { 1, 1, 0, -1 },\r
+ { 1, -1, 0, 1 }, { 1, -1, 0, -1 }, { -1, 1, 0, 1 },\r
+ { -1, 1, 0, -1 }, { -1, -1, 0, 1 }, { -1, -1, 0, -1 },\r
+ { 1, 1, 1, 0 }, { 1, 1, -1, 0 }, { 1, -1, 1, 0 }, { 1, -1, -1, 0 },\r
+ { -1, 1, 1, 0 }, { -1, 1, -1, 0 }, { -1, -1, 1, 0 },\r
+ { -1, -1, -1, 0 } };\r
+\r
+ /**\r
+ * Permutation table\r
+ */\r
+ private static final int[] p = { 151, 160, 137, 91, 90, 15, 131, 13, 201,\r
+ 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37,\r
+ 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62,\r
+ 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56,\r
+ 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139,\r
+ 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133,\r
+ 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25,\r
+ 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200,\r
+ 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3,\r
+ 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255,\r
+ 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,\r
+ 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153,\r
+ 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79,\r
+ 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242,\r
+ 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249,\r
+ 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204,\r
+ 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222,\r
+ 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 };\r
+\r
+ /**\r
+ * To remove the need for index wrapping, double the permutation table\r
+ * length\r
+ */\r
+ private static int[] perm = new int[0x200];\r
+ /**\r
+ * A lookup table to traverse the simplex around a given point in 4D.\r
+ * Details can be found where this table is used, in the 4D noise method.\r
+ */\r
+ private static final int[][] simplex = { { 0, 1, 2, 3 }, { 0, 1, 3, 2 },\r
+ { 0, 0, 0, 0 }, { 0, 2, 3, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },\r
+ { 0, 0, 0, 0 }, { 1, 2, 3, 0 }, { 0, 2, 1, 3 }, { 0, 0, 0, 0 },\r
+ { 0, 3, 1, 2 }, { 0, 3, 2, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },\r
+ { 0, 0, 0, 0 }, { 1, 3, 2, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },\r
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },\r
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 2, 0, 3 }, { 0, 0, 0, 0 },\r
+ { 1, 3, 0, 2 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },\r
+ { 2, 3, 0, 1 }, { 2, 3, 1, 0 }, { 1, 0, 2, 3 }, { 1, 0, 3, 2 },\r
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 2, 0, 3, 1 },\r
+ { 0, 0, 0, 0 }, { 2, 1, 3, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },\r
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },\r
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 2, 0, 1, 3 }, { 0, 0, 0, 0 },\r
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 3, 0, 1, 2 }, { 3, 0, 2, 1 },\r
+ { 0, 0, 0, 0 }, { 3, 1, 2, 0 }, { 2, 1, 0, 3 }, { 0, 0, 0, 0 },\r
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 3, 1, 0, 2 }, { 0, 0, 0, 0 },\r
+ { 3, 2, 0, 1 }, { 3, 2, 1, 0 } };\r
+\r
+ static {\r
+ for (int i = 0; i < 0x200; i++) {\r
+ perm[i] = p[i & 0xff];\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Computes dot product in 2D.\r
+ * \r
+ * @param g\r
+ * 2-vector (grid offset)\r
+ * @param x\r
+ * @param y\r
+ * @return dot product\r
+ */\r
+ private static double dot(int g[], double x, double y) {\r
+ return g[0] * x + g[1] * y;\r
+ }\r
+\r
+ /**\r
+ * Computes dot product in 3D.\r
+ * \r
+ * @param g\r
+ * 3-vector (grid offset)\r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ * @return dot product\r
+ */\r
+ private static double dot(int g[], double x, double y, double z) {\r
+ return g[0] * x + g[1] * y + g[2] * z;\r
+ }\r
+\r
+ /**\r
+ * Computes dot product in 4D.\r
+ * \r
+ * @param g\r
+ * 4-vector (grid offset)\r
+ * @param x\r
+ * @param y\r
+ * @param z\r
+ * @param w\r
+ * @return dot product\r
+ */\r
+ private static double dot(int g[], double x, double y, double z, double w) {\r
+ return g[0] * x + g[1] * y + g[2] * z + g[3] * w;\r
+ }\r
+\r
+ /**\r
+ * This method is a *lot* faster than using (int)Math.floor(x).\r
+ * \r
+ * @param x\r
+ * value to be floored\r
+ * @return\r
+ */\r
+ private static final int fastfloor(double x) {\r
+ return x > 0 ? (int) x : (int) x - 1;\r
+ }\r
+\r
+ /**\r
+ * Computes 2D Simplex Noise.\r
+ * \r
+ * @param x\r
+ * coordinate\r
+ * @param y\r
+ * coordinate\r
+ * @return noise value in range -1 ... +1.\r
+ */\r
+ public static double noise(double x, double y) {\r
+ double n0 = 0, n1 = 0, n2 = 0; // Noise contributions from the three\r
+ // corners\r
+ // Skew the input space to determine which simplex cell we're in\r
+ double s = (x + y) * F2; // Hairy factor for 2D\r
+ int i = fastfloor(x + s);\r
+ int j = fastfloor(y + s);\r
+ double t = (i + j) * G2;\r
+ double x0 = x - (i - t); // The x,y distances from the cell origin\r
+ double y0 = y - (j - t);\r
+ // For the 2D case, the simplex shape is an equilateral triangle.\r
+ // Determine which simplex we are in.\r
+ int i1, j1; // Offsets for second (middle) corner of simplex in (i,j)\r
+ if (x0 > y0) {\r
+ i1 = 1;\r
+ j1 = 0;\r
+ } // lower triangle, XY order: (0,0)->(1,0)->(1,1)\r
+ else {\r
+ i1 = 0;\r
+ j1 = 1;\r
+ } // upper triangle, YX order: (0,0)->(0,1)->(1,1)\r
+ // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and\r
+ // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where\r
+ // c = (3-sqrt(3))/6\r
+ double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed\r
+ double y1 = y0 - j1 + G2;\r
+ double x2 = x0 + G22; // Offsets for last corner in (x,y) unskewed\r
+ double y2 = y0 + G22;\r
+ // Work out the hashed gradient indices of the three simplex corners\r
+ int ii = i & 0xff;\r
+ int jj = j & 0xff;\r
+ // Calculate the contribution from the three corners\r
+ double t0 = 0.5 - x0 * x0 - y0 * y0;\r
+ if (t0 > 0) {\r
+ t0 *= t0;\r
+ int gi0 = perm[ii + perm[jj]] % 12;\r
+ n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for\r
+ // 2D gradient\r
+ }\r
+ double t1 = 0.5 - x1 * x1 - y1 * y1;\r
+ if (t1 > 0) {\r
+ t1 *= t1;\r
+ int gi1 = perm[ii + i1 + perm[jj + j1]] % 12;\r
+ n1 = t1 * t1 * dot(grad3[gi1], x1, y1);\r
+ }\r
+ double t2 = 0.5 - x2 * x2 - y2 * y2;\r
+ if (t2 > 0) {\r
+ t2 *= t2;\r
+ int gi2 = perm[ii + 1 + perm[jj + 1]] % 12;\r
+ n2 = t2 * t2 * dot(grad3[gi2], x2, y2);\r
+ }\r
+ // Add contributions from each corner to get the final noise value.\r
+ // The result is scaled to return values in the interval [-1,1].\r
+ return 70.0 * (n0 + n1 + n2);\r
+ }\r
+\r
+ /**\r
+ * Computes 3D Simplex Noise.\r
+ * \r
+ * @param x\r
+ * coordinate\r
+ * @param y\r
+ * coordinate\r
+ * @param z\r
+ * coordinate\r
+ * @return noise value in range -1 ... +1\r
+ */\r
+ public static double noise(double x, double y, double z) {\r
+ double n0 = 0, n1 = 0, n2 = 0, n3 = 0;\r
+ // Noise contributions from the\r
+ // four corners\r
+ // Skew the input space to determine which simplex cell we're in\r
+ // final double F3 = 1.0 / 3.0;\r
+ double s = (x + y + z) * F3; // Very nice and simple skew factor\r
+ // for 3D\r
+ int i = fastfloor(x + s);\r
+ int j = fastfloor(y + s);\r
+ int k = fastfloor(z + s);\r
+ // final double G3 = 1.0 / 6.0; // Very nice and simple unskew factor,\r
+ // too\r
+ double t = (i + j + k) * G3;\r
+ double x0 = x - (i - t); // The x,y,z distances from the cell origin\r
+ double y0 = y - (j - t);\r
+ double z0 = z - (k - t);\r
+ // For the 3D case, the simplex shape is a slightly irregular\r
+ // tetrahedron.\r
+ // Determine which simplex we are in.\r
+ int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k)\r
+ // coords\r
+ int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords\r
+ if (x0 >= y0) {\r
+ if (y0 >= z0) {\r
+ i1 = 1;\r
+ j1 = 0;\r
+ k1 = 0;\r
+ i2 = 1;\r
+ j2 = 1;\r
+ k2 = 0;\r
+ } // X Y Z order\r
+ else if (x0 >= z0) {\r
+ i1 = 1;\r
+ j1 = 0;\r
+ k1 = 0;\r
+ i2 = 1;\r
+ j2 = 0;\r
+ k2 = 1;\r
+ } // X Z Y order\r
+ else {\r
+ i1 = 0;\r
+ j1 = 0;\r
+ k1 = 1;\r
+ i2 = 1;\r
+ j2 = 0;\r
+ k2 = 1;\r
+ } // Z X Y order\r
+ } else { // x0<y0\r
+ if (y0 < z0) {\r
+ i1 = 0;\r
+ j1 = 0;\r
+ k1 = 1;\r
+ i2 = 0;\r
+ j2 = 1;\r
+ k2 = 1;\r
+ } // Z Y X order\r
+ else if (x0 < z0) {\r
+ i1 = 0;\r
+ j1 = 1;\r
+ k1 = 0;\r
+ i2 = 0;\r
+ j2 = 1;\r
+ k2 = 1;\r
+ } // Y Z X order\r
+ else {\r
+ i1 = 0;\r
+ j1 = 1;\r
+ k1 = 0;\r
+ i2 = 1;\r
+ j2 = 1;\r
+ k2 = 0;\r
+ } // Y X Z order\r
+ }\r
+ // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),\r
+ // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z),\r
+ // and\r
+ // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z),\r
+ // where\r
+ // c = 1/6.\r
+ double x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords\r
+ double y1 = y0 - j1 + G3;\r
+ double z1 = z0 - k1 + G3;\r
+\r
+ double x2 = x0 - i2 + F3; // Offsets for third corner in (x,y,z)\r
+ double y2 = y0 - j2 + F3;\r
+ double z2 = z0 - k2 + F3;\r
+\r
+ double x3 = x0 - 0.5; // Offsets for last corner in (x,y,z)\r
+ double y3 = y0 - 0.5;\r
+ double z3 = z0 - 0.5;\r
+ // Work out the hashed gradient indices of the four simplex corners\r
+ int ii = i & 0xff;\r
+ int jj = j & 0xff;\r
+ int kk = k & 0xff;\r
+\r
+ // Calculate the contribution from the four corners\r
+ double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;\r
+ if (t0 > 0) {\r
+ t0 *= t0;\r
+ int gi0 = perm[ii + perm[jj + perm[kk]]] % 12;\r
+ n0 = t0 * t0 * dot(grad3[gi0], x0, y0, z0);\r
+ }\r
+ double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;\r
+ if (t1 > 0) {\r
+ t1 *= t1;\r
+ int gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1]]] % 12;\r
+ n1 = t1 * t1 * dot(grad3[gi1], x1, y1, z1);\r
+ }\r
+ double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;\r
+ if (t2 > 0) {\r
+ t2 *= t2;\r
+ int gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2]]] % 12;\r
+ n2 = t2 * t2 * dot(grad3[gi2], x2, y2, z2);\r
+ }\r
+ double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;\r
+ if (t3 > 0) {\r
+ t3 *= t3;\r
+ int gi3 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1]]] % 12;\r
+ n3 = t3 * t3 * dot(grad3[gi3], x3, y3, z3);\r
+ }\r
+ // Add contributions from each corner to get the final noise value.\r
+ // The result is scaled to stay just inside [-1,1]\r
+ return 32.0 * (n0 + n1 + n2 + n3);\r
+ }\r
+\r
+ /**\r
+ * Computes 4D Simplex Noise.\r
+ * \r
+ * @param x\r
+ * coordinate\r
+ * @param y\r
+ * coordinate\r
+ * @param z\r
+ * coordinate\r
+ * @param w\r
+ * coordinate\r
+ * @return noise value in range -1 ... +1\r
+ */\r
+ public static double noise(double x, double y, double z, double w) {\r
+ // The skewing and unskewing factors are hairy again for the 4D case\r
+ double n0 = 0, n1 = 0, n2 = 0, n3 = 0, n4 = 0; // Noise contributions\r
+ // from the five corners\r
+ // Skew the (x,y,z,w) space to determine which cell of 24 simplices\r
+ double s = (x + y + z + w) * F4; // Factor for 4D skewing\r
+ int i = fastfloor(x + s);\r
+ int j = fastfloor(y + s);\r
+ int k = fastfloor(z + s);\r
+ int l = fastfloor(w + s);\r
+ double t = (i + j + k + l) * G4; // Factor for 4D unskewing\r
+ double x0 = x - (i - t); // The x,y,z,w distances from the cell origin\r
+ double y0 = y - (j - t);\r
+ double z0 = z - (k - t);\r
+ double w0 = w - (l - t);\r
+ // For the 4D case, the simplex is a 4D shape I won't even try to\r
+ // describe.\r
+ // To find out which of the 24 possible simplices we're in, we need to\r
+ // determine the magnitude ordering of x0, y0, z0 and w0.\r
+ // The method below is a good way of finding the ordering of x,y,z,w and\r
+ // then find the correct traversal order for the simplex were in.\r
+ // First, six pair-wise comparisons are performed between each possible\r
+ // pair of the four coordinates, and the results are used to add up\r
+ // binary bits for an integer index.\r
+ int c = 0;\r
+ if (x0 > y0) {\r
+ c = 0x20;\r
+ }\r
+ if (x0 > z0) {\r
+ c |= 0x10;\r
+ }\r
+ if (y0 > z0) {\r
+ c |= 0x08;\r
+ }\r
+ if (x0 > w0) {\r
+ c |= 0x04;\r
+ }\r
+ if (y0 > w0) {\r
+ c |= 0x02;\r
+ }\r
+ if (z0 > w0) {\r
+ c |= 0x01;\r
+ }\r
+ int i1, j1, k1, l1; // The integer offsets for the second simplex corner\r
+ int i2, j2, k2, l2; // The integer offsets for the third simplex corner\r
+ int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner\r
+ // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some\r
+ // order. Many values of c will never occur, since e.g. x>y>z>w makes\r
+ // x<z, y<w and x<w impossible. Only the 24 indices which have non-zero\r
+ // entries make any sense. We use a thresholding to set the coordinates\r
+ // in turn from the largest magnitude. The number 3 in the "simplex"\r
+ // array is at the position of the largest coordinate.\r
+ int[] sc = simplex[c];\r
+ i1 = sc[0] >= 3 ? 1 : 0;\r
+ j1 = sc[1] >= 3 ? 1 : 0;\r
+ k1 = sc[2] >= 3 ? 1 : 0;\r
+ l1 = sc[3] >= 3 ? 1 : 0;\r
+ // The number 2 in the "simplex" array is at the second largest\r
+ // coordinate.\r
+ i2 = sc[0] >= 2 ? 1 : 0;\r
+ j2 = sc[1] >= 2 ? 1 : 0;\r
+ k2 = sc[2] >= 2 ? 1 : 0;\r
+ l2 = sc[3] >= 2 ? 1 : 0;\r
+ // The number 1 in the "simplex" array is at the second smallest\r
+ // coordinate.\r
+ i3 = sc[0] >= 1 ? 1 : 0;\r
+ j3 = sc[1] >= 1 ? 1 : 0;\r
+ k3 = sc[2] >= 1 ? 1 : 0;\r
+ l3 = sc[3] >= 1 ? 1 : 0;\r
+ // The fifth corner has all coordinate offsets = 1, so no need to look\r
+ // that up.\r
+ double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w)\r
+ double y1 = y0 - j1 + G4;\r
+ double z1 = z0 - k1 + G4;\r
+ double w1 = w0 - l1 + G4;\r
+\r
+ double x2 = x0 - i2 + G42; // Offsets for third corner in (x,y,z,w)\r
+ double y2 = y0 - j2 + G42;\r
+ double z2 = z0 - k2 + G42;\r
+ double w2 = w0 - l2 + G42;\r
+\r
+ double x3 = x0 - i3 + G43; // Offsets for fourth corner in (x,y,z,w)\r
+ double y3 = y0 - j3 + G43;\r
+ double z3 = z0 - k3 + G43;\r
+ double w3 = w0 - l3 + G43;\r
+\r
+ double x4 = x0 + G44; // Offsets for last corner in (x,y,z,w)\r
+ double y4 = y0 + G44;\r
+ double z4 = z0 + G44;\r
+ double w4 = w0 + G44;\r
+\r
+ // Work out the hashed gradient indices of the five simplex corners\r
+ int ii = i & 0xff;\r
+ int jj = j & 0xff;\r
+ int kk = k & 0xff;\r
+ int ll = l & 0xff;\r
+\r
+ // Calculate the contribution from the five corners\r
+ double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;\r
+ if (t0 > 0) {\r
+ t0 *= t0;\r
+ int gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32;\r
+ n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0);\r
+ }\r
+ double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;\r
+ if (t1 > 0) {\r
+ t1 *= t1;\r
+ int gi1 = perm[ii + i1\r
+ + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32;\r
+ n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1);\r
+ }\r
+ double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;\r
+ if (t2 > 0) {\r
+ t2 *= t2;\r
+ int gi2 = perm[ii + i2\r
+ + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32;\r
+ n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2);\r
+ }\r
+ double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;\r
+ if (t3 > 0) {\r
+ t3 *= t3;\r
+ int gi3 = perm[ii + i3\r
+ + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32;\r
+ n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3);\r
+ }\r
+ double t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;\r
+ if (t4 > 0) {\r
+ t4 *= t4;\r
+ int gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32;\r
+ n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4);\r
+ }\r
+ // Sum up and scale the result to cover the range [-1,1]\r
+ return 27.0 * (n0 + n1 + n2 + n3 + n4);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.math.waves;\r
+\r
+/**\r
+ * <p>\r
+ * Amplitude and frequency modulated sine wave. Uses 2 secondary waves to\r
+ * modulate the shape of the main wave.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * <strong>Note:</strong> You must NEVER call the update() method on the\r
+ * modulating waves.\r
+ * </p>\r
+ */\r
+public class AMFMSineWave extends AbstractWave {\r
+\r
+ public AbstractWave fmod;\r
+ public AbstractWave amod;\r
+\r
+ /**\r
+ * Creates a new instance from\r
+ * \r
+ * @param phase\r
+ * @param freq\r
+ * @param fmod\r
+ * @param amod\r
+ */\r
+ public AMFMSineWave(float phase, float freq, AbstractWave fmod,\r
+ AbstractWave amod) {\r
+ super(phase, freq);\r
+ this.amod = amod;\r
+ this.fmod = fmod;\r
+ }\r
+\r
+ /**\r
+ * @param phase\r
+ * @param freq\r
+ * @param offset\r
+ * @param fmod\r
+ * @param amod\r
+ */\r
+ public AMFMSineWave(float phase, float freq, float offset,\r
+ AbstractWave fmod, AbstractWave amod) {\r
+ super(phase, freq, 1, offset);\r
+ this.amod = amod;\r
+ this.fmod = fmod;\r
+ }\r
+\r
+ /**\r
+ * Progresses the wave and updates the result value. You must NEVER call the\r
+ * update() method on the 2 modulating wave since this is handled\r
+ * automatically by this method.\r
+ * \r
+ * @see toxi.math.waves.AbstractWave#update()\r
+ */\r
+ public float update() {\r
+ amp = amod.update();\r
+ value = amp * (float) Math.sin(phase) + offset;\r
+ cyclePhase(frequency + fmod.update());\r
+ return value;\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ * @param fmod\r
+ */\r
+ public void setFMod(AbstractWave fmod) {\r
+ this.fmod = fmod;\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ * @param amod\r
+ */\r
+ public void setAMod(AbstractWave amod) {\r
+ this.amod = amod;\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ */\r
+ public AbstractWave getFMod() {\r
+ return fmod;\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ */\r
+ public AbstractWave getAMod() {\r
+ return amod;\r
+ }\r
+\r
+ /**\r
+ * Resets this wave and its modulating waves as well.\r
+ * \r
+ * @see toxi.math.waves.AbstractWave#reset()\r
+ */\r
+ public void reset() {\r
+ super.reset();\r
+ fmod.reset();\r
+ amod.reset();\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.math.waves;\r
+\r
+/**\r
+ * Abstract wave oscillator type which needs to be subclassed to implement\r
+ * different waveforms. Please note that the frequency unit is radians, but\r
+ * conversion methods to & from Hertz ({@link #hertzToRadians(float, float)})\r
+ * are included in this base class.\r
+ */\r
+public abstract class AbstractWave {\r
+\r
+ public static final float PI = 3.14159265358979323846f;\r
+ public static final float TWO_PI = 2 * PI;\r
+\r
+ /**\r
+ * Current wave phase\r
+ */\r
+ public float phase;\r
+ protected float origPhase;\r
+\r
+ public float frequency;\r
+\r
+ public float amp;\r
+\r
+ public float offset;\r
+\r
+ public float value = 0;\r
+\r
+ /**\r
+ * @param phase\r
+ */\r
+ public AbstractWave(float phase) {\r
+ this(phase, 1, 1, 0);\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @param phase\r
+ * @param freq\r
+ */\r
+ public AbstractWave(float phase, float freq) {\r
+ this(phase, freq, 1, 0);\r
+ }\r
+\r
+ /**\r
+ * @param phase\r
+ * @param freq\r
+ * @param amp\r
+ * @param offset\r
+ */\r
+ public AbstractWave(float phase, float freq, float amp, float offset) {\r
+ this.phase = this.origPhase = phase;\r
+ this.frequency = freq;\r
+ this.amp = amp;\r
+ this.offset = offset;\r
+ }\r
+\r
+ /**\r
+ * Updates the wave and returns new value. Implementing classes should\r
+ * manually ensure the phase remains in the 0...TWO_PI interval or by\r
+ * calling {@link #cyclePhase()}.\r
+ * \r
+ * @return current (newly calculated) wave value\r
+ */\r
+ public abstract float update();\r
+\r
+ /**\r
+ * Ensures phase remains in the 0...TWO_PI interval.\r
+ * \r
+ * @return current phase\r
+ */\r
+ protected final float cyclePhase() {\r
+ phase %= TWO_PI;\r
+ if (phase < 0)\r
+ phase += TWO_PI;\r
+ return phase;\r
+ }\r
+\r
+ /**\r
+ * Progresses phase and ensures it remains in the 0...TWO_PI interval.\r
+ * \r
+ * @param freq\r
+ * normalized progress frequency\r
+ * @return update phase value\r
+ */\r
+ protected final float cyclePhase(float freq) {\r
+ phase = (phase + freq) % TWO_PI;\r
+ if (phase < 0)\r
+ phase += TWO_PI;\r
+ return phase;\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ * @param amp\r
+ */\r
+ // @Deprecated\r
+ public void setAmp(float amp) {\r
+ this.amp = amp;\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ */\r
+ // @Deprecated\r
+ public float getAmp() {\r
+ return amp;\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ */\r
+ // @Deprecated\r
+ public float getValue() {\r
+ return value;\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ */\r
+ // @Deprecated\r
+ public float getPhase() {\r
+ return phase;\r
+ }\r
+\r
+ /**\r
+ * Starts the wave from a new phase. The new phase position will also be\r
+ * used for any later call to {{@link #reset()}\r
+ * \r
+ * @param phase\r
+ * new phase\r
+ */\r
+ public void setPhase(float phase) {\r
+ this.phase = this.origPhase = phase;\r
+ }\r
+\r
+ /**\r
+ * Resets the wave phase to the original start value\r
+ */\r
+ public void reset() {\r
+ phase = origPhase;\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ */\r
+ // @Deprecated\r
+ public float getFrequency() {\r
+ return frequency;\r
+ }\r
+\r
+ /**\r
+ * @deprecated\r
+ * @param freq\r
+ */\r
+ // @Deprecated\r
+ public void setFrequency(float freq) {\r
+ this.frequency = freq;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see java.lang.Object#toString()\r
+ */\r
+ public String toString() {\r
+ StringBuffer sb = new StringBuffer();\r
+ sb.append(this.getClass().getName()).append(" phase: ").append(phase);\r
+ sb.append(" frequency: ").append(frequency);\r
+ sb.append(" amp: ").append(amp);\r
+ return sb.toString();\r
+ }\r
+\r
+ /**\r
+ * Converts a frequency in Hertz into radians.\r
+ * \r
+ * @param hz\r
+ * frequency to convert (in Hz)\r
+ * @param sampleRate\r
+ * sampling rate in Hz (equals period length @ 1 Hz)\r
+ * @return frequency in radians\r
+ */\r
+ public static final float hertzToRadians(float hz, float sampleRate) {\r
+ return hz / sampleRate * TWO_PI;\r
+ }\r
+\r
+ /**\r
+ * Converts a frequency from radians to Hertz.\r
+ * \r
+ * @param f\r
+ * frequency in radians\r
+ * @param sampleRate\r
+ * sampling rate in Hz (equals period length @ 1 Hz)\r
+ * @return freq in Hz\r
+ */\r
+ public static final float radiansToHertz(float f, float sampleRate) {\r
+ return f / TWO_PI * sampleRate;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package toxi.math.waves;\r
+\r
+/**\r
+ * Implements a constant value as waveform.\r
+ * @author toxi\r
+ */\r
+public class ConstantWave extends AbstractWave {\r
+\r
+ public ConstantWave(float value) {\r
+ super(0);\r
+ this.value = value;\r
+ }\r
+\r
+ public final float update() {\r
+ return value;\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.math.waves;\r
+\r
+/**\r
+ * <p>\r
+ * Frequency modulated <strong>bandwidth-limited</strong> square wave using a\r
+ * fourier series of harmonics. Also uses a secondary wave to modulate the\r
+ * frequency of the main wave.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * <strong>Note:</strong> You must NEVER call the update() method on the\r
+ * modulating wave.\r
+ * </p>\r
+ */\r
+public class FMHarmonicSquareWave extends AbstractWave {\r
+\r
+ public AbstractWave fmod;\r
+\r
+ /**\r
+ * Maximum harmonics to add (make sure you stay under Nyquist freq), default\r
+ * = 9\r
+ */\r
+ public int maxHarmonics = 3;\r
+\r
+ /**\r
+ * Convenience constructor to create a non frequency modulated square wave\r
+ * \r
+ * @param phase\r
+ * @param freq\r
+ * base frequency (in radians)\r
+ * @param amp\r
+ * @param offset\r
+ */\r
+ public FMHarmonicSquareWave(float phase, float freq, float amp,\r
+ float offset) {\r
+ this(phase, freq, amp, offset, new ConstantWave(0));\r
+ }\r
+\r
+ public FMHarmonicSquareWave(float phase, float freq,\r
+ AbstractWave fmod) {\r
+ super(phase, freq);\r
+ this.fmod = fmod;\r
+ }\r
+\r
+ public FMHarmonicSquareWave(float phase, float freq, float amp,\r
+ float offset, AbstractWave fmod) {\r
+ super(phase, freq, amp, offset);\r
+ this.fmod = fmod;\r
+ }\r
+\r
+ /**\r
+ * Progresses the wave and updates the result value. You must NEVER call the\r
+ * update() method on the modulating wave since this is handled\r
+ * automatically by this method.\r
+ * \r
+ * @see toxi.math.waves.AbstractWave#update()\r
+ */\r
+ public float update() {\r
+ value = 0;\r
+ for (int i = 1; i <= maxHarmonics; i += 2) {\r
+ value += 1.0 / i * (float) Math.sin(i * phase);\r
+ }\r
+ value = value * amp + offset;\r
+ cyclePhase(frequency + fmod.update());\r
+ return value;\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.math.waves;\r
+\r
+/**\r
+ * <p>\r
+ * Frequency modulated bandwidth unlimited pure sawtooth wave. Uses a secondary\r
+ * wave to modulate the frequency of the main wave.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * <strong>Note:</strong> You must NEVER call the update() method on the\r
+ * modulating wave.\r
+ * </p>\r
+ */\r
+public class FMSawtoothWave extends AbstractWave {\r
+\r
+ public AbstractWave fmod;\r
+\r
+ /**\r
+ * Convenience constructor to create a non frequency modulated sawtooth.\r
+ * \r
+ * @param phase\r
+ * @param freq\r
+ * base frequency (in radians)\r
+ * @param amp\r
+ * @param offset\r
+ */\r
+ public FMSawtoothWave(float phase, float freq, float amp, float offset) {\r
+ this(phase, freq, amp, offset, new ConstantWave(0));\r
+ }\r
+\r
+ public FMSawtoothWave(float phase, float freq, AbstractWave fmod) {\r
+ super(phase, freq);\r
+ this.fmod = fmod;\r
+ }\r
+\r
+ public FMSawtoothWave(float phase, float freq, float amp, float offset,\r
+ AbstractWave fmod) {\r
+ super(phase, freq, amp, offset);\r
+ this.fmod = fmod;\r
+ }\r
+\r
+ /**\r
+ * Progresses the wave and updates the result value. You must NEVER call the\r
+ * update() method on the modulating wave since this is handled\r
+ * automatically by this method.\r
+ * \r
+ * @see toxi.math.waves.AbstractWave#update()\r
+ */\r
+ public float update() {\r
+ value = ((phase / TWO_PI) * 2 - 1) * amp + offset;\r
+ cyclePhase(frequency + fmod.update());\r
+ return value;\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.math.waves;\r
+\r
+/**\r
+ * <p>\r
+ * Frequency modulated sine wave. Uses a secondary wave to modulate the\r
+ * frequency of the main wave.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * <strong>Note:</strong> You must NEVER call the update() method on the\r
+ * modulating wave.\r
+ * </p>\r
+ */\r
+public class FMSineWave extends AbstractWave {\r
+\r
+ public AbstractWave fmod;\r
+\r
+ public FMSineWave(float phase, float freq, float amp, float offset) {\r
+ this(phase, freq, amp, offset, new ConstantWave(0));\r
+ }\r
+\r
+ public FMSineWave(float phase, float freq, AbstractWave fmod) {\r
+ super(phase, freq);\r
+ this.fmod = fmod;\r
+ }\r
+\r
+ public FMSineWave(float phase, float freq, float amp, float offset,\r
+ AbstractWave fmod) {\r
+ super(phase, freq, amp, offset);\r
+ this.fmod = fmod;\r
+ }\r
+\r
+ /**\r
+ * Progresses the wave and updates the result value. You must NEVER call the\r
+ * update() method on the modulating wave since this is handled\r
+ * automatically by this method.\r
+ * \r
+ * @see toxi.math.waves.AbstractWave#update()\r
+ */\r
+ public float update() {\r
+ value = (float) (Math.sin(phase) * amp) + offset;\r
+ cyclePhase(frequency + fmod.update());\r
+ return value;\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+package toxi.math.waves;\r
+\r
+/**\r
+ * <p>\r
+ * Frequency modulated bandwidth unlimited pure digital square wave. Uses a\r
+ * secondary wave to modulate the frequency of the main wave.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * <strong>Note:</strong> You must NEVER call the update() method on the\r
+ * modulating wave.\r
+ * </p>\r
+ */\r
+public class FMSquareWave extends AbstractWave {\r
+\r
+ public AbstractWave fmod;\r
+\r
+ /**\r
+ * Convenience constructor to create a non frequency modulated square wave\r
+ * \r
+ * @param phase\r
+ * @param freq\r
+ * base frequency (in radians)\r
+ * @param amp\r
+ * @param offset\r
+ */\r
+ public FMSquareWave(float phase, float freq, float amp, float offset) {\r
+ this(phase, freq, amp, offset, new ConstantWave(0));\r
+ }\r
+\r
+ public FMSquareWave(float phase, float freq, AbstractWave fmod) {\r
+ super(phase, freq);\r
+ this.fmod = fmod;\r
+ }\r
+\r
+ public FMSquareWave(float phase, float freq, float amp, float offset,\r
+ AbstractWave fmod) {\r
+ super(phase, freq, amp, offset);\r
+ this.fmod = fmod;\r
+ }\r
+\r
+ /**\r
+ * Progresses the wave and updates the result value. You must NEVER call the\r
+ * update() method on the modulating wave since this is handled\r
+ * automatically by this method.\r
+ * \r
+ * @see toxi.math.waves.AbstractWave#update()\r
+ */\r
+ public float update() {\r
+ value = (phase / TWO_PI < 0.5 ? 1 : -1) * amp + offset;\r
+ cyclePhase(frequency + fmod.update());\r
+ return value;\r
+ }\r
+}\r
--- /dev/null
+/* \r
+ * Copyright (c) 2006, 2007 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.math.waves;\r
+\r
+/**\r
+ * Standard Sine wave at fixed frequency and values normalized to the given\r
+ * amplitude.\r
+ */\r
+public class SineWave extends AbstractWave {\r
+\r
+ /**\r
+ * @param phase\r
+ * starting phase\r
+ * @param freq\r
+ * in radians (not Hertz)\r
+ */\r
+ public SineWave(float phase, float freq) {\r
+ super(phase, freq);\r
+ }\r
+\r
+ /**\r
+ * @param phase\r
+ * starting phase\r
+ * @param freq\r
+ * in radians (not Hertz)\r
+ * @param amp\r
+ * amplitude factor\r
+ * @param offset\r
+ * centre oscillation value\r
+ */\r
+ public SineWave(float phase, float freq, float amp, float offset) {\r
+ super(phase, freq, amp, offset);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see toxi.math.waves.AbstractWave#update()\r
+ */\r
+ public float update() {\r
+ value = (float) (Math.sin(phase) * amp) + offset;\r
+ cyclePhase(frequency);\r
+ return value;\r
+ }\r
+}\r
--- /dev/null
+package toxi.util.datatypes;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Random;\r
+\r
+/**\r
+ * A collection of array utilities.\r
+ * \r
+ * @author toxi\r
+ */\r
+public class ArrayUtil {\r
+\r
+ /**\r
+ * Rearranges the array items in random order using the default\r
+ * java.util.Random generator. Operation is in-place, no copy is created.\r
+ * \r
+ * @param array\r
+ */\r
+ public static <T> void shuffle(T[] array) {\r
+ shuffle(array, new Random());\r
+ }\r
+\r
+ /**\r
+ * Rearranges the array items in random order using the given RNG. Operation\r
+ * is in-place, no copy is created.\r
+ * \r
+ * @param array\r
+ * @param rnd\r
+ */\r
+ public static <T> void shuffle(T[] array, Random rnd) {\r
+ int N = array.length;\r
+ for (int i = 0; i < N; i++) {\r
+ int r = i + (int) (rnd.nextFloat() * (N - i)); // between i and N-1\r
+ T swap = array[i];\r
+ array[i] = array[r];\r
+ array[r] = swap;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Reverses the item order of the supplied array (generic types).\r
+ * \r
+ * @param array\r
+ */\r
+ public static <T> void reverse(T[] array) {\r
+ int len = array.length - 1;\r
+ int len2 = array.length / 2;\r
+ for (int i = 0; i < len2; i++) {\r
+ T tmp = array[i];\r
+ array[i] = array[len - i];\r
+ array[len - i] = tmp;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Reverses the item order of the supplied byte array.\r
+ * \r
+ * @param array\r
+ */\r
+ public static void reverse(byte[] array) {\r
+ int len = array.length - 1;\r
+ int len2 = array.length / 2;\r
+ for (int i = 0; i < len2; i++) {\r
+ byte tmp = array[i];\r
+ array[i] = array[len - i];\r
+ array[len - i] = tmp;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Reverses the item order of the supplied short array.\r
+ * \r
+ * @param array\r
+ */\r
+ public static void reverse(short[] array) {\r
+ int len = array.length - 1;\r
+ int len2 = array.length / 2;\r
+ for (int i = 0; i < len2; i++) {\r
+ short tmp = array[i];\r
+ array[i] = array[len - i];\r
+ array[len - i] = tmp;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Reverses the item order of the supplied char array.\r
+ * \r
+ * @param array\r
+ */\r
+ public static void reverse(char[] array) {\r
+ int len = array.length - 1;\r
+ int len2 = array.length / 2;\r
+ for (int i = 0; i < len2; i++) {\r
+ char tmp = array[i];\r
+ array[i] = array[len - i];\r
+ array[len - i] = tmp;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Reverses the item order of the supplied int array.\r
+ * \r
+ * @param array\r
+ */\r
+ public static void reverse(int[] array) {\r
+ int len = array.length - 1;\r
+ int len2 = array.length / 2;\r
+ for (int i = 0; i < len2; i++) {\r
+ int tmp = array[i];\r
+ array[i] = array[len - i];\r
+ array[len - i] = tmp;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Reverses the item order of the supplied float array.\r
+ * \r
+ * @param array\r
+ */\r
+ public static void reverse(float[] array) {\r
+ int len = array.length - 1;\r
+ int len2 = array.length / 2;\r
+ for (int i = 0; i < len2; i++) {\r
+ float tmp = array[i];\r
+ array[i] = array[len - i];\r
+ array[len - i] = tmp;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Converts the generic array into an {@link ArrayList} of the same type.\r
+ * \r
+ * @param array\r
+ * @return array list version\r
+ */\r
+ public static <T> ArrayList<T> arrayToList(T[] array) {\r
+ ArrayList<T> list = new ArrayList<T>(array.length);\r
+ for (int i = 0; i < array.length; i++) {\r
+ list.add(array[i]);\r
+ }\r
+ return list;\r
+ }\r
+\r
+ /**\r
+ * Adds all array elements to the given collection of the same type.\r
+ * \r
+ * @param <T>\r
+ * @param array\r
+ * array\r
+ * @param collection\r
+ * existing collection or null (to create a new {@link ArrayList}\r
+ * automatically)\r
+ */\r
+ static <T> void addArrayToCollection(T[] array, Collection<T> collection) {\r
+ if (collection == null) {\r
+ collection = new ArrayList<T>();\r
+ }\r
+ for (T o : array) {\r
+ collection.add(o);\r
+ }\r
+ }\r
+\r
+}\r
--- /dev/null
+package toxi.util.datatypes;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+public class BiasedFloatRange extends FloatRange {\r
+\r
+ public float bias;\r
+ public float standardDeviation;\r
+\r
+ /**\r
+ * @param min\r
+ * min value (inclusive)\r
+ * @param max\r
+ * max value (inclusive)\r
+ * @param bias\r
+ * bias value (can be outside the min/max range, but values will\r
+ * be clipped)\r
+ * @param sd\r
+ * standard deviation (if bias at range mean sd=1.0, the entire\r
+ * range will be covered)\r
+ */\r
+ public BiasedFloatRange(float min, float max, float bias, float sd) {\r
+ super(min, max);\r
+ this.bias = bias;\r
+ this.standardDeviation = sd * 0.5f;\r
+ }\r
+\r
+ @Override\r
+ public float pickRandom() {\r
+ current = (float) (random.nextGaussian() * standardDeviation * (max - min))\r
+ + bias;\r
+ current = MathUtils.clip(current, min, max);\r
+ return current;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "BiasedFloatRange: " + min + " -> " + max + " bias: " + bias\r
+ + " q: " + standardDeviation;\r
+ }\r
+}\r
--- /dev/null
+package toxi.util.datatypes;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+public class BiasedIntegerRange extends IntegerRange {\r
+\r
+ public int bias;\r
+ public float standardDeviation;\r
+\r
+ /**\r
+ * @param min\r
+ * min value (inclusive)\r
+ * @param max\r
+ * max value (inclusive)\r
+ * @param bias\r
+ * bias value (can be outside the min/max range, but values will\r
+ * be clipped)\r
+ * @param sd\r
+ * standard deviation (if bias at range mean sd=1.0, the entire\r
+ * range will be covered)\r
+ */\r
+ public BiasedIntegerRange(int min, int max, int bias, float sd) {\r
+ super(min, max);\r
+ this.bias = bias;\r
+ this.standardDeviation = sd * 0.5f;\r
+ }\r
+\r
+ @Override\r
+ public int pickRandom() {\r
+ current = (int) (random.nextGaussian() * standardDeviation * (max - min))\r
+ + bias;\r
+ current = MathUtils.clip(current, min, max);\r
+ return current;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "BiasedIntegerRange: " + min + " -> " + max + " bias: " + bias\r
+ + " q: " + standardDeviation;\r
+ }\r
+}\r
--- /dev/null
+package toxi.util.datatypes;\r
+\r
+import java.util.Random;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+public class FloatRange {\r
+\r
+ public float min, max;\r
+ public float current;\r
+\r
+ protected Random random = new Random();\r
+\r
+ public FloatRange(float min, float max) {\r
+ this.min = min;\r
+ this.max = max;\r
+ }\r
+\r
+ public void setRandom(Random rnd) {\r
+ random = rnd;\r
+ }\r
+\r
+ public float pickRandom() {\r
+ current = MathUtils.random(random, min, max);\r
+ return current;\r
+ }\r
+\r
+ public float getCurrent() {\r
+ return current;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "FloatRange: " + min + " -> " + max;\r
+ }\r
+\r
+ public FloatRange copy() {\r
+ FloatRange range = new FloatRange(min, max);\r
+ range.current = current;\r
+ range.random = random;\r
+ return range;\r
+ }\r
+\r
+ public boolean isValueInRange(float val) {\r
+ return val >= min && val <= max;\r
+ }\r
+}\r
--- /dev/null
+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<T> implements Iterable<T> {
+
+ protected ArrayList<T> items;
+ protected int currID = -1;
+ protected T current;
+
+ protected Random random = new Random();
+
+ public GenericSet(Collection<T> items) {
+ this.items = new ArrayList<T>(items);
+ pickRandom();
+ }
+
+ public GenericSet(T obj) {
+ items = new ArrayList<T>();
+ items.add(obj);
+ current = obj;
+ }
+
+ public void add(T obj) {
+ items.add(obj);
+ }
+
+ public void addAll(Collection<T> coll) {
+ items.addAll(coll);
+ }
+
+ public void clear() {
+ items.clear();
+ }
+
+ public boolean contains(T obj) {
+ return items.contains(obj);
+ }
+
+ public GenericSet<T> copy() {
+ GenericSet<T> set = new GenericSet<T>(items);
+ set.current = current;
+ set.currID = currID;
+ set.random = random;
+ return set;
+ }
+
+ public T getCurrent() {
+ return current;
+ }
+
+ public ArrayList<T> getItems() {
+ return items;
+ }
+
+ public Iterator<T> 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;
+ }
+}
--- /dev/null
+package toxi.util.datatypes;\r
+\r
+import java.util.Random;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+public class IntegerRange {\r
+\r
+ public int min, max;\r
+ public int current;\r
+\r
+ protected Random random = new Random();\r
+\r
+ public IntegerRange(int min, int max) {\r
+ this.min = min;\r
+ this.max = max;\r
+ }\r
+\r
+ public void setRandom(Random rnd) {\r
+ random = rnd;\r
+ }\r
+\r
+ public int pickRandom() {\r
+ current = MathUtils.random(min, max);\r
+ return current;\r
+ }\r
+\r
+ public int getCurrent() {\r
+ return current;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "IntegerRange: " + min + " -> " + max;\r
+ }\r
+\r
+ public boolean isValueInRange(int val) {\r
+ return val >= min && val < max;\r
+ }\r
+}\r
--- /dev/null
+package toxi.util.datatypes;\r
+\r
+import java.util.Random;\r
+\r
+import toxi.math.MathUtils;\r
+\r
+public class IntegerSet {\r
+\r
+ public int[] items;\r
+ public int currID = -1;\r
+ public int current;\r
+\r
+ private Random random = new Random();\r
+\r
+ public IntegerSet(int[] items) {\r
+ this.items = items;\r
+ pickRandom();\r
+ }\r
+\r
+ public void setRandom(Random rnd) {\r
+ random = rnd;\r
+ }\r
+\r
+ public int pickRandom() {\r
+ currID = MathUtils.random(random, items.length);\r
+ current = items[currID];\r
+ return current;\r
+ }\r
+\r
+ public int pickRandomUnique() {\r
+ if (items.length > 1) {\r
+ int newID = currID;\r
+ while (newID == currID) {\r
+ newID = MathUtils.random(random, items.length);\r
+ }\r
+ currID = newID;\r
+ } else {\r
+ currID = 0;\r
+ }\r
+ current = items[currID];\r
+ return current;\r
+ }\r
+\r
+ public int getCurrent() {\r
+ return current;\r
+ }\r
+\r
+ public boolean contains(int value) {\r
+ for (int i = 0; i < items.length; i++) {\r
+ if (items[i] == value)\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+}\r
--- /dev/null
+package toxi.util.datatypes;\r
+\r
+import java.util.HashMap;\r
+import java.util.logging.Logger;\r
+\r
+/**\r
+ * Implements a registry for dynamic singleton management. Use this registry\r
+ * instead of using "new" to enforce singletons of any class with a visible\r
+ * default constructor. The registry itself is implemented as singleton.\r
+ */\r
+public class SingletonRegistry {\r
+\r
+ /**\r
+ * The singleton instance of the registry itself.\r
+ */\r
+ public static SingletonRegistry REGISTRY = new SingletonRegistry();\r
+\r
+ private static HashMap<String, Object> map = new HashMap<String, Object>();\r
+\r
+ private static Logger logger = Logger.getLogger(SingletonRegistry.class\r
+ .getName());\r
+\r
+ protected SingletonRegistry() {\r
+ }\r
+\r
+ /**\r
+ * Alternative, more conventional accessor to the singleton instance of the\r
+ * registry itself.\r
+ * \r
+ * @return registry instance\r
+ */\r
+ public static SingletonRegistry getRegistry() {\r
+ return REGISTRY;\r
+ }\r
+\r
+ /**\r
+ * Creates or returns an instance of the class requested by name.\r
+ * \r
+ * @param className\r
+ * @return class singleton instance\r
+ */\r
+ public static synchronized Object getInstanceOf(String className) {\r
+ Object instance = map.get(className);\r
+ if (instance != null) {\r
+ return instance;\r
+ }\r
+ try {\r
+ instance = Class.forName(className).newInstance();\r
+ logger.info("Created singleton: " + instance);\r
+ } catch (ClassNotFoundException cnf) {\r
+ logger.severe("Couldn't find class: " + className);\r
+ } catch (InstantiationException ie) {\r
+ logger.severe("Couldn't instantiate the class: " + className);\r
+ } catch (IllegalAccessException ia) {\r
+ logger.severe("Couldn't access class: " + className);\r
+ }\r
+ map.put(className, instance);\r
+ return instance;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright (c) 2006-2008 Karsten Schmidt\r
+ * \r
+ * This library is free software; you can redistribute it and/or modify it under\r
+ * the terms of the GNU Lesser General Public License as published by the Free\r
+ * Software Foundation; either version 2.1 of the License, or (at your option)\r
+ * any later version.\r
+ * \r
+ * http://creativecommons.org/licenses/LGPL/2.1/\r
+ * \r
+ * This library is distributed in the hope that it will be useful, but WITHOUT\r
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\r
+ * details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public License\r
+ * along with this library; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+ */\r
+\r
+package toxi.util.datatypes;\r
+\r
+import java.io.FileInputStream;\r
+import java.util.Properties;\r
+import java.util.StringTokenizer;\r
+import java.util.logging.Logger;\r
+\r
+/**\r
+ * Convenience wrapper providing typed access to Java {@link Properties} files.\r
+ * \r
+ * @author toxi\r
+ * \r
+ */\r
+\r
+@SuppressWarnings("serial")\r
+public class TypedProperties extends Properties {\r
+\r
+ public static final String DELIM = "\t\n\r\f\u00A0,";\r
+\r
+ private static final Logger logger = Logger.getLogger(TypedProperties.class\r
+ .getName());\r
+\r
+ /**\r
+ * Attempts to load properties from the specified (absolute) file path (In\r
+ * Processing use sketchPath() or dataPath() to build absolute path).\r
+ * \r
+ * @param path\r
+ * config file\r
+ * @return true, if successful.\r
+ */\r
+ public boolean load(String path) {\r
+ try {\r
+ load(new FileInputStream(path));\r
+ return true;\r
+ } catch (Exception e) {\r
+ logger.warning("error opening config file: " + path);\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Returns a property as boolean.\r
+ * \r
+ * @param id\r
+ * property name\r
+ * @param defaultState\r
+ * @return prop value\r
+ */\r
+ public boolean getBoolean(String id, boolean defaultState) {\r
+ return Boolean.parseBoolean(getProperty(id, "" + defaultState));\r
+ }\r
+\r
+ /**\r
+ * Returns a property as integer.\r
+ * \r
+ * @param id\r
+ * property name\r
+ * @param defaultValue\r
+ * @return prop value\r
+ */\r
+ public int getInt(String id, int defaultValue) {\r
+ return Integer.parseInt(getProperty(id, "" + defaultValue));\r
+ }\r
+\r
+ /**\r
+ * Returns a hexadecimal property as integer\r
+ * \r
+ * @param id\r
+ * prop name\r
+ * @param defaultValue\r
+ * @return prop value\r
+ */\r
+ public int getHexInt(String id, int defaultValue) {\r
+ return Integer.parseInt(getProperty(id, Integer\r
+ .toHexString(defaultValue)), 16);\r
+ }\r
+\r
+ /**\r
+ * Returns a property as float.\r
+ * \r
+ * @param id\r
+ * @param defaultValue\r
+ * @return prop value\r
+ */\r
+ public float getFloat(String id, float defaultValue) {\r
+ return Float.parseFloat(getProperty(id, "" + defaultValue));\r
+ }\r
+\r
+ /**\r
+ * Shorthand wrapper for {{@link #getIntArray(String, int[])} automatically\r
+ * supplying an empty int[] array as default value.\r
+ * \r
+ * @param id\r
+ * @return prop values as array\r
+ */\r
+ public int[] getIntArray(String id) {\r
+ return getIntArray(id, new int[0]);\r
+ }\r
+\r
+ /**\r
+ * Returns a comma delimited property value as int[] array. Non-integer\r
+ * items will be ignored.\r
+ * \r
+ * @param id\r
+ * prop name\r
+ * @return prop items as array\r
+ */\r
+ public int[] getIntArray(String id, int[] defaultArray) {\r
+ StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""),\r
+ DELIM);\r
+ int pieces[] = new int[tokenizer.countTokens()];\r
+ int index = 0;\r
+ while (tokenizer.hasMoreTokens()) {\r
+ try {\r
+ pieces[index] = Integer.parseInt(tokenizer.nextToken());\r
+ index++;\r
+ } catch (NumberFormatException e) {\r
+ // ignore non-integer items\r
+ }\r
+ }\r
+ if (index > 0) {\r
+ int[] result = new int[index];\r
+ System.arraycopy(pieces, 0, result, 0, index);\r
+ return result;\r
+ } else\r
+ return defaultArray;\r
+ }\r
+\r
+ /**\r
+ * Shorthand wrapper for {@link #getFloatArray(String, float[])}\r
+ * automatically supplying an empty float[] array as default value.\r
+ * \r
+ * @param id\r
+ * @return prop values as array\r
+ */\r
+ public float[] getFloatArray(String id) {\r
+ return getFloatArray(id, new float[0]);\r
+ }\r
+\r
+ /**\r
+ * Returns a comma delimited property value as float[] array.\r
+ * \r
+ * @param id\r
+ * prop name\r
+ * @return prop items as array\r
+ */\r
+ public float[] getFloatArray(String id, float[] defaultArray) {\r
+ StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""),\r
+ DELIM);\r
+ float pieces[] = new float[tokenizer.countTokens()];\r
+ int index = 0;\r
+ while (tokenizer.hasMoreTokens()) {\r
+ try {\r
+ pieces[index] = Float.parseFloat(tokenizer.nextToken());\r
+ index++;\r
+ } catch (NumberFormatException e) {\r
+ // ignore NaN items\r
+ }\r
+ }\r
+ if (index > 0) {\r
+ float[] result = new float[index];\r
+ System.arraycopy(pieces, 0, result, 0, index);\r
+ return result;\r
+ } else\r
+ return defaultArray;\r
+ }\r
+\r
+ /**\r
+ * Shorthand wrapper for {@link #getByteArray(String, byte[])} automatically\r
+ * supplying an empty byte[] as default value.\r
+ * \r
+ * @param id\r
+ * @return prop values as array\r
+ */\r
+ public byte[] getByteArray(String id) {\r
+ return getByteArray(id, new byte[0]);\r
+ }\r
+\r
+ /**\r
+ * Returns a comma delimited property value as byte[] array. Non-byte values\r
+ * will be ignored.\r
+ * \r
+ * @param id\r
+ * prop name\r
+ * @return prop values as array\r
+ */\r
+ public byte[] getByteArray(String id, byte[] defaultArray) {\r
+ StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""),\r
+ DELIM);\r
+ byte[] pieces = new byte[tokenizer.countTokens()];\r
+ int index = 0;\r
+ while (tokenizer.hasMoreTokens()) {\r
+ try {\r
+ pieces[index] = Byte.parseByte(tokenizer.nextToken());\r
+ index++;\r
+ } catch (NumberFormatException e) {\r
+ // ignore non-integer items\r
+ }\r
+ }\r
+ if (index > 0) {\r
+ byte[] result = new byte[index];\r
+ System.arraycopy(pieces, 0, result, 0, index);\r
+ return result;\r
+ } else\r
+ return defaultArray;\r
+ }\r
+\r
+ public String[] getStringArray(String id) {\r
+ return getStringArray(id, new String[0]);\r
+ }\r
+\r
+ public String[] getStringArray(String id, String[] defaultArray) {\r
+ StringTokenizer tokenizer = new StringTokenizer(getProperty(id, ""),\r
+ DELIM);\r
+ int index = 0;\r
+ String[] pieces = null;\r
+ while (tokenizer.hasMoreTokens()) {\r
+ if (pieces == null) {\r
+ pieces = new String[tokenizer.countTokens()];\r
+ }\r
+ String token = tokenizer.nextToken();\r
+ if (token.length() > 0) {\r
+ pieces[index++] = token;\r
+ }\r
+ }\r
+ if (index > 0) {\r
+ String[] result = new String[index];\r
+ System.arraycopy(pieces, 0, result, 0, index);\r
+ return result;\r
+ } else\r
+ return defaultArray;\r
+ }\r
+}
\ No newline at end of file