diff --git a/.gitignore b/.gitignore index ba68b4401..896cbbf5f 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,9 @@ /core/resources-src/pix/sormus.xcf.gz /core/resources-src/pix/splashscreen-sormus.png /core/resources-src/pix/splashscreen-sormus.xcf.gz +<<<<<<< HEAD /*/bin/ /android-libraries/*/bin/ +======= +>>>>>>> Convert svn:ignore properties to .gitignore. diff --git a/core/.classpath b/core/.classpath index c4cc74f89..ec1d3df2c 100644 --- a/core/.classpath +++ b/core/.classpath @@ -10,12 +10,6 @@ - - - - - - @@ -26,12 +20,16 @@ - - + + + + + + diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 000000000..5e56e040e --- /dev/null +++ b/core/.gitignore @@ -0,0 +1 @@ +/bin diff --git a/core/.settings/org.eclipse.jdt.core.prefs b/core/.settings/org.eclipse.jdt.core.prefs index e536d4d53..b2bf28c08 100644 --- a/core/.settings/org.eclipse.jdt.core.prefs +++ b/core/.settings/org.eclipse.jdt.core.prefs @@ -71,3 +71,283 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +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=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +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.alignment_for_union_type_in_multicatch=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=0 +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_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=false +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=false +org.eclipse.jdt.core.formatter.comment.format_line_comments=false +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=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=200 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +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.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +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=true +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=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not 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_try=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_semicolon_in_try_resources=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_try=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_try=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_semicolon_in_try_resources=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.join_lines_in_comments=false +org.eclipse.jdt.core.formatter.join_wrapped_lines=false +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=200 +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=5 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/core/.settings/org.eclipse.jdt.ui.prefs b/core/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..f110f8e55 --- /dev/null +++ b/core/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,56 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_OpenRocket style +formatter_settings_version=12 +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.make_local_variable_final=false +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=true +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=false +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_trailing_whitespaces=false +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/core/ChangeLog b/core/ChangeLog index d7cd95268..991d8f932 100644 --- a/core/ChangeLog +++ b/core/ChangeLog @@ -1,3 +1,36 @@ + +2013-04-17 Bill Kuker and Kevin Ruland + * Added support for decals on rockets. Added two new 3d views - 3d finished and 3d unfinshed. Added support to export and + apply decals (images) to rocket components. Added ability to launch external graphics editor to edit decals and monitor + file for writes to update the rocket view. + +2013-04-17 Kevin Ruland + * Added simulation of tumble recovery based on experimentation done by Sampo Niskane. This is particularly useful + for low power staged flights. + +2013-04-17 Kevin Ruland + * Extended "motor configuration" concept to cover more properties. The concept was renamed "Flight Configuration" and + allows the user to override stage separation, recovery deployment, and motor ignition per flight configuration. + These configurations are stored in the ork file with each component. If no override is specified then the user specified + default is used (set in the component edit dialog). The flight configuration dialog was reworked to make it more + usable. The user selects the configuration in a drop down with buttons for "add", "delete" and "rename". After selecting + the configuration, the tabbed area below allows the user to change the configuration for this simulation. + +2013-04-17 Kevin Ruland + * Allow simulation of stages without a motor. Users in the past have attempted to simulate separate recovery of payload + and booster by tricking OpenRocket using a dummy motor with trivial thrust curve. Now the user does not need to do this. + There is an example of this provided in TARC Payloader.ork and Boosted Dart.ork + +2013-04-17 Kevin Ruland + * Simulate recovery of boosters. The simulation engine will create new FlightDataBranches for each stage after + separation. The data for the boosters begins at t=separation. The simulation plot allows the user to select + which stage's data to show (or all). + +2013-04-17 Kevin Ruland + * Modified the zoom and pan controls in the simulation plot. Added zoom in, out, and reset buttons. Added + scroll with mouse wheel. If the alt key is used with either of these, only the domain is zoomed. Richard + contributed a more logical mouse controlled zoom - right click and drag will zoom (either domain, range or both). + 2012-09-28 Sampo Niskane * Released version 12.09.1 diff --git a/core/LICENSE.TXT b/core/LICENSE.TXT index ced6952bc..31fc84333 100644 --- a/core/LICENSE.TXT +++ b/core/LICENSE.TXT @@ -1,6 +1,6 @@ OpenRocket - A model rocket simulator -Copyright (C) 2007-2012 Sampo Niskanen and others +Copyright (C) 2007-2013 Sampo Niskanen and others This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/core/README.TXT b/core/README.TXT index 1498a5964..073eb9fea 100644 --- a/core/README.TXT +++ b/core/README.TXT @@ -2,7 +2,7 @@ OpenRocket - an Open Source model rocket simulator -------------------------------------------------- -Copyright (C) 2007-2012 Sampo Niskanen and others +Copyright (C) 2007-2013 Sampo Niskanen and others For license information see the file LICENSE.TXT. diff --git a/core/build.xml b/core/build.xml index ba9e25a8c..35e1f66ce 100644 --- a/core/build.xml +++ b/core/build.xml @@ -70,7 +70,7 @@ Compiling main classes - + @@ -80,7 +80,7 @@ - + @@ -96,11 +96,12 @@ - - + + + @@ -217,7 +218,7 @@ - + Checking project for FIXMEs. @@ -243,7 +244,7 @@ ${criticaltodos} - + Checking project for non-ASCII characters. diff --git a/core/doc/properties.txt b/core/doc/properties.txt index c9a44cb40..e4a5827f5 100644 --- a/core/doc/properties.txt +++ b/core/doc/properties.txt @@ -12,6 +12,13 @@ openrocket.locale openrocket.3d.disable Disables all OpenGL calls if set. +openrocket.plugins + List of extra plugin JARs to load. By default the directory Plugins/ under the + application directory is scanned and all .JAR files are used. Multiple plugins + are separated using the platform-specific path separator (; or :). + + + Logging options --------------- @@ -30,7 +37,6 @@ openrocket.log.tracelevel - Debugging options ----------------- diff --git a/core/fileformat.txt b/core/fileformat.txt index 616b540dd..311c0dbfa 100644 --- a/core/fileformat.txt +++ b/core/fileformat.txt @@ -43,3 +43,7 @@ The following file format versions exist: Added lowerstageseparation as recovery device deployment event. Added section for supporting datatypes other than internal ones. Currently only supports datatypes from custom expressions. + +1.6: Introduced with OpenRocket 13.04. Added component Appearances (decals & paint) + Added configurable parameters to recovery devices, motor ignition and separation. + diff --git a/core/lib-test/test-plugin.jar b/core/lib-test/test-plugin.jar new file mode 100644 index 000000000..1a6f40aaf Binary files /dev/null and b/core/lib-test/test-plugin.jar differ diff --git a/core/lib/annotation-detector-3.0.2-SNAPSHOT.jar b/core/lib/annotation-detector-3.0.2-SNAPSHOT.jar new file mode 100644 index 000000000..bab8dc3e1 Binary files /dev/null and b/core/lib/annotation-detector-3.0.2-SNAPSHOT.jar differ diff --git a/core/lib/jcommon-1.0.16.jar b/core/lib/jcommon-1.0.17.jar similarity index 83% rename from core/lib/jcommon-1.0.16.jar rename to core/lib/jcommon-1.0.17.jar index 4cd680744..f1bd165a9 100644 Binary files a/core/lib/jcommon-1.0.16.jar and b/core/lib/jcommon-1.0.17.jar differ diff --git a/core/lib/jfreechart-1.0.13.jar b/core/lib/jfreechart-1.0.14.jar similarity index 54% rename from core/lib/jfreechart-1.0.13.jar rename to core/lib/jfreechart-1.0.14.jar index 83c699318..0e4d020ad 100644 Binary files a/core/lib/jfreechart-1.0.13.jar and b/core/lib/jfreechart-1.0.14.jar differ diff --git a/core/lib/jogl/gluegen-rt-natives-linux-amd64.jar b/core/lib/jogl/gluegen-rt-natives-linux-amd64.jar index ee7cf4be9..851477ae9 100644 Binary files a/core/lib/jogl/gluegen-rt-natives-linux-amd64.jar and b/core/lib/jogl/gluegen-rt-natives-linux-amd64.jar differ diff --git a/core/lib/jogl/gluegen-rt-natives-linux-i586.jar b/core/lib/jogl/gluegen-rt-natives-linux-i586.jar index a7a700783..d45803dcd 100644 Binary files a/core/lib/jogl/gluegen-rt-natives-linux-i586.jar and b/core/lib/jogl/gluegen-rt-natives-linux-i586.jar differ diff --git a/core/lib/jogl/gluegen-rt-natives-macosx-universal.jar b/core/lib/jogl/gluegen-rt-natives-macosx-universal.jar index 72d86d37d..de6a15970 100644 Binary files a/core/lib/jogl/gluegen-rt-natives-macosx-universal.jar and b/core/lib/jogl/gluegen-rt-natives-macosx-universal.jar differ diff --git a/core/lib/jogl/gluegen-rt-natives-windows-amd64.jar b/core/lib/jogl/gluegen-rt-natives-windows-amd64.jar index 19f62cb93..aeb51ecd7 100644 Binary files a/core/lib/jogl/gluegen-rt-natives-windows-amd64.jar and b/core/lib/jogl/gluegen-rt-natives-windows-amd64.jar differ diff --git a/core/lib/jogl/gluegen-rt-natives-windows-i586.jar b/core/lib/jogl/gluegen-rt-natives-windows-i586.jar index 713ce14c1..ffaedb49c 100644 Binary files a/core/lib/jogl/gluegen-rt-natives-windows-i586.jar and b/core/lib/jogl/gluegen-rt-natives-windows-i586.jar differ diff --git a/core/lib/jogl/gluegen-rt.jar b/core/lib/jogl/gluegen-rt.jar index 07bcb06ee..15ad94def 100644 Binary files a/core/lib/jogl/gluegen-rt.jar and b/core/lib/jogl/gluegen-rt.jar differ diff --git a/core/lib/jogl/jogl-all-natives-linux-amd64.jar b/core/lib/jogl/jogl-all-natives-linux-amd64.jar index 544b7d7e0..726eb1320 100644 Binary files a/core/lib/jogl/jogl-all-natives-linux-amd64.jar and b/core/lib/jogl/jogl-all-natives-linux-amd64.jar differ diff --git a/core/lib/jogl/jogl-all-natives-linux-i586.jar b/core/lib/jogl/jogl-all-natives-linux-i586.jar index 75165df31..cfbffdda8 100644 Binary files a/core/lib/jogl/jogl-all-natives-linux-i586.jar and b/core/lib/jogl/jogl-all-natives-linux-i586.jar differ diff --git a/core/lib/jogl/jogl-all-natives-macosx-universal.jar b/core/lib/jogl/jogl-all-natives-macosx-universal.jar index 347583871..b23abf431 100644 Binary files a/core/lib/jogl/jogl-all-natives-macosx-universal.jar and b/core/lib/jogl/jogl-all-natives-macosx-universal.jar differ diff --git a/core/lib/jogl/jogl-all-natives-windows-amd64.jar b/core/lib/jogl/jogl-all-natives-windows-amd64.jar index 9dbe67cd4..b8ab7277b 100644 Binary files a/core/lib/jogl/jogl-all-natives-windows-amd64.jar and b/core/lib/jogl/jogl-all-natives-windows-amd64.jar differ diff --git a/core/lib/jogl/jogl-all-natives-windows-i586.jar b/core/lib/jogl/jogl-all-natives-windows-i586.jar index 0b03ce7c2..3e0685737 100644 Binary files a/core/lib/jogl/jogl-all-natives-windows-i586.jar and b/core/lib/jogl/jogl-all-natives-windows-i586.jar differ diff --git a/core/lib/jogl/jogl.all.jar b/core/lib/jogl/jogl-all.jar similarity index 52% rename from core/lib/jogl/jogl.all.jar rename to core/lib/jogl/jogl-all.jar index 34e9aa7d3..91cf32d1e 100644 Binary files a/core/lib/jogl/jogl.all.jar and b/core/lib/jogl/jogl-all.jar differ diff --git a/core/reference/gluegen-v2.0-rc11-sources.jar b/core/reference/gluegen-v2.0-rc11-sources.jar new file mode 100644 index 000000000..a0e3b57ec Binary files /dev/null and b/core/reference/gluegen-v2.0-rc11-sources.jar differ diff --git a/core/reference/jfreechart-1.0.14-sources.jar b/core/reference/jfreechart-1.0.14-sources.jar new file mode 100644 index 000000000..1c7f43756 Binary files /dev/null and b/core/reference/jfreechart-1.0.14-sources.jar differ diff --git a/core/reference/jogl-v2.0-rc11-sources.jar b/core/reference/jogl-v2.0-rc11-sources.jar new file mode 100644 index 000000000..d4668de7e Binary files /dev/null and b/core/reference/jogl-v2.0-rc11-sources.jar differ diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/00INDEX.txt b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/00INDEX.txt index 5190d5026..35cefe10a 100644 --- a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/00INDEX.txt +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/00INDEX.txt @@ -1,5 +1,6 @@ +Downloaded 2013/03/13 Rocket motor simulation data downloaded from ThrustCurve.org. -This ZIP file contains 1556 simulator data files. +This ZIP file contains 1581 simulator data files. For more info, please see http://www.thrustcurve.org/ AMW_I195.eng @@ -1864,6 +1865,13 @@ AeroTech_H128.rse Data Source: user Contributor: John Coker +AeroTech_H135.eng + Manufacturer: AeroTech + Designation: H135W + Data Format: RASP + Data Source: cert + Contributor: Robert Belknap + AeroTech_H148.eng Manufacturer: AeroTech Designation: H148R @@ -3434,12 +3442,19 @@ AeroTech_M1600.rse AeroTech_M1780.eng Manufacturer: AeroTech - Designation: M1780T + Designation: M1780NT Data Format: RASP Data Source: mfr Contributor: Mark Koelsch AeroTech_M1780_1.eng + Manufacturer: AeroTech + Designation: M1780T + Data Format: RASP + Data Source: mfr + Contributor: Mark Koelsch + +AeroTech_M1780_2.eng Manufacturer: AeroTech Designation: M1780T Data Format: RASP @@ -3551,6 +3566,13 @@ AeroTech_M650.rse Data Source: user Contributor: John Coker +AeroTech_M685.eng + Manufacturer: AeroTech + Designation: M685W-PS + Data Format: RASP + Data Source: mfr + Contributor: Mark Koelsch + AeroTech_M750.eng Manufacturer: AeroTech Designation: M750W @@ -3796,6 +3818,13 @@ Apogee_F10.rse Data Source: user Contributor: John Coker +Cesaroni_E22.eng + Manufacturer: Cesaroni Technology + Designation: E22-SS-13A + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_E22.rse Manufacturer: Cesaroni Technology Designation: E22-SS-13A @@ -3803,6 +3832,13 @@ Cesaroni_E22.rse Data Source: cert Contributor: Andre Choquette +Cesaroni_E31.eng + Manufacturer: Cesaroni Technology + Designation: E31-WT-15A + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_E31.rse Manufacturer: Cesaroni Technology Designation: E31-WT-15A @@ -3908,6 +3944,13 @@ Cesaroni_F36_1.rse Data Source: cert Contributor: Len Bryan +Cesaroni_F50.eng + Manufacturer: Cesaroni Technology + Designation: F50-SK-13A + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_F50.rse Manufacturer: Cesaroni Technology Designation: F50-SK-13A @@ -3915,6 +3958,13 @@ Cesaroni_F50.rse Data Source: cert Contributor: Andre Choquette +Cesaroni_F51.eng + Manufacturer: Cesaroni Technology + Designation: F51-CL-12A + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_F51.rse Manufacturer: Cesaroni Technology Designation: F51-CL-12A @@ -4909,6 +4959,20 @@ Cesaroni_I120.rse Data Source: cert Contributor: Andre Choquette +Cesaroni_I125.eng + Manufacturer: Cesaroni Technology + Designation: 567-I125-WH/LB + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + +Cesaroni_I125.rse + Manufacturer: Cesaroni Technology + Designation: 567-I125-WH/LB + Data Format: RockSim + Data Source: cert + Contributor: Andre Choquette + Cesaroni_I140.eng Manufacturer: Cesaroni Technology Designation: 396-I140-14A @@ -5728,6 +5792,13 @@ Cesaroni_J449.rse Data Source: cert Contributor: Andre Choquette +Cesaroni_J453.eng + Manufacturer: Cesaroni Technology + Designation: J453-WH-16A + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_J453.rse Manufacturer: Cesaroni Technology Designation: J453-WH-16A @@ -5938,6 +6009,13 @@ Cesaroni_K1720.rse Data Source: user Contributor: John Coker +Cesaroni_K2000.eng + Manufacturer: Cesaroni Technology + Designation: K2000-VM-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_K2000.rse Manufacturer: Cesaroni Technology Designation: K2000-VM-P @@ -6234,10 +6312,10 @@ Cesaroni_K635.rse Cesaroni_K650.eng Manufacturer: Cesaroni Technology - Designation: 1750 K650SS-16A + Designation: K650-PK-21A Data Format: RASP - Data Source: mfr - Contributor: Casey Hatch + Data Source: cert + Contributor: Andre Choquette Cesaroni_K650.rse Manufacturer: Cesaroni Technology @@ -6246,6 +6324,13 @@ Cesaroni_K650.rse Data Source: cert Contributor: Andre Choquette +Cesaroni_K650_1.eng + Manufacturer: Cesaroni Technology + Designation: 1750 K650SS-16A + Data Format: RASP + Data Source: mfr + Contributor: Casey Hatch + Cesaroni_K650_1.rse Manufacturer: Cesaroni Technology Designation: 1750 K650SS-16A @@ -6267,6 +6352,13 @@ Cesaroni_K660.rse Data Source: user Contributor: John Coker +Cesaroni_K661.eng + Manufacturer: Cesaroni Technology + Designation: K661-BS-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_K661.rse Manufacturer: Cesaroni Technology Designation: K661-BS-P @@ -6323,6 +6415,13 @@ Cesaroni_K710.rse Data Source: cert Contributor: Len Bryan +Cesaroni_K735.eng + Manufacturer: Cesaroni Technology + Designation: K735-SK-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_K735.rse Manufacturer: Cesaroni Technology Designation: K735-SK-P @@ -6428,6 +6527,13 @@ Cesaroni_L1030.rse Data Source: mfr Contributor: Thomas Raithby +Cesaroni_L1050.eng + Manufacturer: Cesaroni Technology + Designation: L1050-BS-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_L1050.rse Manufacturer: Cesaroni Technology Designation: L1050-BS-P @@ -6498,6 +6604,13 @@ Cesaroni_L1290.rse Data Source: cert Contributor: Andre Choquette +Cesaroni_L1350.eng + Manufacturer: Cesaroni Technology + Designation: L1350-CS-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_L1350.rse Manufacturer: Cesaroni Technology Designation: L1350-CS-P @@ -6582,6 +6695,13 @@ Cesaroni_L3150.rse Data Source: mfr Contributor: Thomas Raithby +Cesaroni_L3200.eng + Manufacturer: Cesaroni Technology + Designation: L3200-VM-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_L3200.rse Manufacturer: Cesaroni Technology Designation: L3200-VM-P @@ -6631,6 +6751,13 @@ Cesaroni_L640.rse Data Source: cert Contributor: Len Bryan +Cesaroni_L645.eng + Manufacturer: Cesaroni Technology + Designation: L645-GR-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_L645.rse Manufacturer: Cesaroni Technology Designation: L645-GR-P @@ -6673,6 +6800,13 @@ Cesaroni_L800_1.eng Data Source: mfr Contributor: Casey Hatch +Cesaroni_L805.eng + Manufacturer: Cesaroni Technology + Designation: L805-WH-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_L805.rse Manufacturer: Cesaroni Technology Designation: L805-WH-P @@ -6722,6 +6856,13 @@ Cesaroni_L890.rse Data Source: user Contributor: John Coker +Cesaroni_L910.eng + Manufacturer: Cesaroni Technology + Designation: L910-CS-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_L910.rse Manufacturer: Cesaroni Technology Designation: L910-CS-P @@ -7261,6 +7402,20 @@ Cesaroni_M520.rse Data Source: user Contributor: John Coker +Cesaroni_M6400.eng + Manufacturer: Cesaroni Technology + Designation: 8634-M6400-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + +Cesaroni_M6400.rse + Manufacturer: Cesaroni Technology + Designation: 8634-M6400-P + Data Format: RockSim + Data Source: cert + Contributor: Andre Choquette + Cesaroni_M795.eng Manufacturer: Cesaroni Technology Designation: 10133 M795-P @@ -7513,6 +7668,13 @@ Cesaroni_N5800.rse Data Source: cert Contributor: Andre Choquette +Cesaroni_O25000.eng + Manufacturer: Cesaroni Technology + Designation: 30795-O25000-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + Cesaroni_O25000.rse Manufacturer: Cesaroni Technology Designation: 30795-O25000-P @@ -7520,6 +7682,20 @@ Cesaroni_O25000.rse Data Source: user Contributor: Andre Choquette +Cesaroni_O3400.eng + Manufacturer: Cesaroni Technology + Designation: 21062-O3400-P + Data Format: RASP + Data Source: cert + Contributor: Andre Choquette + +Cesaroni_O3400.rse + Manufacturer: Cesaroni Technology + Designation: 21062-O3400-P + Data Format: RockSim + Data Source: cert + Contributor: Andre Choquette + Cesaroni_O3700.eng Manufacturer: Cesaroni Technology Designation: 29920-O3700-P diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/AeroTech_H135.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/AeroTech_H135.eng new file mode 100644 index 000000000..d0d183816 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/AeroTech_H135.eng @@ -0,0 +1,22 @@ +; AeroTech Pro-SU 29mm H135 14sec adjustable delay. +; From Tripoli Data Sheet updated 2013/02/10 +; Created by Robert Belknap 2013/03/04 +H135W 29 216 6-8-10-12-14 0.082 0.212 AT + 0.014 159.806 + 0.045 110.931 + 0.104 144.43 + 0.131 135.643 + 0.252 154.315 + 0.293 130.701 + 0.387 149.921 + 1.005 140.037 + 1.261 119.168 + 1.297 142.233 + 1.324 109.833 + 1.387 120.267 + 1.518 110.382 + 1.595 80.727 + 1.685 60.408 + 1.797 33.499 + 1.896 14.278 + 2.072 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/AeroTech_M1780_2.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/AeroTech_M1780_2.eng new file mode 100644 index 000000000..42b6beb7a --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/AeroTech_M1780_2.eng @@ -0,0 +1,23 @@ +M1780 75 665 P 2.56 4.715 AT + 0.028 2204.193 + 0.118 2566.025 + 0.173 2566.025 + 0.341 2214.151 + 0.44 2144.44 + 0.636 2114.564 + 1.34 2134.482 + 1.395 2204.193 + 1.648 2121.203 + 1.841 2041.534 + 1.874 1895.473 + 2.127 1643.186 + 2.353 1517.042 + 2.584 1460.61 + 2.672 1347.744 + 2.763 1068.901 + 2.829 723.666 + 2.939 438.183 + 2.999 292.122 + 3.101 172.617 + 3.272 36.515 + 3.5 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/AeroTech_M685.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/AeroTech_M685.eng new file mode 100644 index 000000000..c105bb8bf --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/AeroTech_M685.eng @@ -0,0 +1,24 @@ +; M685W-PS Moonburner +M685W-PS 75 801 P 4.32 7.008 AT + 0.083 1333.469 + 0.13 1368.376 + 0.249 1361.395 + 0.308 1380.012 + 0.403 1359.068 + 0.675 1184.53 + 1.018 1072.826 + 1.456 996.029 + 1.977 958.794 + 2.995 914.578 + 3.99 856.399 + 4.985 781.929 + 5.494 730.732 + 5.991 679.534 + 7.258 542.231 + 7.862 463.107 + 8.015 456.125 + 8.998 330.458 + 9.993 207.118 + 10.514 137.303 + 11.496 34.908 + 11.994 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_E22.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_E22.eng new file mode 100644 index 000000000..032433b0c --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_E22.eng @@ -0,0 +1,18 @@ +;Smoky Sam 24mm 1G +;24-E22-SS-13A +24-E22-SS-13A 24 69 13-10-8-6-4 0.0203 0.0565 CTI +0.008 18.292 +0.026 30 +0.038 30.792 +0.067 18.708 +0.101 21.875 +0.33 26.083 +0.528 28.042 +0.716 27.875 +0.841 23.542 +0.912 17.833 +0.987 7 +1.016 3.333 +1.065 1.083 +1.087 0 +; diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_E31.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_E31.eng new file mode 100644 index 000000000..dad740801 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_E31.eng @@ -0,0 +1,16 @@ +;White 24mm 1G +;26-E31-WH-15A +26-E31-WH-15A 24 69 15-12-10-8-6 0.0169 0.052 CTI +0.02 43.824 +0.027 39.964 +0.049 26.781 +0.113 32.601 +0.193 34.739 +0.282 35.808 +0.5 34.442 +0.727 29.276 +0.771 22.743 +0.807 9.561 +0.84 3.563 +0.87 0 +; diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_F50.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_F50.eng new file mode 100644 index 000000000..b4821faf5 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_F50.eng @@ -0,0 +1,15 @@ +; Skidmark 24mm 3G +; 60-F50-SK-13A +60-F50-SK-13A 24 133 13-10-8-6-4 0.0382 0.0939 CTI + 0.015 64.982 + 0.022 69.516 + 0.064 55.537 + 0.118 62.81 + 0.342 62.149 + 0.536 59.41 + 0.743 53.837 + 0.884 46.942 + 0.976 40.047 + 1.096 12.562 + 1.246 2.078 + 1.298 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_F51.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_F51.eng new file mode 100644 index 000000000..850b6cc88 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_F51.eng @@ -0,0 +1,12 @@ +; Classic 24mm 3G +; 75-F51-CL-12A +75-F51-CL-12A 24 133 12-9-7-5 0.040 0.095 CTI + 0.02 75.924 + 0.031 84.148 + 0.062 70.441 + 0.117 73.659 + 1.211 38.737 + 1.376 14.779 + 1.456 7.271 + 1.532 3.337 + 1.577 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_I125.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_I125.eng new file mode 100644 index 000000000..1de2c32d7 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_I125.eng @@ -0,0 +1,17 @@ +; CTI Pro-38 5G +; 567 I125 WH LB 10A +567-I125-WH-LB-10 38 367 10-7-5-3 0.3556 0.647 CTI + 0.046 174.74 + 0.089 267.82 + 0.135 235.986 + 0.258 193.772 + 0.35 182.007 + 0.553 174.74 + 1.124 176.125 + 1.502 173.01 + 1.806 162.284 + 2.439 135.986 + 3.372 84.775 + 4.005 36.678 + 4.395 14.879 + 4.585 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_I125.rse b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_I125.rse new file mode 100644 index 000000000..124d7e420 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_I125.rse @@ -0,0 +1,31 @@ + + + + CTI Pro-38 5G +567 I125 WH LB 10A + + + + + + + + + + + + + + + + + + + + + diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_J453.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_J453.eng new file mode 100644 index 000000000..e09b402b3 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_J453.eng @@ -0,0 +1,14 @@ +; White 38mm 6GXL +; 1013-J453-WH-16A +1013-J453-WH-16A 38 500 16-13-11-9-7 0.6132 0.9643 CTI + 0.018 663.789 + 0.04 725.18 + 0.105 630.216 + 0.23 578.417 + 0.451 543.885 + 1.454 535.252 + 1.797 291.607 + 1.91 188.969 + 2.088 128.537 + 2.276 19.185 + 2.364 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K2000.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K2000.eng new file mode 100644 index 000000000..1ebeedd0b --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K2000.eng @@ -0,0 +1,16 @@ +; VMax 75mm 2G +; 2330-K2000-VM-P +2330-K2000-VM-P 75 350 P 1.1642 2.4645 CTI + 0.01 1979.532 + 0.029 1634.503 + 0.161 1947.368 + 0.321 2190.058 + 0.457 2397.661 + 0.548 2473.684 + 0.678 2383.041 + 0.768 2263.158 + 0.977 1923.977 + 1.027 1883.041 + 1.104 859.649 + 1.149 87.719 + 1.191 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K650.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K650.eng index 8d9f29d17..153d50f66 100644 --- a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K650.eng +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K650.eng @@ -1,17 +1,12 @@ +;Pink 54mm 5G +;1997-K650-PK-21 +1997-K650-PK-21 54 488 21-19-17-15-13-11 1.0651 1.71 CTI +0.011 1353.5 +0.028 793.832 +0.186 815.421 +2.578 601.186 +2.689 479.953 +2.855 189.324 +3.086 43.179 +3.451 0 ; -; -K650SS 54.0 488.00 6-16 1.28100 1.98990 CTI - 0.04 664.52 - 0.12 645.90 - 0.31 642.24 - 0.60 664.78 - 0.91 684.59 - 1.22 712.82 - 1.50 723.41 - 1.80 728.70 - 2.10 664.52 - 2.40 614.68 - 2.51 680.53 - 2.55 534.62 - 2.61 268.19 - 2.66 0.00 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K650_1.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K650_1.eng new file mode 100644 index 000000000..8d9f29d17 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K650_1.eng @@ -0,0 +1,17 @@ +; +; +K650SS 54.0 488.00 6-16 1.28100 1.98990 CTI + 0.04 664.52 + 0.12 645.90 + 0.31 642.24 + 0.60 664.78 + 0.91 684.59 + 1.22 712.82 + 1.50 723.41 + 1.80 728.70 + 2.10 664.52 + 2.40 614.68 + 2.51 680.53 + 2.55 534.62 + 2.61 268.19 + 2.66 0.00 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K661.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K661.eng new file mode 100644 index 000000000..f3b9aae0e --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K661.eng @@ -0,0 +1,19 @@ +; Blue Streak 75mm 2G +; 2430-K661-BS-P +2430-K661-BS-P 75 350 P 1.2625 2.5278 CTI + 0.041 588.591 + 0.073 659.371 + 0.122 635.157 + 0.225 634.226 + 0.679 713.388 + 1.039 751.572 + 1.241 758.091 + 1.832 727.357 + 2.298 687.311 + 2.729 667.753 + 3.195 645.402 + 3.367 670.547 + 3.584 182.538 + 3.672 55.879 + 3.72 18.626 + 3.798 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K735.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K735.eng new file mode 100644 index 000000000..8d5e087ca --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_K735.eng @@ -0,0 +1,17 @@ +; Skidmark 75mm 2G +; 1955-K735-SK-P +1955-K735-SK-P 75 350 P 1.2221 2.5088 CTI + 0.053 683.157 + 0.142 593.64 + 0.408 697.291 + 0.893 848.057 + 1.109 886.926 + 1.24 882.214 + 1.362 889.282 + 2.0 783.274 + 2.346 706.714 + 2.439 648.999 + 2.612 123.675 + 2.676 36.514 + 2.754 12.956 + 2.831 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L1050.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L1050.eng new file mode 100644 index 000000000..a4593662f --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L1050.eng @@ -0,0 +1,18 @@ +; Blue Streak 75mm 3G +; 3727-L1050-BS-P +3727-L1050-BS-P 75 486 P 1.8634 3.4477 CTI + 0.035 721.412 + 0.081 1110.118 + 0.131 1057.412 + 0.249 1100.235 + 0.913 1194.118 + 1.125 1207.294 + 2.271 1100.235 + 2.797 1055.765 + 3.076 1042.588 + 3.122 996.471 + 3.23 807.059 + 3.452 289.882 + 3.544 123.529 + 3.685 36.235 + 3.731 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L1350.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L1350.eng new file mode 100644 index 000000000..dff14438a --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L1350.eng @@ -0,0 +1,18 @@ +; C-Star 75mm 3G +; 4263-L1350-CS-P +4263-L1350-CS-P 75 486 P 2.0245 3.5707 CTI + 0.016 1421.724 + 0.034 1345.218 + 0.049 1502.479 + 0.081 1415.348 + 0.21 1432.349 + 0.453 1432.349 + 0.809 1462.102 + 1.07 1534.357 + 1.28 1540.732 + 2.661 1283.589 + 2.843 1277.214 + 2.932 1115.702 + 3.037 488.784 + 3.163 82.881 + 3.284 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L3200.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L3200.eng new file mode 100644 index 000000000..3bdbfb4cf --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L3200.eng @@ -0,0 +1,16 @@ +; VMax 75mm 3G +; 3300-L3200-VM-P +3300-L3200-VM-P 75 486 P 1.6584 3.2637 CTI + 0.008 3315.23 + 0.024 2672.963 + 0.108 2975.207 + 0.415 3669.421 + 0.524 3711.924 + 0.644 3669.421 + 0.819 3225.502 + 0.911 3022.432 + 0.937 3050.767 + 0.957 2899.646 + 1.022 288.076 + 1.039 51.948 + 1.055 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L645.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L645.eng new file mode 100644 index 000000000..0985993c7 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L645.eng @@ -0,0 +1,15 @@ +; Green 75mm 3G +; 3419-L645-GR-P +3419-L645-GR-P 75 486 P 2.1441 3.7518 CTI + 0.05 511.243 + 0.12 647.574 + 0.256 689.231 + 0.425 684.497 + 1.554 736.568 + 2.043 764.97 + 3.075 687.337 + 4.557 601.183 + 4.756 550.059 + 4.985 412.781 + 5.281 69.112 + 5.39 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L805.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L805.eng new file mode 100644 index 000000000..7bff0a497 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L805.eng @@ -0,0 +1,14 @@ +;White 54mm 6GXL +;2833-L805-WH-P +2833-L805-WH-P 54 649 0 1.6752 2.5025 CTI +0.012 1618.1 +0.037 1171.8 +0.173 1027.97 +0.388 994.125 +0.626 985.664 +2.318 886.251 +3.054 448.414 +3.464 126.91 +3.608 40.188 +3.827 0 +; diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L910.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L910.eng new file mode 100644 index 000000000..10ab72802 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_L910.eng @@ -0,0 +1,17 @@ +;C-Star 75mm 2G +;2856-L910-CS-P +2856-L910-CS-P 75 350 0 1.3643 2.6158 CTI +0.034 858.741 +0.056 921.678 +0.305 952.448 +0.718 983.217 +1.221 1047.55 +1.642 1005.59 +1.842 973.427 +2.951 773.427 +3.035 584.615 +3.108 169.231 +3.152 72.727 +3.182 27.972 +3.262 0 +; diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_M6400.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_M6400.eng new file mode 100644 index 000000000..53827381e --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_M6400.eng @@ -0,0 +1,15 @@ +; CTI Pro-98 4G +; 8634 M6400-VM P +8634-M6400-VM-P 98 702 P 4.308 7.9190000000000005 CTI + 0.011 6079.636 + 0.135 6598.407 + 0.354 7080.774 + 0.503 7244.596 + 0.713 7162.685 + 0.954 6707.622 + 1.183 5688.282 + 1.233 5460.751 + 1.26 4914.676 + 1.288 3394.767 + 1.331 800.91 + 1.383 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_M6400.rse b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_M6400.rse new file mode 100644 index 000000000..ab58678bf --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_M6400.rse @@ -0,0 +1,29 @@ + + + + CTI Pro-98 4G +8634 M6400-VM P + + + + + + + + + + + + + + + + + + + diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_O25000.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_O25000.eng new file mode 100644 index 000000000..84e9fc538 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_O25000.eng @@ -0,0 +1,14 @@ +; CTI 30,795-O25,000-VM-P +; Single-Use +O25,000-VM-P 132 1407 P 14.705 23.558 CTI + 0.0080 21113.445 + 0.022 24705.882 + 0.042 26281.513 + 0.067 24390.756 + 0.201 24044.118 + 0.56 25052.521 + 0.927 24075.63 + 1.202 22815.126 + 1.25 23602.941 + 1.297 4852.941 + 1.32 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_O3400.eng b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_O3400.eng new file mode 100644 index 000000000..89d9a6061 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_O3400.eng @@ -0,0 +1,18 @@ +; CTI Pro-98 6GXL +; 21062 O3400-IM P +21062-O3400-IM-P 98 1239 P 11.272 16.842 CTI + 0.04 3959.811 + 0.052 4432.624 + 0.101 4515.366 + 0.19 4420.804 + 0.38 4391.253 + 0.965 4444.444 + 2.176 4698.582 + 2.887 4592.199 + 3.658 4225.768 + 4.17 2854.61 + 4.493 2559.102 + 4.881 1619.385 + 5.483 868.794 + 6.137 248.227 + 6.322 0.0 diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_O3400.rse b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_O3400.rse new file mode 100644 index 000000000..bf02a05b0 --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Cesaroni_O3400.rse @@ -0,0 +1,32 @@ + + + + CTI Pro-98 6GXL +21062 O3400-IM P + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Loki_J175.rse b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Loki_J175.rse new file mode 100644 index 000000000..aef205f3a --- /dev/null +++ b/core/resources-src/datafiles/thrustcurves/thrustcurve.org/Loki_J175.rse @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/resources-src/pix/splashscreen-1.0.png b/core/resources-src/pix/splashscreen-1.0.png index c2767aa53..ab90ebda7 100644 Binary files a/core/resources-src/pix/splashscreen-1.0.png and b/core/resources-src/pix/splashscreen-1.0.png differ diff --git a/core/resources-src/pix/splashscreen-1.0.xcf.gz b/core/resources-src/pix/splashscreen-1.0.xcf.gz index 19a3e4f27..7a9559632 100644 Binary files a/core/resources-src/pix/splashscreen-1.0.xcf.gz and b/core/resources-src/pix/splashscreen-1.0.xcf.gz differ diff --git a/core/resources/build.properties b/core/resources/build.properties index 640d6d5d1..82fbc6649 100644 --- a/core/resources/build.properties +++ b/core/resources/build.properties @@ -1,7 +1,7 @@ # The OpenRocket build version -build.version=12.09.1dev +build.version=13.04beta1 # The source of the package. When building a package for a specific diff --git a/core/resources/datafiles/examples/A simple model rocket.ork b/core/resources/datafiles/examples/A simple model rocket.ork index c87b3cd2b..686e6b69d 100644 Binary files a/core/resources/datafiles/examples/A simple model rocket.ork and b/core/resources/datafiles/examples/A simple model rocket.ork differ diff --git a/core/resources/datafiles/examples/Apocalypse with decals.ork b/core/resources/datafiles/examples/Apocalypse with decals.ork new file mode 100644 index 000000000..982215881 Binary files /dev/null and b/core/resources/datafiles/examples/Apocalypse with decals.ork differ diff --git a/core/resources/datafiles/examples/Boosted Dart.ork b/core/resources/datafiles/examples/Boosted Dart.ork new file mode 100644 index 000000000..37cebb2f4 Binary files /dev/null and b/core/resources/datafiles/examples/Boosted Dart.ork differ diff --git a/core/resources/datafiles/examples/Clustered rocket design.ork b/core/resources/datafiles/examples/Clustered rocket design.ork index 517bb601e..d923be811 100644 Binary files a/core/resources/datafiles/examples/Clustered rocket design.ork and b/core/resources/datafiles/examples/Clustered rocket design.ork differ diff --git a/core/resources/datafiles/examples/High Power Airstart.ork b/core/resources/datafiles/examples/High Power Airstart.ork new file mode 100644 index 000000000..b30577db5 Binary files /dev/null and b/core/resources/datafiles/examples/High Power Airstart.ork differ diff --git a/core/resources/datafiles/examples/Hybrid rocket with dual parachute deployment.ork b/core/resources/datafiles/examples/Hybrid rocket with dual parachute deployment.ork index f748d70fa..f8af09ffa 100644 Binary files a/core/resources/datafiles/examples/Hybrid rocket with dual parachute deployment.ork and b/core/resources/datafiles/examples/Hybrid rocket with dual parachute deployment.ork differ diff --git a/core/resources/datafiles/examples/Preset Usage.ork b/core/resources/datafiles/examples/Preset Usage.ork index 9e78075fc..b24790f90 100644 Binary files a/core/resources/datafiles/examples/Preset Usage.ork and b/core/resources/datafiles/examples/Preset Usage.ork differ diff --git a/core/resources/datafiles/examples/Roll-stabilized rocket.ork b/core/resources/datafiles/examples/Roll-stabilized rocket.ork index e7695cd7f..ee2d4b496 100644 Binary files a/core/resources/datafiles/examples/Roll-stabilized rocket.ork and b/core/resources/datafiles/examples/Roll-stabilized rocket.ork differ diff --git a/core/resources/datafiles/examples/Simulation listeners.ork b/core/resources/datafiles/examples/Simulation listeners.ork index d09d68c6e..19d6e57d7 100644 Binary files a/core/resources/datafiles/examples/Simulation listeners.ork and b/core/resources/datafiles/examples/Simulation listeners.ork differ diff --git a/core/resources/datafiles/examples/TARC Payloader.ork b/core/resources/datafiles/examples/TARC Payloader.ork new file mode 100644 index 000000000..9c244c3bf Binary files /dev/null and b/core/resources/datafiles/examples/TARC Payloader.ork differ diff --git a/core/resources/datafiles/examples/Three-stage rocket.ork b/core/resources/datafiles/examples/Three-stage rocket.ork index 22edba941..df0ebe936 100644 Binary files a/core/resources/datafiles/examples/Three-stage rocket.ork and b/core/resources/datafiles/examples/Three-stage rocket.ork differ diff --git a/core/resources/datafiles/presets/system.ser b/core/resources/datafiles/presets/system.ser index 113a824fb..625e63631 100644 Binary files a/core/resources/datafiles/presets/system.ser and b/core/resources/datafiles/presets/system.ser differ diff --git a/core/resources/datafiles/textures/balsa.png b/core/resources/datafiles/textures/balsa.png new file mode 100644 index 000000000..f48f581fb Binary files /dev/null and b/core/resources/datafiles/textures/balsa.png differ diff --git a/core/resources/datafiles/textures/cardboard.png b/core/resources/datafiles/textures/cardboard.png new file mode 100644 index 000000000..0101594de Binary files /dev/null and b/core/resources/datafiles/textures/cardboard.png differ diff --git a/core/resources/datafiles/textures/chute.png b/core/resources/datafiles/textures/chute.png new file mode 100644 index 000000000..c58f4f465 Binary files /dev/null and b/core/resources/datafiles/textures/chute.png differ diff --git a/core/resources/datafiles/textures/hardboard.png b/core/resources/datafiles/textures/hardboard.png new file mode 100644 index 000000000..a769700d8 Binary files /dev/null and b/core/resources/datafiles/textures/hardboard.png differ diff --git a/core/resources/datafiles/textures/motors/aerotech.png b/core/resources/datafiles/textures/motors/aerotech.png new file mode 100644 index 000000000..5efeac52f Binary files /dev/null and b/core/resources/datafiles/textures/motors/aerotech.png differ diff --git a/core/resources/datafiles/textures/motors/estes.png b/core/resources/datafiles/textures/motors/estes.png new file mode 100644 index 000000000..9d8efbf24 Binary files /dev/null and b/core/resources/datafiles/textures/motors/estes.png differ diff --git a/core/resources/datafiles/textures/motors/reusable.png b/core/resources/datafiles/textures/motors/reusable.png new file mode 100644 index 000000000..17ce44745 Binary files /dev/null and b/core/resources/datafiles/textures/motors/reusable.png differ diff --git a/core/resources/datafiles/textures/spiral-wound-alpha.png b/core/resources/datafiles/textures/spiral-wound-alpha.png new file mode 100644 index 000000000..628dd92bb Binary files /dev/null and b/core/resources/datafiles/textures/spiral-wound-alpha.png differ diff --git a/core/resources/datafiles/textures/wadding.png b/core/resources/datafiles/textures/wadding.png new file mode 100644 index 000000000..a80104b0a Binary files /dev/null and b/core/resources/datafiles/textures/wadding.png differ diff --git a/core/resources/datafiles/textures/wood.png b/core/resources/datafiles/textures/wood.png new file mode 100644 index 000000000..8e46ce4a4 Binary files /dev/null and b/core/resources/datafiles/textures/wood.png differ diff --git a/core/resources/datafiles/thrustcurves/thrustcurves.ser b/core/resources/datafiles/thrustcurves/thrustcurves.ser index 39fe9755d..061d74654 100644 Binary files a/core/resources/datafiles/thrustcurves/thrustcurves.ser and b/core/resources/datafiles/thrustcurves/thrustcurves.ser differ diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 492a90a83..f561dd63f 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -45,14 +45,14 @@ RocketActions.MoveDownAct.ttip.Movedown = Move this component downwards. ! RocketPanel RocketPanel.FigTypeAct.Sideview = Side view -RocketPanel.FigTypeAct.ttip.Sideview = Side view RocketPanel.FigTypeAct.Backview = Back view -RocketPanel.FigTypeAct.ttip.Backview = Rear view -RocketPanel.FigViewAct.2D = 2D View -RocketPanel.FigViewAct.ttip.2D = 2D View -RocketPanel.FigViewAct.3D = 3D View -RocketPanel.FigViewAct.ttip.3D = 3D View -RocketPanel.lbl.Motorcfg = Motor configuration: +RocketPanel.FigTypeAct.Figure3D = 3D Figure +RocketPanel.FigTypeAct.Finished = 3D Finished +RocketPanel.FigTypeAct.Unfinished = 3D Unfinished + + +RocketPanel.lbl.Flightcfg = Flight configuration: +RocketPanel.but.FlightcfgEdit = Edit RocketPanel.lbl.infoMessage = Click to select    Shift+click to select other    Double-click to edit    Click+drag to move @@ -76,6 +76,7 @@ error.fileExists.desc = File '{filename}' exists. Do you want to overwrite it? error.writing.title = Error writing file error.writing.desc = An error occurred while writing to the file: +Configuration.lbl.override = Parameter can be overridden in each flight configuration. ! Labels used in buttons of dialog windows @@ -173,16 +174,38 @@ debuglogdlg.lbl.Stacktrace = Stack trace: MotorChooserDialog.title = Select a rocket motor ! Edit Motor configuration dialog -edtmotorconfdlg.but.removemotor = Remove motor -edtmotorconfdlg.but.Selectmotor = Select motor -edtmotorconfdlg.but.Removeconfiguration = Remove configuration -edtmotorconfdlg.but.Newconfiguration = New configuration -edtmotorconfdlg.lbl.Motormounts = Motor mounts: -edtmotorconfdlg.title.Editmotorconf = Edit motor configurations +edtmotorconfdlg.but.Removeconfiguration = Remove +edtmotorconfdlg.but.Renameconfiguration = Rename +edtmotorconfdlg.but.Newconfiguration = New +edtmotorconfdlg.but.Copyconfiguration = Copy +edtmotorconfdlg.title.Editmotorconf = Edit Flight configurations +edtmotorconfdlg.title.Renameconf = Rename Flight Configuration +edtmotorconfdlg.title.Selectdeploymentconf = Select Deployment Configuration +edtmotorconfdlg.title.Selectignitionconf = Select Ignition Configuration +edtmotorconfdlg.title.Selectseparationconf = Select Separation Configuration +edtmotorconfdlg.lbl.Selectedconf = Selected flight configuration: edtmotorconfdlg.selectcomp = Select which components function as motor mounts: -edtmotorconfdlg.lbl.Motorconfig = Motor configurations: edtmotorconfdlg.lbl.Configname = Configuration name: -edtmotorconfdlg.lbl.Leavenamedefault = Leave name empty for default. +edtmotorconfdlg.lbl.Motortab = Motors +edtmotorconfdlg.lbl.Recoverytab = Recovery +edtmotorconfdlg.lbl.Stagetab = Stages +edtmotorconfdlg.tbl.None = None +edtmotorconfdlg.tbl.Motorheader = Motor +edtmotorconfdlg.tbl.Mountheader = Motor Mount +edtmotorconfdlg.tbl.Ignitionheader = Ignition +edtmotorconfdlg.but.Resetdeployment = Reset to default +edtmotorconfdlg.but.Selectdeployment = Select deployment +edtmotorconfdlg.tbl.Recoveryheader = Recovery Device +edtmotorconfdlg.tbl.Deploymentheader = Deployment +edtmotorconfdlg.but.Resetseparation = Reset to default +edtmotorconfdlg.but.Selectseparation = Select separation +edtmotorconfdlg.tbl.Stageheader = Stage +edtmotorconfdlg.tbl.Separationheader = Separation + +! Rename FlightConfiguration Dialog +RenameConfigDialog.title = Rename Configuration +RenameConfigDialog.lbl.name = Name for flight configuration: +RenameConfigDialog.but.reset = Reset to default ! Example design dialog exdesigndlg.but.open = Open @@ -230,6 +253,7 @@ pref.dlg.tab.Materials = Materials pref.dlg.tab.Custommaterials = Custom materials pref.dlg.tab.Options = Options pref.dlg.tab.Miscellaneousoptions = Miscellaneous options +pref.dlg.tab.DecalEditor = Graphics Editor pref.dlg.lbl.Positiontoinsert = Position to insert new body components: pref.dlg.lbl.Confirmdeletion = Confirm deletion of simulations: pref.dlg.lbl.User-definedthrust = User-defined thrust curves: @@ -291,9 +315,10 @@ simedtdlg.tab.Simopt = Simulation options simedtdlg.tab.Plotdata = Plot data simedtdlg.tab.CustomExpressions = Custom expressions simedtdlg.tab.Exportdata = Export data -simedtdlg.lbl.Motorcfg = Motor configuration: -simedtdlg.lbl.ttip.Motorcfg = Select the motor configuration to use. -simedtdlg.combo.ttip.motorconf = Select the motor configuration to use. +simedtdlg.lbl.Flightcfg = Flight configuration: +simedtdlg.but.FlightcfgEdit = Edit +simedtdlg.lbl.ttip.Flightcfg = Select the flight configuration to use. +simedtdlg.combo.ttip.Flightcfg = Select the flight configuration to use. simedtdlg.lbl.Wind = Wind simedtdlg.lbl.Averwindspeed = Average windspeed: simedtdlg.lbl.ttip.Averwindspeed = The average windspeed relative to the ground. @@ -387,6 +412,7 @@ simpanel.dlg.lbl.DeleteSim2 = This operation cannot be undone. simpanel.dlg.lbl.DeleteSim3 = Delete simulations simpanel.col.Name = Name simpanel.col.Motors = Motors +simpanel.col.Configuration = Configuration simpanel.col.Velocityoffrod = Velocity off rod simpanel.col.Velocityatdeploy = Velocity at deployment simpanel.col.Apogee = Apogee @@ -698,6 +724,29 @@ FinMarkingGuide.lbl.Front = Front MotorDbLoadDlg.title = Loading motors MotorDbLoadDlg.Loadingmotors = Loading motors... +! AppearanceConfig +AppearanceCfg.lbl.Appearance = Appearance +AppearanceCfg.lbl.Usedefault = Use default +AppearanceCfg.but.edit = Edit +AppearanceCfg.but.savedefault = Save as default appearance +AppearanceCfg.lbl.Texture = Texture: +AppearanceCfg.lbl.shine = Shine: +AppearanceCfg.lbl.color.Color = Color: +AppearanceCfg.lbl.color.diffuse = Diffuse Color: +AppearanceCfg.lbl.color.ambient = Ambient Color: +AppearanceCfg.lbl.color.specular = Specular Color: +AppearanceCfg.lbl.texture.scale = Scale: +AppearanceCfg.lbl.texture.offset = Offset: +AppearanceCfg.lbl.texture.center = Center: +AppearanceCfg.lbl.texture.rotation = Rotation: +AppearanceCfg.lbl.texture.repeat = Repeat: + +! Texture Wrap Modes +TextureWrap.Repeat = Repeat +TextureWrap.Mirror = Repeat & Mirror +TextureWrap.Clamp = Clamp Edge Pixels +TextureWrap.Sticker = Sticker + ! RocketConfig RocketCfg.lbl.Designname = Design name: RocketCfg.lbl.Designer = Designer: @@ -859,8 +908,9 @@ MassComponentCfg.but.Reset = Reset ! MotorConfig MotorCfg.checkbox.compmotormount = This component is a motor mount -MotorCfg.lbl.Motorcfg = Motor configuration: +MotorCfg.lbl.Flightcfg = Flight configuration: MotorCfg.but.New = New +MotorCfg.but.FlightcfgEdit = Edit MotorCfg.lbl.Currentmotor = Current motor: MotorCfg.lbl.Motoroverhang = Motor overhang: MotorCfg.lbl.Ignitionat = Ignition at: @@ -1020,8 +1070,6 @@ StorageOptChooser.lbl.seconds = seconds StorageOptChooser.rdbut.Onlyprimfig = Only primary figures StorageOptChooser.lbl.longC1 = Store only the values shown in the summary table.
StorageOptChooser.lbl.longC2 = This results in the smallest files. -StorageOptChooser.checkbox.Compfile = Compress file -StorageOptChooser.lbl.UsingComp = Using compression reduces the file size significantly. StorageOptChooser.lbl.longD1 = An estimate on how large the resulting file would be with the present options. StorageOptChooser.ttip.Saveopt = Save options StorageOptChooser.lbl.Estfilesize = Estimated file size: @@ -1054,10 +1102,8 @@ TCMotorSelPan.noDescription = No description available. ! PlotDialog -PlotDialog.title.Flightdataplot = Flight data plot -PlotDialog.Chart.Simulatedflight = Simulated flight PlotDialog.CheckBox.Showdatapoints = Show data points -PlotDialog.lbl.Chart = Click and drag down+right to zoom in, up+left to zoom out +PlotDialog.lbl.Chart = left click drag to zoom area. mouse wheel to zoom. ctrl-mouse wheel to zoom x axis only. ctrl-left click drag to pan. right click drag to zoom dynamically. ! "main" prefix is used for the main application dialog @@ -1084,6 +1130,8 @@ main.menu.file.close = Close BasicFrame.item.Closedesign = Close the current rocket design main.menu.file.quit = Quit BasicFrame.item.Quitprogram = Quit the program +main.menu.file.exportDecal = Export Decal +main.menu.file.exportDecal.desc = Export a decal from the current rocket design to a file for editing. main.menu.edit = Edit BasicFrame.menu.Rocketedt = Rocket editing @@ -1273,7 +1321,8 @@ Streamer.Streamer = Streamer Sleeve.Sleeve = Sleeve !Rocket -Rocket.motorCount.Nomotor = [No motors] +Rocket.motorCount.Nomotor = No motors +Rocket.motorCount.noStageMotors = None Rocket.compname.Rocket = Rocket !MotorMount @@ -1283,6 +1332,13 @@ MotorMount.IgnitionEvent.EJECTION_CHARGE = First ejection charge of previous sta MotorMount.IgnitionEvent.BURNOUT = First burnout of previous stage MotorMount.IgnitionEvent.NEVER = Never +MotorMount.IgnitionEvent.short.AUTOMATIC = Automatic +MotorMount.IgnitionEvent.short.LAUNCH = Launch +MotorMount.IgnitionEvent.short.EJECTION_CHARGE = Ejection charge +MotorMount.IgnitionEvent.short.BURNOUT = Burnout +MotorMount.IgnitionEvent.short.NEVER = Never + + !ComponentIcons ComponentIcons.Nosecone = Nose cone ComponentIcons.Bodytube = Body tube @@ -1314,6 +1370,15 @@ RecoveryDevice.DeployEvent.CURRENT_STAGE_SEPARATION = Current stage separation RecoveryDevice.DeployEvent.LOWER_STAGE_SEPARATION = Lower stage separation RecoveryDevice.DeployEvent.NEVER = Never +RecoveryDevice.DeployEvent.short.LAUNCH = Launch +RecoveryDevice.DeployEvent.short.EJECTION = Ejection charge +RecoveryDevice.DeployEvent.short.APOGEE = Apogee +RecoveryDevice.DeployEvent.short.ALTITUDE = Altitude +RecoveryDevice.DeployEvent.short.CURRENT_STAGE_SEPARATION = Current stage separation +RecoveryDevice.DeployEvent.short.LOWER_STAGE_SEPARATION = Lower stage separation +RecoveryDevice.DeployEvent.short.NEVER = Never + + ! FlightEvent FlightEvent.Type.LAUNCH = Launch FlightEvent.Type.IGNITION = Motor ignition @@ -1327,6 +1392,7 @@ FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT = Recovery device deployment FlightEvent.Type.GROUND_HIT = Ground hit FlightEvent.Type.SIMULATION_END = Simulation end FlightEvent.Type.ALTITUDE = Altitude change +FlightEvent.Type.TUMBLE = Tumbling ! ThrustCurveMotorColumns TCurveMotorCol.MANUFACTURER = Manufacturer @@ -1718,6 +1784,13 @@ CustomFinImport.description = The image will be converted internally to black an PresetModel.lbl.select = Select preset PresetModel.lbl.database = From database... +DecalModel.lbl.select = +DecalModel.lbl.choose = From file... + +! Export Decal Dialog +ExportDecalDialog.title = Export Decal +ExportDecalDialog.decalList.lbl = Decal +ExportDecalDialog.exception = Unable to write decal to file ''{0}'' ! Component Preset Chooser Dialog ComponentPresetChooserDialog.title = Choose component preset @@ -1756,4 +1829,43 @@ table.column.LineCount = Line Count table.column.LineLength = Line Length table.column.LineMaterial = Line Material +! Edit Decal Dialog +EditDecalDialog.title = Edit decal +EditDecalDialog.lbl.prompt = Show Prompt +EditDecalDialog.lbl.select = Select Editor +EditDecalDialog.lbl.system = Use Default Editor +EditDecalDialog.lbl.cmdline = Command Line +EditDecalDialog.lbl.cmdline.help = The filename will be subsituted for ''%%'' +EditDecalDialog.lbl.always = Always use these settings +EditDecalDialog.btn.chooser = Select Graphics Editor Program +EditDecalHelper.createFileException = Cannot create temporary file {0} +EditDecalHelper.launchSystemEditorException = Cannot launch system graphics editor +EditDecalHelper.launchCustomEditorException = Cannot launch graphics editor with command ''{0}'' +EditDecalHelper.editPreferencesHelp = The editor used can be modified in the Preferences dialog. +MotorConfigurationPanel.lbl.motorMounts = Motor mounts: +MotorConfigurationPanel.lbl.motorConfiguration = Motor configurations: +MotorConfigurationPanel.btn.removeMotor = Remove motor +MotorConfigurationPanel.btn.selectMotor = Select motor +MotorConfigurationPanel.btn.selectIgnition = Select ignition +MotorConfigurationPanel.btn.resetIgnition = Reset ignition + +MotorConfigurationTableModel.table.ignition.default = Default ({0}) +RecoveryConfigurationPanel.table.deployment.default = Default ({0}) +SeparationConfigurationPanel.table.separation.default = Default ({0}) + +IgnitionSelectionDialog.opt.title = Which flight configurations are affected: +IgnitionSelectionDialog.opt.default = Change all configurations using the default ignition event +IgnitionSelectionDialog.opt.override = Override for the {0} flight configuration only + +DeploymentSelectionDialog.opt.title = Which flight configurations are affected: +DeploymentSelectionDialog.opt.default = Change all configuration using the default deployment event +DeploymentSelectionDialog.opt.override = Override for the {0} flight configuration only + +SeparationSelectionDialog.opt.title = Which flight configurations are affected: +SeparationSelectionDialog.opt.default = Change all configuration using the default separation event +SeparationSelectionDialog.opt.override = Override for the {0} flight configuration only + +MotorConfigurationPanel.description = Select the motors and motor ignition events of the selected flight configuration.
Motor mounts: Select which components function as motor mounts.
Motor configurations: Select the motor and ignition event for each motor mount. + +MotorDescriptionSubstitutor.description = Motors in the configuration diff --git a/core/resources/l10n/messages_cs.properties b/core/resources/l10n/messages_cs.properties index c8a913dbd..96751cf89 100644 --- a/core/resources/l10n/messages_cs.properties +++ b/core/resources/l10n/messages_cs.properties @@ -170,14 +170,14 @@ debuglogdlg.lbl.Stacktrace = Tracov ! Edit Motor configuration dialog -edtmotorconfdlg.but.removemotor = Odeber motor -edtmotorconfdlg.but.Selectmotor = Vyber motor +MotorConfigurationPanel.btn.removeMotor = Odeber motor +MotorConfigurationPanel.btn.selectMotor = Vyber motor edtmotorconfdlg.but.Removeconfiguration = Odeber nastavení edtmotorconfdlg.but.Newconfiguration = Nové nastavení -edtmotorconfdlg.lbl.Motormounts = Pripojení motoru: +MotorConfigurationPanel.lbl.motorMounts = Pripojení motoru: edtmotorconfdlg.title.Editmotorconf = Úprava nastavení motoru edtmotorconfdlg.selectcomp = Vyber ke kterým komponentám se má motor pripojit: -edtmotorconfdlg.lbl.Motorconfig = Nastavení motoru: +MotorConfigurationPanel.lbl.motorConfiguration = Nastavení motoru: edtmotorconfdlg.lbl.Configname = Jméno nastavení: edtmotorconfdlg.lbl.Leavenamedefault = Nechej prázdné polícko jako výchozí hodnotu. @@ -1159,7 +1159,7 @@ ShockCord.ShockCord = Poutac Bulkhead.Bulkhead = Prepá\u017Eka !Rocket -Rocket.motorCount.Nomotor = [\u017Dádné motory] +Rocket.motorCount.Nomotor = \u017Dádné motory Rocket.compname.Rocket = Raketa !MotorMount diff --git a/core/resources/l10n/messages_de.properties b/core/resources/l10n/messages_de.properties index 38133ab98..753563197 100644 --- a/core/resources/l10n/messages_de.properties +++ b/core/resources/l10n/messages_de.properties @@ -171,14 +171,14 @@ debuglogdlg.lbl.Stacktrace = Stacktrace: ! Edit Motor configuration dialog -edtmotorconfdlg.but.removemotor = Motor entfernen -edtmotorconfdlg.but.Selectmotor = Motor auswählen +MotorConfigurationPanel.btn.removeMotor = Motor entfernen +MotorConfigurationPanel.btn.selectMotor = Motor auswählen edtmotorconfdlg.but.Removeconfiguration = Konfiguration entfernen edtmotorconfdlg.but.Newconfiguration = Neue Konfiguration -edtmotorconfdlg.lbl.Motormounts = Motorhalterungen: +MotorConfigurationPanel.lbl.motorMounts = Motorhalterungen: edtmotorconfdlg.title.Editmotorconf = Motorkonfiguration bearbeiten edtmotorconfdlg.selectcomp = Auswählen, welche Komponenten Motorhalterungen sind: -edtmotorconfdlg.lbl.Motorconfig = Motorkonfigurationen: +MotorConfigurationPanel.lbl.motorConfiguration = Motorkonfigurationen: edtmotorconfdlg.lbl.Configname = Name der Konfiguration: edtmotorconfdlg.lbl.Leavenamedefault = Leer lassen für Standardwert. @@ -1216,7 +1216,7 @@ ShockCord.ShockCord = Gummiband Bulkhead.Bulkhead = Schott !Rocket -Rocket.motorCount.Nomotor = [Keine Motoren] +Rocket.motorCount.Nomotor = Keine Motoren Rocket.compname.Rocket = Rakete !MotorMount diff --git a/core/resources/l10n/messages_es.properties b/core/resources/l10n/messages_es.properties index 36ea24e56..4e22d9dd0 100644 --- a/core/resources/l10n/messages_es.properties +++ b/core/resources/l10n/messages_es.properties @@ -179,14 +179,14 @@ debuglogdlg.lbl.Stacktrace = Trazabilidad de la pila: ! Edit Motor configuration dialog -edtmotorconfdlg.but.removemotor = Quitar motor -edtmotorconfdlg.but.Selectmotor = Seleccionar motor +MotorConfigurationPanel.btn.removeMotor = Quitar motor +MotorConfigurationPanel.btn.selectMotor = Seleccionar motor edtmotorconfdlg.but.Removeconfiguration = Quitar configuración edtmotorconfdlg.but.Newconfiguration = Nueva configuración -edtmotorconfdlg.lbl.Motormounts = Porta motor: +MotorConfigurationPanel.lbl.motorMounts = Porta motor: edtmotorconfdlg.title.Editmotorconf = Mostrar las configuraciones de motor edtmotorconfdlg.selectcomp = Seleccionar qué componentes tienen la función de porta motor: -edtmotorconfdlg.lbl.Motorconfig = Configuraciones del motor: +MotorConfigurationPanel.lbl.motorConfiguration = Configuraciones del motor: edtmotorconfdlg.lbl.Configname = Nombre de la configuración: edtmotorconfdlg.lbl.Leavenamedefault = Dejar el nombre por defecto. @@ -1227,7 +1227,7 @@ ShockCord.ShockCord = Tirante de suspensi Bulkhead.Bulkhead = Disco de enganche !Rocket -Rocket.motorCount.Nomotor = [Sin motores] +Rocket.motorCount.Nomotor = Sin motores Rocket.compname.Rocket = Cohete !MotorMount diff --git a/core/resources/l10n/messages_fr.properties b/core/resources/l10n/messages_fr.properties index ead57a82e..4762a745d 100644 --- a/core/resources/l10n/messages_fr.properties +++ b/core/resources/l10n/messages_fr.properties @@ -1,4 +1,3 @@ -# # French base translation file # Translated by Tripoli France # Should you need to add new logical keys here is the proposed method @@ -173,14 +172,14 @@ debuglogdlg.lbl.Stacktrace = Stack trace: MotorChooserDialog.title = Selectionnez un moteur fusée ! Edit Motor configuration dialog -edtmotorconfdlg.but.removemotor = Enlever le moteur -edtmotorconfdlg.but.Selectmotor = Choisir le moteur +MotorConfigurationPanel.btn.removeMotor = Enlever le moteur +MotorConfigurationPanel.btn.selectMotor = Choisir le moteur edtmotorconfdlg.but.Removeconfiguration = Supprimer la configuration edtmotorconfdlg.but.Newconfiguration = Nouvelle configuration -edtmotorconfdlg.lbl.Motormounts = Portes moteur: +MotorConfigurationPanel.lbl.motorMounts = Portes moteur: edtmotorconfdlg.title.Editmotorconf = Changer la configuration moteur edtmotorconfdlg.selectcomp = Choisir les pièces utilisées par le porte moteur: -edtmotorconfdlg.lbl.Motorconfig = Configurations moteur: +MotorConfigurationPanel.lbl.motorConfiguration = Configurations moteur: edtmotorconfdlg.lbl.Configname = Nom de la configuration: edtmotorconfdlg.lbl.Leavenamedefault = Laisser le champ vide par défaut. @@ -220,6 +219,7 @@ MaterialModel.title.Defcustmat = D pref.dlg.but.add = Ajouter pref.dlg.but.reset = Réinitialiser pref.dlg.but.checknow = Vérifier maintenant +pref.dlg.but.openlast = Ouvrir le dernier fichier de projet au démarrage pref.dlg.but.defaultmetric = Système métrique pref.dlg.but.defaultimperial = Système impérial pref.dlg.title.Preferences = Préférences @@ -359,8 +359,10 @@ simedtdlg.IntensityDesc.High = Haute simedtdlg.IntensityDesc.Veryhigh = Très haute simedtdlg.IntensityDesc.Extreme = Extrême -GeodeticComputationStrategy.none.name = Aucune -GeodeticComputationStrategy.none.desc = Ne pas faire de calculs geodetic. +GeodeticComputationStrategy.flat.name = Flat Earth +GeodeticComputationStrategy.flat.desc = Perform computations with a flat Earth approximation. Sufficient for low-altitude flights. +#GeodeticComputationStrategy.none.name = Aucune +#GeodeticComputationStrategy.none.desc = Ne pas faire de calculs geodetic. GeodeticComputationStrategy.spherical.name = Approximation sphérique GeodeticComputationStrategy.spherical.desc = Performe des calculs geodetic en assumant une terre sphérique.
Ceci est suffisamment précis dans la plupart des cas. GeodeticComputationStrategy.wgs84.name = ellipsoïde WGS84 @@ -631,6 +633,8 @@ componentanalysisdlg.lbl.reflenght = Longueur de r componentanalysisdlg.lbl.refarea = Aire de référence: !componentanalysisdlg.but.close =Fermer componentanalysisdlg.TabStability.Col.Component = Pièce +componentanalysisdlg.TOTAL = Total +componentanalysisdlg.noWarnings = Pas d'alertes. ! Custom Material dialog custmatdlg.title.Custommaterial = Matériau personnalisé @@ -1048,6 +1052,7 @@ TCMotorSelPan.lbl.Digest = Assimilation: TCMotorSelPan.title.Thrustcurve = Courbe de poussée: TCMotorSelPan.title.Thrust = Poussée TCMotorSelPan.delayBox.None = Aucun +TCMotorSelPan.noDescription = Aucune description disponible ! PlotDialog @@ -1094,6 +1099,7 @@ main.menu.edit.paste = Coller main.menu.edit.delete = Effacer main.menu.edit.resize = Redimensionner... main.menu.edit.resize.desc = Redimensionner certaines parties de la fusée +main.menu.edit.editpreset= Edit Component Preset File main.menu.edit.preferences = Préférences main.menu.edit.preferences.desc = Configure les préférences de l'application @@ -1269,7 +1275,7 @@ Streamer.Streamer = Streamer Sleeve.Sleeve = Sleeve !Rocket -Rocket.motorCount.Nomotor = [aucun moteurs] +Rocket.motorCount.Nomotor = aucun moteurs Rocket.compname.Rocket = Fusée !MotorMount @@ -1331,6 +1337,15 @@ TCurveMotorCol.TYPE = Type TCurveMotorCol.DIAMETER = Diamètre TCurveMotorCol.LENGTH = Longueur +TCurveMotor.ttip.diameter = Diamètre: +TCurveMotor.ttip.length = Longeur: +TCurveMotor.ttip.maxThrust = Poussée Maximum: +TCurveMotor.ttip.avgThrust = Poussée moyenne: +TCurveMotor.ttip.burnTime = Durée de la combustion: +TCurveMotor.ttip.totalImpulse = Impulsion Totale: +TCurveMotor.ttip.launchMass = Masse au décolage: +TCurveMotor.ttip.emptyMass = Masse à vide: + ! RocketInfo RocketInfo.lengthLine.Length = Longueur RocketInfo.lengthLine.maxdiameter = , diamètre max. @@ -1716,6 +1731,7 @@ ComponentPresetChooserDialog.menu.sortAsc = Trier par ordre croissant ComponentPresetChooserDialog.menu.sortDesc = Trier par ordre dé-croissant ComponentPresetChooserDialog.menu.units = Unités ComponentPresetChooserDialog.checkbox.showAllCompatible = Montrer tous ceux qui sont compatibles +ComponentPresetChooserDialog.lbl.favorites = Select to add preset to drop-down menu table.column.Favorite = Préférée table.column.Manufacturer = Frabriquant table.column.PartNo = Réference de la pièce diff --git a/core/resources/l10n/messages_it.properties b/core/resources/l10n/messages_it.properties index 1d2c03661..45c49e561 100644 --- a/core/resources/l10n/messages_it.properties +++ b/core/resources/l10n/messages_it.properties @@ -173,14 +173,14 @@ debuglogdlg.lbl.Stacktrace = traccia della memoria: ! Edit Motor configuration dialog -edtmotorconfdlg.but.removemotor = Rimuovi il motore -edtmotorconfdlg.but.Selectmotor = Seleziona il motore +MotorConfigurationPanel.btn.removeMotor = Rimuovi il motore +MotorConfigurationPanel.btn.selectMotor = Seleziona il motore edtmotorconfdlg.but.Removeconfiguration = Rimuovi la configurazione edtmotorconfdlg.but.Newconfiguration = Nuova configurazione -edtmotorconfdlg.lbl.Motormounts = Alloggiamenti motore: +MotorConfigurationPanel.lbl.motorMounts = Alloggiamenti motore: edtmotorconfdlg.title.Editmotorconf = Modifica la configurazione del motore edtmotorconfdlg.selectcomp = Seleziona quali componenti fungono da alloggiamenti per il motore: -edtmotorconfdlg.lbl.Motorconfig = Configurazione dei motori: +MotorConfigurationPanel.lbl.motorConfiguration = Configurazione dei motori: edtmotorconfdlg.lbl.Configname = Nome della configurazione: edtmotorconfdlg.lbl.Leavenamedefault = Lascia il campo vuoto per il nome prestabilito. @@ -1220,7 +1220,7 @@ ShockCord.ShockCord = Shock cord Bulkhead.Bulkhead = Paratia !Rocket -Rocket.motorCount.Nomotor = [Nessun motore] +Rocket.motorCount.Nomotor = Nessun motore Rocket.compname.Rocket = Razzo !MotorMount diff --git a/core/resources/l10n/messages_ja.properties b/core/resources/l10n/messages_ja.properties new file mode 100644 index 000000000..635895bb1 --- /dev/null +++ b/core/resources/l10n/messages_ja.properties @@ -0,0 +1,1753 @@ +# +# Japanese base translation file +# +# Should you need to add new logical keys here is the proposed method +# +# className.ComponantType.componantName +# + + + +! Set to the name of the current translation file (used for debugging purposes) +debug.currentFile = messages_ja.properties + +! RocketActions +RocketActions.checkbox.Donotaskmeagain = \u6B21\u56DE\u304B\u3089\u8868\u793A\u3057\u306A\u3044 +RocketActions.lbl.Youcanchangedefop = \u8A2D\u5B9A\u306E\u4E2D\u3067\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u5909\u66F4\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059 +RocketActions.showConfirmDialog.lbl1 = \u9078\u629E\u3057\u305F\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u3092\u6D88\u53BB\u3057\u307E\u3059\u304B\uFF1F +RocketActions.showConfirmDialog.lbl2 = \u6D88\u53BB\u3057\u305F\u3089\u5FA9\u5143\u3067\u304D\u307E\u305B\u3093 +RocketActions.showConfirmDialog.title = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u524A\u9664 +RocketActions.DelCompAct.Delete = \u524A\u9664 +RocketActions.DelCompAct.ttip.Delete = \u9078\u629E\u3057\u305F\u90E8\u54C1\u306E\u524A\u9664 +RocketActions.DelSimuAct.Delete = \u524A\u9664 +RocketActions.DelSimuAct.ttip.Delete = \u9078\u629E\u3057\u305F\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u524A\u9664 +RocketActions.DelAct.Delete = \u524A\u9664 +RocketActions.DelAct.ttip.Delete = \u9078\u629E\u3057\u305F\u90E8\u54C1\u3084\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u524A\u9664 +RocketActions.CutAction.Cut = \u5207\u308A\u53D6\u308A +RocketActions.CutAction.ttip.Cut = \u3053\u306E\u56F3\u9762\u304B\u3089\u90E8\u54C1\u3084\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u3092\u5207\u308A\u53D6\u308A +RocketActions.CopyAct.Copy = \u30B3\u30D4\u30FC +RocketActions.CopyAct.ttip.Copy = \u90E8\u54C1\u3068\u30AF\u30EA\u30C3\u30D7\u30DC\u30FC\u30C9\u306B\u30B3\u30D4\u30FC +RocketActions.PasteAct.Paste = \u8CBC\u308A\u4ED8\u3051 +RocketActions.PasteAct.ttip.Paste = \u90E8\u54C1\u3084\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u3092\u8CBC\u308A\u4ED8\u3051 +RocketActions.EditAct.Edit = \u7DE8\u96C6 +RocketActions.EditAct.ttip.Edit = \u9078\u629E\u3057\u305F\u90E8\u54C1\u306E\u7DE8\u96C6 +RocketActions.NewStageAct.Newstage = \u65B0\u3057\u3044\u30B9\u30C6\u30FC\u30B8 +RocketActions.NewStageAct.ttip.Newstage = \u30ED\u30B1\u30C3\u30C8\u306B\u65B0\u3057\u3044\u30B9\u30C6\u30FC\u30B8\u3092\u8FFD\u52A0\u3059\u308B +RocketActions.ActBoosterstage = \u30D6\u30FC\u30B9\u30BF\u30FC\u30B9\u30C6\u30FC\u30B8 +RocketActions.MoveUpAct.Moveup = \u4E0A\u306B\u79FB\u52D5 +RocketActions.MoveUpAct.ttip.Moveup = \u90E8\u54C1\u3092\u4E0A\u306E\u968E\u5C64\u306B\u79FB\u52D5 +RocketActions.MoveDownAct.Movedown = \u4E0B\u306B\u79FB\u52D5 +RocketActions.MoveDownAct.ttip.Movedown = \u90E8\u54C1\u3092\u4E0B\u306E\u968E\u5C64\u306B\u79FB\u52D5 + +! RocketPanel +RocketPanel.FigTypeAct.Sideview = \u5074\u9762\u56F3 +RocketPanel.FigTypeAct.ttip.Sideview = \u5074\u9762\u56F3 +RocketPanel.FigTypeAct.Backview = \u80CC\u9762\u56F3 +RocketPanel.FigTypeAct.ttip.Backview = \u5F8C\u308D\u304B\u3089\u306E\u56F3 +RocketPanel.FigViewAct.2D = 2D View +RocketPanel.FigViewAct.ttip.2D = 2D View +RocketPanel.FigViewAct.3D = 3D View +RocketPanel.FigViewAct.ttip.3D = 3D View +RocketPanel.lbl.Motorcfg = \u30E2\u30FC\u30BF\u30FC\u69CB\u6210\uFF1A +RocketPanel.lbl.infoMessage = \u30AF\u30EA\u30C3\u30AF\u3067\u9078\u629E    Shift+\u30AF\u30EA\u30C3\u30AF\u3067\u4ED6\u306E\u3092\u9078\u629E    \u30C0\u30D6\u30EB\u30AF\u30EA\u30C3\u30AF\u3067\u7DE8\u96C6    \u30AF\u30EA\u30C3\u30AF\uFF0B\u30C9\u30E9\u30C3\u30B0\u3067\u79FB\u52D5 + + +! BasicFrame +BasicFrame.tab.Rocketdesign = \u30ED\u30B1\u30C3\u30C8\u30C7\u30B6\u30A4\u30F3 +BasicFrame.tab.Flightsim = \u30D5\u30E9\u30A4\u30C8\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3 +BasicFrame.title.Addnewcomp = \u65B0\u3057\u3044\u90E8\u54C1\u306E\u8FFD\u52A0 +BasicFrame.dlg.lbl1 = \u30C7\u30B6\u30A4\u30F3 ' +BasicFrame.dlg.lbl2 = \u4FDD\u5B58\u3055\u308C\u3066\u3044\u307E\u305B\u3093 +BasicFrame.dlg.lbl3 = \u4FDD\u5B58\u3057\u307E\u3059\u304B\uFF1F +BasicFrame.dlg.title = \u30C7\u30B6\u30A4\u30F3\u306F\u4FDD\u5B58\u3055\u308C\u3066\u3044\u307E\u305B\u3093 +BasicFrame.StageName.Sustainer = \u30B5\u30B9\u30C6\u30CA\u30FC +BasicFrame.WarningDialog.txt1 = \u4EE5\u4E0B\u306E\u30A8\u30E9\u30FC\u304C\u8D77\u3053\u308A\u307E\u3057\u305F +BasicFrame.WarningDialog.txt2 = \u3044\u304F\u3064\u304B\u306E\u30C7\u30B6\u30A4\u30F3\u306E\u8981\u7D20\u304C\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F +BasicFrame.WarningDialog.title = \u958B\u3044\u3066\u3044\u308B\u30D5\u30A1\u30A4\u30EB\u306E\u8B66\u544A + + +! General error messages used in multiple contexts +error.fileExists.title = \u30D5\u30A1\u30A4\u30EB\u306E\u5B58\u5728 +error.fileExists.desc = \u30D5\u30A1\u30A4\u30EB '{filename}' \u306F\u65E2\u306B\u5B58\u5728\u3057\u3066\u3044\u307E\u3059\u3002\u4E0A\u66F8\u304D\u3057\u307E\u3059\u304B\uFF1F + +error.writing.title = \u30D5\u30A1\u30A4\u30EB\u306E\u66F8\u304D\u51FA\u3057\u30A8\u30E9\u30FC +error.writing.desc = \u30D5\u30A1\u30A4\u30EB\u306B\u66F8\u304D\u8FBC\u307F\u4E2D\u306B\u30A8\u30E9\u30FC\uFF1A + + +! Labels used in buttons of dialog windows +# TODO: Rename these to "btn.xxx" +button.ok = OK +button.cancel = \u30AD\u30E3\u30F3\u30BB\u30EB +button.close = \u9589\u3058\u308B + +! Common labels used in buttons of dialog windows +dlg.but.ok = OK +dlg.but.cancel = \u30AD\u30E3\u30F3\u30BB\u30EB +dlg.but.close = \u9589\u3058\u308B + +! General file type names +filetypes.pdf = PDF files (*.pdf) +BasicFrame.SimpleFileFilter1 = All rocket designs (*.ork; *.rkt) +BasicFrame.SimpleFileFilter2 = OpenRocket designs (*.ork) +BasicFrame.SimpleFileFilter3 = RockSim designs (*.rkt) +BasicFrame.SimpleFileFilter4 = OpenRocket presets (*.orc) +filetypes.images = \u753B\u50CF\u30D5\u30A1\u30A4\u30EB + + +! About Dialog +AboutDialog.lbl.version = \u30D0\u30FC\u30B8\u30E7\u30F3 +! The texts below provide additional credits for the translation maintainer +! - In AboutDialog.lbl.translation replace "English" with the current language. +! - AboutDialog.lbl.translator is the translator / group name (may be empty) +! - AboutDialog.lbl.translatorWebsite is a URL to the translator / group (may be empty) +! - AboutDialog.lbl.translatorIcon is the file name of an icon under pix/translators/ (may be empty) +AboutDialog.lbl.translation = \u65E5\u672C\u8A9E\u7FFB\u8A33\uFF1A +AboutDialog.lbl.translator = \u7A32\u5DDD\u8CB4\u5927 +AboutDialog.lbl.translatorWebsite = http://www.ina111.org +AboutDialog.lbl.translatorIcon = + + +! Print dialog +PrintDialog.title = \u4FDD\u5B58\u3068\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8 +PrintDialog.but.previewAndPrint = \u30D7\u30EC\u30D3\u30E5\u30FC&\u5370\u5237 +PrintDialog.checkbox.showByStage = Show by stage +PrintDialog.lbl.selectElements = \u5370\u5237\u3059\u308B\u90E8\u54C1\u306E\u9078\u629E\uFF1A +printdlg.but.saveaspdf = PDF\u3067\u4FDD\u5B58 +printdlg.but.preview = \u30D7\u30EC\u30D3\u30E5\u30FC +printdlg.but.settings = \u8A2D\u5B9A +PrintDialog.error.preview.title = \u30D7\u30EC\u30D3\u30E5\u30FC\u304C\u958B\u3051\u307E\u305B\u3093 +PrintDialog.error.preview.desc1 = PDF\u30D7\u30EC\u30D3\u30E5\u30FC\u304C\u958B\u3051\u307E\u305B\u3093 +PrintDialog.error.preview.desc2 = PDF\u3067\u4FDD\u5B58\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044 + +!PrintSettingsDialog +PrintSettingsDialog.title = \u5370\u5237\u8A2D\u5B9A +PrintSettingsDialog.lbl.Templatefillcolor = Template fill color: +PrintSettingsDialog.lbl.Templatebordercolor = Template border color: +PrintSettingsDialog.lbl.Papersize = \u7D19\u30B5\u30A4\u30BA\uFF1A +PrintSettingsDialog.lbl.Paperorientation = \u5370\u5237\u65B9\u5411\uFF1A +PrintSettingsDialog.but.Reset = \u30EA\u30BB\u30C3\u30C8 +PrintSettingsDialog.but.Close = \u9589\u3058\u308B + + +! Bug Report dialog +bugreport.dlg.title = \u30D0\u30B0\u30EC\u30DD\u30FC\u30C8 +bugreport.dlg.but.Sendbugreport = \u30D0\u30B0\u30EC\u30DD\u30FC\u30C8\u3092\u9001\u308B +bugreport.dlg.but.Sendbugreport.Ttip = OpenRocket\u958B\u767A\u8005\u306B\u81EA\u52D5\u7684\u306B\u30D0\u30B0\u30EC\u30DD\u30FC\u30C8\u304C\u9001\u4FE1\u3055\u308C\u307E\u3059 +bugreport.dlg.successmsg1 = \u30D0\u30B0\u30EC\u30DD\u30FC\u30C8\u304C\u9001\u4FE1\u3055\u308C\u307E\u3057\u305F +bugreport.dlg.successmsg2 = OpenRocket\u306E\u6539\u826F\u306B\u5354\u529B\u3042\u308A\u304C\u3068\u3046\u3054\u3056\u3044\u307E\u3059\uFF01 +bugreport.dlg.successmsg3 = \u30D0\u30B0\u30EC\u30DD\u30FC\u30C8\u306E\u9001\u4FE1 +bugreport.dlg.connectedInternet = If connected to the Internet, you can simply click Send bug report. +bugreport.dlg.otherwise = Otherwise, send the text below to the address: +bugreport.lbl.Theinformation = The information above may be included in a public bug report. Make sure it does not contain any sensitive information you do not want to be made public. +bugreport.dlg.failedmsg1 = OpenRocket was unable to send the bug report: +bugreport.dlg.failedmsg2 = Please send the report manually to +bugreport.dlg.failedmsg3 = Error sending report +bugreport.reportDialog.txt = You can report a bug in OpenRocket by filling in and submitting the form below.
You can also report bugs and include attachments on the project web site. +bugreport.reportDialog.txt2 = Please include a short description about what you were doing when the exception occurred. +bugreport.dlg.provideDescription = Please provide a description of the bug first. +bugreport.dlg.provideDescription.title = Bug description missing + + +! Debug log dialog +debuglogdlg.but.clear = Clear +debuglogdlg.OpenRocketdebuglog = OpenRocket debug log +debuglogdlg.Displayloglines = Display log lines: +debuglogdlg.Follow = Follow +debuglogdlg.col.Time = Time +debuglogdlg.col.Level = Level +debuglogdlg.col.Location = Location +debuglogdlg.col.Message = Message +debuglogdlg.lbl.Loglinenbr = Log line number: +debuglogdlg.lbl.Time = Time: +debuglogdlg.lbl.Level = Level: +debuglogdlg.lbl.Location = Location: +debuglogdlg.lbl.Logmessage = Log message: +debuglogdlg.lbl.Stacktrace = Stack trace: + + +! MotorChooserDialog +MotorChooserDialog.title = \u30ED\u30B1\u30C3\u30C8\u30E2\u30FC\u30BF\u30FC\u306E\u9078\u629E + +! Edit Motor configuration dialog +MotorConfigurationPanel.btn.removeMotor = \u30E2\u30FC\u30BF\u30FC\u306E\u524A\u9664 +MotorConfigurationPanel.btn.selectMotor = \u30E2\u30FC\u30BF\u30FC\u306E\u9078\u629E +edtmotorconfdlg.but.Removeconfiguration = \u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u304B\u3089\u524A\u9664 +edtmotorconfdlg.but.Newconfiguration = \u65B0\u3057\u3044\u30E2\u30FC\u30BF\u30FC +MotorConfigurationPanel.lbl.motorMounts = \u30E2\u30FC\u30BF\u30FC\u30DE\u30A6\u30F3\u30C8\uFF1A +edtmotorconfdlg.title.Editmotorconf = \u30E2\u30FC\u30BF\u30FC\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u7DE8\u96C6 +edtmotorconfdlg.selectcomp = \u3069\u306E\u90E8\u54C1\u3092\u30E2\u30FC\u30BF\u30FC\u30DE\u30A6\u30F3\u30C8\u3068\u3059\u308B\u304B\uFF1A +MotorConfigurationPanel.lbl.motorConfiguration = \u30E2\u30FC\u30BF\u30FC\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\uFF1A +edtmotorconfdlg.lbl.Configname = \u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u540D\u524D\uFF1A +edtmotorconfdlg.lbl.Leavenamedefault = Leave name empty for default. + +! Example design dialog +exdesigndlg.but.open = \u958B\u304F +exdesigndlg.lbl.Selectexample = \u30B5\u30F3\u30D7\u30EB\u30C7\u30B6\u30A4\u30F3\u306E\u9078\u629E\uFF1A +exdesigndlg.lbl.Openexampledesign = \u30B5\u30F3\u30D7\u30EB\u30C7\u30B6\u30A4\u30F3\u3092\u958B\u304F +exdesigndlg.lbl.Exampledesignsnotfound = \u30B5\u30F3\u30D7\u30EB\u30C7\u30B6\u30A4\u30F3\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093 +exdesigndlg.lbl.Examplesnotfound = \u30B5\u30F3\u30D7\u30EB\u30C7\u30B6\u30A4\u30F3\u304C\u3042\u308A\u307E\u305B\u3093 + + +! Material edit panel +matedtpan.but.new = \u65B0\u3057\u3044\u6750\u6599 +matedtpan.but.edit = \u7DE8\u96C6 +matedtpan.but.delete = \u524A\u9664 +matedtpan.but.revertall = \u5168\u3066\u5143\u306B\u623B\u3059 +matedtpan.col.Material = \u6750\u6599 +matedtpan.col.Type = \u30BF\u30A4\u30D7 +matedtpan.col.Density = \u5BC6\u5EA6 +matedtpan.col.but.ttip.New = \u65B0\u3057\u3044\u6750\u6599\u306E\u8FFD\u52A0 +matedtpan.title.Addcustmaterial = \u30AB\u30B9\u30BF\u30E0\u3055\u308C\u305F\u6750\u6599\u306E\u8FFD\u52A0 +matedtpan.but.ttip.edit = \u6750\u6599\u306E\u7DE8\u96C6 +matedtpan.title.Editmaterial = \u6750\u6599\u306E\u7DE8\u96C6 +matedtpan.title2.Editmaterial = \u7D44\u307F\u8FBC\u307E\u308C\u305F\u6750\u6599\u306F\u5909\u66F4\u3067\u304D\u307E\u305B\u3093 +matedtpan.but.ttip.delete = \u30E6\u30FC\u30B6\u30FC\u5B9A\u7FA9\u306E\u6750\u6599\u3092\u524A\u9664 +matedtpan.but.ttip.revertall = \u30E6\u30FC\u30B6\u30FC\u5B9A\u7FA9\u306E\u6750\u6599\u3092\u5168\u3066\u524A\u9664 +matedtpan.title.Deletealluser-defined = \u30E6\u30FC\u30B6\u30FC\u5B9A\u7FA9\u306E\u6750\u6599\u3092\u5168\u3066\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F +matedtpan.title.Revertall = \u5168\u3066\u5143\u306B\u623B\u3057\u307E\u3059\u304B\uFF1F +matedtpan.lbl.edtmaterials = \u6750\u6599\u306E\u7DE8\u96C6\u306F\u65E2\u5B58\u306E\u30ED\u30B1\u30C3\u30C8\u30C7\u30B6\u30A4\u30F3\u306B\u306F\u5909\u66F4\u304C\u5F71\u97FF\u3057\u307E\u305B\u3093 + +!MaterialModel +MaterialModel.title.Material = \u6750\u6599 +MaterialModel.title.Defcustmat = \u30AB\u30B9\u30BF\u30E0\u6750\u6599\u306E\u5B9A\u7FA9 + + +! Preference dialog +pref.dlg.but.add = \u8FFD\u52A0 +pref.dlg.but.reset = \u30EA\u30BB\u30C3\u30C8 +pref.dlg.but.checknow = \u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u306E\u78BA\u8A8D +pref.dlg.but.openlast = \u8D77\u52D5\u6642\u306B\u524D\u56DE\u958B\u3044\u3066\u3044\u305F\u30C7\u30B6\u30A4\u30F3\u30D5\u30A1\u30A4\u30EB\u3092\u958B\u304F +pref.dlg.but.defaultmetric = \u30E1\u30FC\u30C8\u30EB\u5358\u4F4D\u7CFB +pref.dlg.but.defaultimperial = \u82F1\u30DD\u30F3\u30C9\u30FB\u30E4\u30FC\u30C9\u5358\u4F4D\u7CFB +pref.dlg.title.Preferences = \u8A2D\u5B9A +pref.dlg.tab.Units = \u5358\u4F4D\u7CFB +pref.dlg.tab.Defaultunits = \u30C7\u30D5\u30A9\u30EB\u30C8\u5358\u4F4D\u7CFB +pref.dlg.tab.Materials = \u6750\u6599 +pref.dlg.tab.Custommaterials = \u30AB\u30B9\u30BF\u30E0\u6750\u6599 +pref.dlg.tab.Options = \u30AA\u30D7\u30B7\u30E7\u30F3 +pref.dlg.tab.Miscellaneousoptions = \u305D\u306E\u4ED6\u306E\u30AA\u30D7\u30B7\u30E7\u30F3 +pref.dlg.lbl.Positiontoinsert = \u90E8\u54C1\u3092\u8FFD\u52A0\u3057\u305F\u6642\u306E\u4F4D\u7F6E +pref.dlg.lbl.Confirmdeletion = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u524A\u9664\u6642\u306E\u78BA\u8A8D +pref.dlg.lbl.User-definedthrust = \u30E6\u30FC\u30B6\u30FC\u5B9A\u7FA9\u306E\u30B9\u30E9\u30B9\u30C8\u30AB\u30FC\u30D6\uFF1A +pref.dlg.lbl.Windspeed = \u98A8\u901F +pref.dlg.Allthrustcurvefiles = All thrust curve files (*.eng; *.rse; *.zip; directories) +pref.dlg.RASPfiles = RASP motor files (*.eng) +pref.dlg.RockSimfiles = RockSim engine files (*.rse) +pref.dlg.ZIParchives = ZIP archives (*.zip) +pref.dlg.checkbox.Checkupdates = \u958B\u59CB\u6642\u306B\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u306E\u78BA\u8A8D\u3092\u3059\u308B +pref.dlg.ttip.Checkupdatesnow = \u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u306E\u78BA\u8A8D +pref.dlg.lbl.Selectprefunits = \u5358\u4F4D\u7CFB\u306E\u9078\u629E\uFF1A +pref.dlg.lbl.Rocketdimensions = \u5BF8\u6CD5\uFF1A +pref.dlg.lbl.Linedensity = \u7DDA\u5BC6\u5EA6\uFF1A +pref.dlg.lbl.Motordimensions = \u30E2\u30FC\u30BF\u30FC\u5BF8\u6CD5\uFF1A +pref.dlg.lbl.Surfacedensity = \u9762\u5BC6\u5EA6\uFF1A +pref.dlg.lbl.Distance = \u8DDD\u96E2\uFF1A +pref.dlg.lbl.Bulkdensity = \u5BC6\u5EA6\uFF1A +pref.dlg.lbl.Velocity = \u901F\u5EA6\uFF1A +pref.dlg.lbl.Surfaceroughness = \u8868\u9762\u3042\u3089\u3055\uFF1A +pref.dlg.lbl.Acceleration = \u52A0\u901F\u5EA6\uFF1A +pref.dlg.lbl.Area = \u9762\u7A4D\uFF1A +pref.dlg.lbl.Mass = \u8CEA\u91CF\uFF1A +pref.dlg.lbl.Angle = \u89D2\u5EA6\uFF1A +pref.dlg.lbl.Force = \u529B\uFF1A +pref.dlg.lbl.Rollrate = \u89D2\u901F\u5EA6\uFF1A +pref.dlg.lbl.Totalimpulse = \u30C8\u30FC\u30BF\u30EB\u30A4\u30F3\u30D1\u30EB\u30B9\uFF1A +pref.dlg.lbl.Temperature = \u6E29\u5EA6\uFF1A +pref.dlg.lbl.Momentofinertia = \u6163\u6027\u30E2\u30FC\u30E1\u30F3\u30C8\uFF1A +pref.dlg.lbl.Pressure = \u6C17\u5727\uFF1A +pref.dlg.lbl.Stability = \u5B89\u5B9A\u6027\uFF1A +pref.dlg.lbl.FlightTime = \u98DB\u7FD4\u6642\u9593\uFF1A +pref.dlg.lbl.effect1 = \u5909\u66F4\u306F\u30BD\u30D5\u30C8\u306E\u518D\u8D77\u52D5\u6642\u306B\u6709\u52B9\u306B\u306A\u308A\u307E\u3059 +pref.dlg.lbl.Checkingupdates = \u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u306E\u78BA\u8A8D\u4E2D\u2026 +pref.dlg.lbl.msg1 = \u30B5\u30FC\u30D0\u30FC\u3068\u306E\u901A\u4FE1\u30A8\u30E9\u30FC +pref.dlg.lbl.msg2 = \u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u60C5\u5831\u306E\u8AAD\u307F\u51FA\u3057\u304C\u3067\u304D\u307E\u305B\u3093 +pref.dlg.lbl.msg3 = \u3053\u306EOpenRocket\u306F\u6700\u65B0\u7248\u3067\u3059 +pref.dlg.lbl.msg4 = \u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u304C\u5229\u7528\u3067\u304D\u307E\u305B\u3093 +pref.dlg.PrefChoiseSelector1 = \u5E38\u306B\u78BA\u8A8D +pref.dlg.PrefChoiseSelector2 = \u4E2D\u5FC3\u306B\u8FFD\u52A0 +pref.dlg.PrefChoiseSelector3 = \u7AEF\u306B\u8FFD\u52A0 +pref.dlg.PrefBooleanSelector1 = \u524A\u9664 +pref.dlg.PrefBooleanSelector2 = \u78BA\u8A8D +pref.dlg.Add = \u8FFD\u52A0 +pref.dlg.DescriptionArea.Adddirectories = \u30D5\u30A9\u30EB\u30C0, RASP motor files (*.eng), RockSim engine files (*.rse) \u3082\u3057\u304F\u306F ZIP archives \u3092\u30BB\u30DF\u30B3\u30ED\u30F3(;)\u306B\u3088\u3063\u3066\u5206\u3051\u3089\u308C\u305F\u5F62\u3067\u8FFD\u52A0\u306E\u63A8\u529B\u5C65\u6B74\u3068\u3057\u3066\u8FFD\u52A0\u3067\u304D\u307E\u3059\u3002\u3053\u306E\u5909\u66F4\u306FOpenRocket\u306E\u518D\u8D77\u52D5\u6642\u306B\u6709\u52B9\u306B\u306A\u308A\u307E\u3059 + +PreferencesDialog.lbl.language = \u8A00\u8A9E\uFF1A +PreferencesDialog.languages.default = \u30B7\u30B9\u30C6\u30E0\u8A00\u8A9E +PreferencesDialog.lbl.languageEffect = \u8A00\u8A9E\u306F\u518D\u8D77\u52D5\u6642\u306B\u5909\u66F4\u3055\u308C\u307E\u3059 + +! Simulation edit dialog +simedtdlg.but.runsimulation = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u5B9F\u884C +simedtdlg.but.resettodefault = \u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u623B\u3059 +simedtdlg.but.add = \u8FFD\u52A0 +simedtdlg.but.remove = \u524A\u9664 +simedtdlg.title.Editsim = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u7DE8\u96C6 +simedtdlg.lbl.Simname = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u540D\u524D\uFF1A +simedtdlg.tab.Launchcond = \u30ED\u30FC\u30F3\u30C1\u30B3\u30F3\u30C7\u30A3\u30B7\u30E7\u30F3 +simedtdlg.tab.Simopt = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u30AA\u30D7\u30B7\u30E7\u30F3 +simedtdlg.tab.Plotdata = \u30C7\u30FC\u30BF\u30D7\u30ED\u30C3\u30C8 +simedtdlg.tab.CustomExpressions = \u30AB\u30B9\u30BF\u30E0\u5F0F +simedtdlg.tab.Exportdata = \u30C7\u30FC\u30BF\u306E\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8 +simedtdlg.lbl.Motorcfg = \u30E2\u30FC\u30BF\u30FC\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\uFF1A +simedtdlg.lbl.ttip.Motorcfg = \u4F7F\u7528\u3059\u308B\u30E2\u30FC\u30BF\u30FC\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u9078\u629E +simedtdlg.combo.ttip.motorconf = \u4F7F\u7528\u3059\u308B\u30E2\u30FC\u30BF\u30FC\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u9078\u629E +simedtdlg.lbl.Wind = \u98A8 +simedtdlg.lbl.Averwindspeed = \u5E73\u5747\u98A8\u901F\uFF1A +simedtdlg.lbl.ttip.Averwindspeed = \u5BFE\u5730\u306E\u5E73\u5747\u98A8\u901F +simedtdlg.lbl.Stddeviation = \u6A19\u6E96\u504F\u5DEE\uFF1A +simedtdlg.lbl.ttip.Stddeviation = \u98A8\u901F\u306E\u6A19\u6E96\u504F\u5DEE
\u98A8\u901F\u306F\u5E73\u5747\u304B\u3089\u6A19\u6E96\u504F\u5DEE\u306E\u4E8C\u500D\u306E\u7BC4\u56F2\u4EE5\u5185\u306B\u53CE\u307E\u308A\u307E\u3059 +simedtdlg.lbl.Turbulenceintensity = \u4E71\u308C\u5F37\u3055\uFF1A +simedtdlg.lbl.ttip.Turbulenceintensity1 = \u4E71\u308C\u5F37\u3055\u306F\u5E73\u5747\u98A8\u901F\u304B\u3089\u306E\u6A19\u6E96\u504F\u5DEE\u306E\u5024\u306B\u306A\u308A\u307E\u3059
+simedtdlg.lbl.ttip.Turbulenceintensity2 = \u6A19\u6E96\u7684\u306A\u5024\u306F +simedtdlg.lbl.ttip.Turbulenceintensity3 = \u304B\u3089 +simedtdlg.border.Atmoscond = \u5927\u6C17\u306E\u30B3\u30F3\u30C7\u30A3\u30B7\u30E7\u30F3 +simedtdlg.checkbox.InterStdAtmosphere = \u56FD\u969B\u6A19\u6E96\u5927\u6C17\u306E\u4F7F\u7528 +simedtdlg.checkbox.ttip.InterStdAtmosphere1 = \u56FD\u969B\u6A19\u6E96\u5927\u6C17\u3092\u9078\u629E
\u3053\u306E\u30E2\u30C7\u30EB\u306F\u6E29\u5EA6 +simedtdlg.checkbox.ttip.InterStdAtmosphere2 = \u6C17\u5727 +simedtdlg.checkbox.ttip.InterStdAtmosphere3 = \u6D77\u9762\u6C17\u5727 +simedtdlg.lbl.Temperature = \u6E29\u5EA6\uFF1A +simedtdlg.lbl.ttip.Temperature = \u767A\u5C04\u70B9\u3067\u306E\u6E29\u5EA6 +simedtdlg.lbl.Pressure = \u6C17\u5727\uFF1A +simedtdlg.lbl.ttip.Pressure = \u767A\u5C04\u70B9\u3067\u306E\u6C17\u5727 +simedtdlg.lbl.Launchsite = \u767A\u5C04\u70B9 +simedtdlg.lbl.Latitude = \u7DEF\u5EA6\uFF1A +simedtdlg.lbl.ttip.Latitude = \u767A\u5C04\u70B9\u306E\u7DEF\u5EA6\u306F\u91CD\u529B\u52A0\u901F\u5EA6\u306B\u5F71\u97FF\u3059\u308B
\u30D7\u30E9\u30B9\u306E\u5024\u306F\u5317\u7DEF\uFF0C\u30DE\u30A4\u30CA\u30B9\u306F\u5357\u7DEF + +simedtdlg.lbl.Longitude = \u7D4C\u5EA6\uFF1A +simedtdlg.lbl.ttip.Longitude = \u30E2\u30C7\u30EB\u306E\u4E88\u6E2C\u3068\u8A55\u4FA1\u306E\u305F\u3081\u306B\u5FC5\u8981\u3067\u3059\u3002 + +simedtdlg.lbl.Altitude = \u9AD8\u5EA6\uFF1A +simedtdlg.lbl.ttip.Altitude = \u767A\u5C04\u70B9\u306E\u6D77\u9762\u9AD8\u5EA6
\u3053\u308C\u306F\u30ED\u30B1\u30C3\u30C8\u306E\u4F4D\u7F6E\u306B\u3088\u308B\u5927\u6C17\u5727\u30E2\u30C7\u30EB\u306B\u5F71\u97FF\u3092\u4E0E\u3048\u308B +simedtdlg.border.Launchrod = \u30ED\u30FC\u30F3\u30C1\u30ED\u30C3\u30C9 +simedtdlg.lbl.Length = \u9577\u3055\uFF1A +simedtdlg.lbl.ttip.Length = \u30ED\u30FC\u30F3\u30C1\u30ED\u30C3\u30C9\u306E\u9577\u3055\uFF1A +simedtdlg.lbl.Angle = \u89D2\u5EA6\uFF1A +simedtdlg.lbl.ttip.Angle = \u5782\u76F4\u304B\u3089\u306E\u30ED\u30FC\u30F3\u30C1\u30ED\u30C3\u30C9\u306E\u89D2\u5EA6 +simedtdlg.lbl.Direction = \u65B9\u5411 +simedtdlg.lbl.ttip.Direction1 = \u98A8\u306B\u5BFE\u3057\u3066\u306E\u767A\u5C04\u65B9\u5411
+simedtdlg.lbl.ttip.Direction2 = \uFF1D\u98A8\u4E0A\u65B9\u5411 +simedtdlg.lbl.ttip.Direction3 = \uFF1D\u98A8\u4E0B\u65B9\u5411 +simedtdlg.border.Simopt = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u30AA\u30D7\u30B7\u30E7\u30F3 +simedtdlg.lbl.Calcmethod = \u8A08\u7B97\u624B\u6CD5 +simedtdlg.lbl.ttip.Calcmethod = \u62E1\u5F35Barrowman\u624B\u6CD5\u306F\u591A\u304F\u306E\u90E8\u54C1\u306B\u5BFE\u5FDC\u3059\u308B\u3088\u3046
\u62E1\u5F35\u3055\u308C\u305FBarrowman\u65B9\u7A0B\u5F0F\u306B\u3088\u3063\u3066\u7A7A\u529B\u8A08\u7B97\u3055\u308C\u308B +simedtdlg.lbl.ExtBarrowman = \u62E1\u5F35Barrowman +simedtdlg.lbl.Simmethod = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u624B\u6CD5\uFF1A +simedtdlg.lbl.ttip.Simmethod1 = 6\u81EA\u7531\u5EA6\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306F\u98DB\u7FD4\u4E2D\u306E\u30ED\u30B1\u30C3\u30C8\u306E\u81EA\u7531\u5EA6\u306E\u7DCF\u6570\u3092\u6E80\u8DB3\u3059\u308B
+simedtdlg.lbl.ttip.Simmethod2 = \u6570\u5024\u7A4D\u5206\u306F4\u6B21\u306E\u30EB\u30F3\u30B2=\u30AF\u30C3\u30BF\u6CD5\u306B\u3088\u308B +simedtdlg.lbl.GeodeticMethod = \u8A08\u7B97\u6E2C\u5730\u7CFB\uFF1A +simedtdlg.lbl.ttip.GeodeticMethodTip = \u5730\u7403\u306E\u5EA7\u6A19\u7CFB\u8A08\u7B97\u306B\u95A2\u4FC2\u3057\u307E\u3059\u3002\u30B3\u30EA\u30AA\u30EA\u529B\u304C\u8003\u616E\u3055\u308C\u307E\u3059\u3002 +simedtdlg.lbl.Timestep = \u6642\u9593\u30B9\u30C6\u30C3\u30D7\uFF1A +simedtdlg.lbl.ttip.Timestep1 = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u6642\u9593\u523B\u307F
\u6642\u9593\u30B9\u30C6\u30C3\u30D7\u3092\u5C0F\u3055\u304F\u3059\u308B\u3068\u6B63\u78BA\u306B\u306A\u308B\u304C\u8A08\u7B97\u304C\u9045\u304F\u306A\u308B
+simedtdlg.lbl.ttip.Timestep2 = 4\u6B21\u306E\u30EB\u30F3\u30B2=\u30AF\u30C3\u30BF\u6CD5\u3067\u5341\u5206\u306A\u7CBE\u5EA6\u3092\u3082\u3064\u6642\u9593\u30B9\u30C6\u30C3\u30D7\u306F +simedtdlg.but.ttip.resettodefault = \u6642\u9593\u30B9\u30C6\u30C3\u30D7\u3092\u30C7\u30D5\u30A9\u30EB\u30C8\u5024\u306B\u30EA\u30BB\u30C3\u30C8( +simedtdlg.border.Simlist = Simulator listeners +simedtdlg.txt.longA1 = Simulation listeners \u3053\u308C\u306F\u30E6\u30FC\u30B6\u304C\u66F8\u3044\u305F\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u5F85\u53D7\u5F62\u5F0F\u3084\u5BFE\u8A71\u5F62\u5F0F\u306E\u30B3\u30FC\u30C9\u3092\u4F7F\u7528\u53EF\u80FD\u306B\u3059\u308B\u9AD8\u5EA6\u306A\u6A5F\u80FD\u3067\u3059 +simedtdlg.txt.longA2 = simulation listeners\u306E\u8A73\u7D30\u306FOpenRocket\u306E\u30C6\u30AF\u30CB\u30AB\u30EB\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044 +simedtdlg.lbl.Curlist = Current listeners: +simedtdlg.lbl.Addsimlist = Add simulation listener +simedtdlg.lbl.Noflightdata = No flight data available. +simedtdlg.lbl.runsimfirst = Please run the simulation first. +simedtdlg.chart.Simflight = Simulated flight +simedtdlg.dlg.Simres = Simulation results +simedtdlg.IntensityDesc.None = None +simedtdlg.IntensityDesc.Verylow = Very low +simedtdlg.IntensityDesc.Low = Low +simedtdlg.IntensityDesc.Medium = Medium +simedtdlg.IntensityDesc.High = High +simedtdlg.IntensityDesc.Veryhigh = Very high +simedtdlg.IntensityDesc.Extreme = Extreme + +GeodeticComputationStrategy.flat.name = \u5E73\u9762\u5730\u7403 +GeodeticComputationStrategy.flat.desc = \u5E73\u9762\u5730\u7403\u8FD1\u4F3C\u3067\u8A08\u7B97\u3057\u307E\u3059\u3002\u4F4E\u9AD8\u5EA6\u3067\u306F\u5341\u5206\u3067\u3059\u3002 +GeodeticComputationStrategy.spherical.name = \u7403\u5F62\u8FD1\u4F3C +GeodeticComputationStrategy.spherical.desc = \u5730\u7403\u3092\u7403\u4F53\u3068\u4EEE\u5B9A\u3057\u6E2C\u5730\u7CFB\u306E\u8A08\u7B97\u3092\u3057\u307E\u3059\u3002
\u3053\u308C\u306F\u307B\u307C\u5168\u3066\u306E\u76EE\u7684\u3067\u5341\u5206\u306A\u7CBE\u5EA6\u304C\u3042\u308A\u307E\u3059\u3002 +GeodeticComputationStrategy.wgs84.name = WGS84\u6955\u5186\u4F53 +GeodeticComputationStrategy.wgs84.desc = Vincenty\u306E\u624B\u6CD5\u306B\u3088\u308BWGS84\u306B\u3088\u308B\u6955\u5186\u4F53\u3068\u3057\u3066\u6E2C\u5730\u7CFB\u306E\u8A08\u7B97\u3092\u3057\u307E\u3059\u3002
\u307B\u3068\u3093\u3069\u306E\u5834\u5408\u3001\u8A08\u7B97\u304C\u9045\u304F\u5FC5\u8981\u3042\u308A\u307E\u305B\u3093\u3002 + + + + +! Simulation Panel +simpanel.but.newsimulation = \u65B0\u898F\u4F5C\u6210 +simpanel.but.editsimulation = \u7DE8\u96C6 +simpanel.but.runsimulations = \u8A08\u7B97 +simpanel.but.deletesimulations = \u524A\u9664 +simpanel.but.plotexport = \u30D7\u30ED\u30C3\u30C8\u30FB\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8 +simpanel.but.ttip.newsimulation = \u65B0\u898F\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u8FFD\u52A0 +simpanel.but.ttip.editsim = \u9078\u629E\u3057\u305F\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u7DE8\u96C6 +simpanel.but.ttip.runsimu = \u9078\u629E\u3057\u305F\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u518D\u8A08\u7B97 +simpanel.but.ttip.deletesim = \u9078\u629E\u3057\u305F\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u524A\u9664 +simpanel.checkbox.donotask = \u6B21\u56DE\u304B\u3089\u8868\u793A\u3057\u306A\u3044 +simpanel.lbl.defpref = \u8A2D\u5B9A\u3067\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u5909\u66F4\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059 +simpanel.dlg.lbl.DeleteSim1 = \u9078\u629E\u3057\u305F\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u3092\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F +simpanel.dlg.lbl.DeleteSim2 = \u3053\u306E\u64CD\u4F5C\u306F\u3084\u308A\u76F4\u305B\u307E\u305B\u3093 +simpanel.dlg.lbl.DeleteSim3 = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u524A\u9664 +simpanel.col.Name = \u540D\u524D +simpanel.col.Motors = \u30E2\u30FC\u30BF\u30FC +simpanel.col.Velocityoffrod = \u30ED\u30FC\u30F3\u30C1\u30ED\u30C3\u30C9\u96E2\u8131\u901F\u5EA6 +simpanel.col.Velocityatdeploy = \u5C55\u958B\u6642\u901F\u5EA6 +simpanel.col.Apogee = \u9060\u5730\u70B9 +simpanel.col.Maxvelocity = \u6700\u5927\u901F\u5EA6 +simpanel.col.Maxacceleration = \u6700\u5927\u52A0\u901F\u5EA6 +simpanel.col.Timetoapogee = \u9060\u5730\u70B9\u306E\u6642\u523B +simpanel.col.Flighttime = \u30D5\u30E9\u30A4\u30C8\u6642\u9593 +simpanel.col.Groundhitvelocity = \u5730\u9762\u885D\u7A81\u901F\u5EA6 +simpanel.ttip.uptodate = Up to date +simpanel.ttip.loaded = Data loaded from a file +simpanel.ttip.outdated = Imported data +simpanel.ttip.notSimulated = Not simulated yet
Click Run simulations to simulate. +simpanel.ttip.noData = No simulation data available. +simpanel.ttip.noWarnings = \u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\u30D5\u30A1\u30A4\u30EB\u3067\u306E\u533A\u5207\u308A\u6587\u5B57
+SimExpPan.lbl.longA2 = ,'\u3092\u4F7F\u3046\u3068Comma Separated Values (CSV) \u30D5\u30A1\u30A4\u30EB\u306B\u306A\u308B +SimExpPan.checkbox.Includesimudesc = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u8AAC\u660E\u3092\u30D5\u30A1\u30A4\u30EB\u306B\u542B\u3081\u308B +SimExpPan.checkbox.ttip.Includesimudesc = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u8AAC\u660E\u3092\u30D5\u30A1\u30A4\u30EB\u306E\u982D\u306B\u30B3\u30E1\u30F3\u30C8\u3057\u3066\u8FFD\u52A0\u3059\u308B +SimExpPan.border.Comments = \u30B3\u30E1\u30F3\u30C8 +SimExpPan.checkbox.Includefielddesc = \u5909\u6570\u306E\u8AAC\u660E\u3092\u30D5\u30A1\u30A4\u30EB\u306B\u542B\u3081\u308B +SimExpPan.checkbox.ttip.Includefielddesc = \u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\u3059\u308B\u5909\u6570\u306E\u8AAC\u660E\u3092\u30B3\u30E1\u30F3\u30C8\u30E9\u30A4\u30F3\u3067\u8FFD\u52A0\u3059\u308B +SimExpPan.checkbox.Incflightevents = \u30D5\u30E9\u30A4\u30C8\u30A4\u30D9\u30F3\u30C8\u3092\u542B\u3081\u308B +SimExpPan.checkbox.ttip.Incflightevents = \u30D5\u30E9\u30A4\u30C8\u30A4\u30D9\u30F3\u30C8\u3092\u30B3\u30E1\u30F3\u30C8\u30E9\u30A4\u30F3\u3067\u8FFD\u52A0\u3059\u308B +SimExpPan.lbl.Commentchar = \u30B3\u30E1\u30F3\u30C8\u6587\u5B57\uFF1A +SimExpPan.lbl.ttip.Commentchar = \u30B3\u30E1\u30F3\u30C8\u30E9\u30A4\u30F3\u3068\u3057\u3066\u4F7F\u7528\u3059\u308B\u6587\u5B57 +SimExpPan.but.Exporttofile = \u30A8\u30AF\u30B9\u30DD\u30FC\u30C8 +SimExpPan.Fileexists.desc1 = \u30D5\u30A1\u30A4\u30EB \" +SimExpPan.Fileexists.desc2 = \" \u306F\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059\u3002\u4E0A\u66F8\u304D\u3057\u307E\u3059\u304B\uFF1F +SimExpPan.Fileexists.title = \u30D5\u30A1\u30A4\u30EB\u306E\u4E0A\u66F8\u304D +SimExpPan.ExportingVar.desc1 = Exporting 1 variable out of +SimExpPan.ExportingVar.desc2 = \u30A8\u30AF\u30B9\u30DD\u30FC\u30C8 +SimExpPan.ExportingVar.desc3 = \u5909\u6570\u3001\u5168\u4F53\u306E\u5909\u6570 +SimExpPan.Col.Variable = \u5909\u6570 +SimExpPan.Col.Unit = \u5358\u4F4D + + +CsvOptionPanel.separator.space = SPACE +CsvOptionPanel.separator.tab = TAB + + +! Custom expression general stuff +customExpression.Name = \u540D\u79F0 +customExpression.Symbol = \u30B7\u30F3\u30DC\u30EB +customExpression.Expression = \u5F0F +customExpression.Units = \u5358\u4F4D +customExpression.Operator = \u6F14\u7B97\u5B50 +customExpression.Description = \u8AAC\u660E + +! Custom expression panel +customExpressionPanel.but.NewExpression = \u65B0\u3057\u3044\u5F0F +customExpressionPanel.but.ttip.NewExpression = \u65B0\u3057\u3044\u30AB\u30B9\u30BF\u30E0\u5F0F\u3092\u8FFD\u52A0 +customExpressionPanel.but.Import = \u30A4\u30F3\u30DD\u30FC\u30C8 +customExpressionPanel.but.ttip.Import = .ork\u30D5\u30A1\u30A4\u30EB\u304B\u3089\u30AB\u30B9\u30BF\u30E0\u5F0F\u3092\u30A4\u30F3\u30DD\u30FC\u30C8 +customExpressionPanel.lbl.UpdateNote = \u30B0\u30E9\u30D5\u3092\u5F97\u308B\u305F\u3081\u306B\u306F\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u3092\u8A08\u7B97\u3055\u305B\u3066\u304A\u304B\u306A\u3044\u3068\u3044\u3051\u306A\u3044 +customExpressionPanel.lbl.CalcNote = \u5F0F\u306F\u8868\u793A\u3055\u308C\u305F\u9806\u5E8F\u3067\u8A08\u7B97\u3055\u308C\u308B +customExpressionPanel.lbl.CustomExpressions = \u30AB\u30B9\u30BF\u30E0\u5F0F +customExpression.Units.but.ttip.Remove = \u3053\u306E\u5F0F\u3092\u524A\u9664 +customExpression.Units.but.ttip.Edit = \u3053\u306E\u5F0F\u3092\u7DE8\u96C6 +customExpression.Units.but.ttip.MoveUp = \u5F0F\u3092\u4E0A\u306B\u79FB\u52D5 +customExpression.Units.but.ttip.MoveDown = \u5F0F\u3092\u4E0B\u306B\u79FB\u52D5 + + +! Custom expression builder window +ExpressionBuilderDialog.title = \u5F0F\u30D3\u30EB\u30C0\u30FC +ExpressionBuilderDialog.InsertVariable = \u5909\u6570\u306E\u633F\u5165 +ExpressionBuilderDialog.InsertOperator = \u6F14\u7B97\u5B50\u306E\u633F\u5165 +ExpressionBuilderDialog.led.ttip.Name = \u65E2\u306B\u3042\u308B\u540D\u524D\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093 +ExpressionBuilderDialog.led.ttip.Symbol = \u65E2\u306B\u3042\u308B\u30B7\u30F3\u30DC\u30EB\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093 +ExpressionBuilderDialog.led.ttip.Expression = \u5F0F\u306F\u30B7\u30F3\u30DC\u30EB\u3068\u6F14\u7B97\u5B50\u3092\u4F7F\u3046\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059 +ExpressionBuilderDialog.CopyToOtherSimulations = \u4ED6\u306E\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306B\u30B3\u30D4\u30FC +ExpressionBuilderDialog.CopyToOtherSimulations.ttip = Make a copy of this expression in other simulations in this document.
\u4ED6\u306E\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u4E2D\u306E\u65E2\u5B58\u306E\u5F0F\u306B\u4E0A\u66F8\u304D\u3084\u5909\u66F4\u3092\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044 + +! Custom expression variable selector +CustomVariableSelector.title = Variable Selector + +! Custom operator selector +CustomOperatorSelector.title = Operator Selector + +! Operators +Operator.plus = \u52A0\u7B97 +Operator.minus = \u6E1B\u7B97 +Operator.star = \u639B\u7B97 +Operator.div = \u9664\u7B97 +Operator.mod = \u6CD5 +Operator.pow = \u6307\u6570 +Operator.abs = \u7D76\u5BFE\u5024 +Operator.ceil = \u5C0F\u6570\u5207\u308A\u4E0A\u3052 +Operator.floor = \u5C0F\u6570\u5207\u308A\u4E0B\u3052 +Operator.sqrt = \u30EB\u30FC\u30C8 +Operator.cbrt = \u4E09\u4E57\u6839 +Operator.exp = e^x +Operator.ln = \u81EA\u7136\u5BFE\u6570 +Operator.sin = \u30B5\u30A4\u30F3 +Operator.cos = \u30B3\u30B5\u30A4\u30F3 +Operator.tan = \u30BF\u30F3\u30B8\u30A7\u30F3\u30C8 +Operator.asin = \u30A2\u30FC\u30AF\u30B5\u30A4\u30F3 +Operator.acos = \u30A2\u30FC\u30AF\u30B3\u30B5\u30A4\u30F3 +Operator.atan = \u30A2\u30FC\u30AF\u30BF\u30F3\u30B8\u30A7\u30F3\u30C8 +Operator.hsin = \u30CF\u30A4\u30D1\u30DC\u30EA\u30C3\u30AF\u30B5\u30A4\u30F3 +Operator.hcos = \u30CF\u30A4\u30D1\u30DC\u30EA\u30C3\u30AF\u30B3\u30B5\u30A4\u30F3 +Operator.htan = \u30CF\u30A4\u30D1\u30DC\u30EA\u30C3\u30AF\u30BF\u30F3\u30B8\u30A7\u30F3\u30C8 +Operator.log10 = \u5E9510\u306E\u5BFE\u6570 +Operator.round = \u518D\u8FD1\u508D\u306E\u6574\u6570\u5024 +Operator.random = 0~1\u306E\u30E9\u30F3\u30C0\u30E0\u5024 +Operator.expm1 = exp(x)-1 +Operator.mean = \u5E73\u5747 +Operator.min = \u6700\u5C0F\u5024 +Operator.max = \u6700\u5927\u5024 +Operator.var = \u5206\u6563 +Operator.stdev = \u6A19\u6E96\u504F\u5DEE +Operator.rms = RMS\uFF08\u4E8C\u4E57\u5E73\u5747\u5E73\u65B9\u6839\uFF09 +Operator.lclip = Clips a value (1st parameter) to be no less than a given value (2nd parameter) +Operator.uclip = Clips a value (1st parameter) to be no greater than a given value (2nd parameter) +Operator.binf = Gives the fraction of values in a given range (1st parameter) inside a bin with given lower (2nd parameter) and upper (3rd parameter) bounds +Operator.trapz = \u4E0E\u3048\u3089\u308C\u305F\u7BC4\u56F2\u3092\u53F0\u5F62\u7A4D\u5206 +Operator.tnear = Find the time corresponding to the point in a range (1st parameter) nearest to a given value (2nd parameter) + +! MotorPlot +MotorPlot.title.Motorplot = Motor plot +MotorPlot.but.Select = Select +MotorPlot.Chart.Motorthrustcurve = \u30E2\u30FC\u30BF\u30FC\u63A8\u529B\u5C65\u6B74 +MotorPlot.Chart.Time = Time / s +MotorPlot.Chart.Thrust = Thrust / N +MotorPlot.txt.Designation = \u578B\u756A\uFF1A +MotorPlot.txt.Manufacturer = \u88FD\u9020\u4F1A\u793E\uFF1A +MotorPlot.txt.Type = \u30BF\u30A4\u30D7\uFF1A +MotorPlot.txt.Delays = Delays: +MotorPlot.txt.Comment = \u30B3\u30E1\u30F3\u30C8\uFF1A\n + +! Simulation plot panel +simplotpanel.lbl.Presetplotconf = \u30B0\u30E9\u30D5\u7A2E\u985E\uFF1A +simplotpanel.lbl.Xaxistype = X\u8EF8\uFF1A +simplotpanel.lbl.Unit = \u5358\u4F4D\uFF1A +simplotpanel.lbl.Yaxistypes = Y\u8EF8\uFF1A +simplotpanel.lbl.Flightevents = \u30D5\u30E9\u30A4\u30C8\u30A4\u30D9\u30F3\u30C8\uFF1A +simplotpanel.but.All = \u5168\u3066\u9078\u629E +simplotpanel.but.None = \u5168\u3066\u975E\u9078\u629E +simplotpanel.but.NewYaxisplottype = Y\u8EF8\u8FFD\u52A0 +simplotpanel.but.Plotflight = \u30D7\u30ED\u30C3\u30C8 +simplotpanel.lbl.Axis = \u8EF8\uFF1A +simplotpanel.but.ttip.Removethisplot = \u3053\u306E\u30D7\u30ED\u30C3\u30C8\u3092\u524A\u9664 +simplotpanel.Desc = X\u8EF8\u304C\u6642\u9593\u3067\u306A\u3044\u6642\u306F\u30C7\u30FC\u30BF\u306F\u6642\u9593\u9806\u5E8F\u3067\u30D7\u30ED\u30C3\u30C8\u3055\u308C\u308B +simplotpanel.OptionPane.lbl1 = Y\u8EF8\u306F15\u5909\u6570\u307E\u3067 +simplotpanel.OptionPane.lbl2 = \u8FFD\u52A0\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u306A\u3044 +simplotpanel.AUTO_NAME = \u81EA\u52D5 +simplotpanel.LEFT_NAME = \u5DE6 +simplotpanel.RIGHT_NAME = \u53F3 +simplotpanel.CUSTOM = \u30AB\u30B9\u30BF\u30E0 +SimulationPlotPanel.error.noPlotSelected = Y\u8EF8\u306B\u5909\u6570\u3092\u4E00\u3064\u4EE5\u4E0A\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044 +SimulationPlotPanel.error.noPlotSelected.title = \u30D7\u30ED\u30C3\u30C8\u3059\u308B\u3082\u306E\u304C\u3042\u308A\u307E\u305B\u3093 + +! Component add buttons +compaddbuttons.Bodycompandfinsets = \u30DC\u30C7\u30A3\u90E8\u54C1\u3068\u30D5\u30A3\u30F3 +compaddbuttons.Nosecone = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3 +compaddbuttons.Bodytube = \u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6 +compaddbuttons.Transition = \u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3 +compaddbuttons.Trapezoidal = \u53F0\u5F62\u30D5\u30A3\u30F3 +compaddbuttons.Elliptical = \u6955\u5186\u5F62\u30D5\u30A3\u30F3 +compaddbuttons.Freeform = \u81EA\u7531\u5F62\u30D5\u30A3\u30F3 +compaddbuttons.Launchlug = \u30ED\u30FC\u30F3\u30C1\u30E9\u30B0 +compaddbuttons.Innercomponent = \u30A4\u30F3\u30CA\u30FC\u90E8\u54C1 +compaddbuttons.Innertube = \u30A4\u30F3\u30CA\u30FC\u30C1\u30E5\u30FC\u30D6 +compaddbuttons.Coupler = \u30AB\u30D7\u30E9\u30FC +compaddbuttons.Centeringring = \u30BB\u30F3\u30BF\u30FC\u30EA\u30F3\u30B0\n\u30EA\u30F3\u30B0 +compaddbuttons.Bulkhead = \u30D0\u30EB\u30AF\u30D8\u30C3\u30C9 +compaddbuttons.Engineblock = \u30A8\u30F3\u30B8\u30F3\n\u30D6\u30ED\u30C3\u30AF +compaddbuttons.Massobjects = \u30AA\u30D6\u30B8\u30A7\u30AF\u30C8 +compaddbuttons.Parachute = \u30D1\u30E9\u30B7\u30E5\u30FC\u30C8 +compaddbuttons.Streamer = \u30B9\u30C8\u30EA\u30FC\u30DE\u30FC +compaddbuttons.Shockcord = \u30B7\u30E7\u30C3\u30AF\u30B3\u30FC\u30C9 +compaddbuttons.Masscomponent = \u304A\u3082\u308A +compaddbuttons.Donotaskmeagain = \u6B21\u56DE\u304B\u3089\u8868\u793A\u3057\u306A\u3044 +compaddbuttons.Selectcomppos = \u90E8\u54C1\u306E\u5834\u6240\u306E\u9078\u629E +compaddbuttons.lbl.Youcanchange = \u8A2D\u5B9A\u3067\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u5909\u66F4\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059 +compaddbuttons.lbl.insertcomp = \u90E8\u54C1\u3092\u73FE\u5728\u306E\u90E8\u54C1\u306E\u5F8C\u306B\u633F\u5165\u304B\u3001\u6700\u5F8C\u306E\u90E8\u54C1\u3068\u3057\u3066\u8FFD\u52A0\u304B\uFF1F +compaddbuttons.askPosition.Inserthere = \u3053\u3053\u306B\u633F\u5165 +compaddbuttons.askPosition.Addtotheend = \u6700\u5F8C\u306B\u8FFD\u52A0 +compaddbuttons.askPosition.Cancel = \u30AD\u30E3\u30F3\u30BB\u30EB + +! Component Analysis Dialog +componentanalysisdlg.componentanalysis = \u90E8\u54C1\u89E3\u6790 +componentanalysisdlg.lbl.winddir = \u98A8\u5411\u304D\uFF1A +componentanalysisdlg.TitledBorder.warnings = \u30A8\u30E9\u30FC\uFF1A +componentanalysisdlg.ToggleBut.worst = Worst +componentanalysisdlg.lbl.angleofattack = \u8FCE\u3048\u89D2\uFF1A +componentanalysisdlg.lbl.machnumber = \u30DE\u30C3\u30CF\u6570\uFF1A +componentanalysisdlg.lbl.rollrate = \u89D2\u901F\u5EA6\uFF08\u30ED\u30FC\u30EB\uFF09\uFF1A +componentanalysisdlg.lbl.activestages = Active stages: +componentanalysisdlg.lbl.motorconf = Motor configuration: +componentanalysisdlg.TabStability.Col = \u90E8\u54C1 +componentanalysisdlg.TabStability.Col.CG = CG +componentanalysisdlg.TabStability.Col.Mass = \u8CEA\u91CF +componentanalysisdlg.TabStability.Col.CP = CP +componentanalysisdlg.TabStability = \u5B89\u5B9A\u6027 +componentanalysisdlg.TabStability.ttip = \u5B89\u5B9A\u6027 +componentanalysisdlg.dragTableModel.Col.Component = \u90E8\u54C1 +componentanalysisdlg.dragTableModel.Col.Pressure = \u5727\u529B CD +componentanalysisdlg.dragTableModel.Col.Base = Base CD +componentanalysisdlg.dragTableModel.Col.friction = \u6469\u64E6 CD +componentanalysisdlg.dragTableModel.Col.total = Total CD +componentanalysisdlg.dragTabchar = \u6297\u529B\u6307\u6A19 +componentanalysisdlg.dragTabchar.ttip = \u6297\u529B\u6307\u6A19 +componentanalysisdlg.rollTableModel.Col.component = \u90E8\u54C1 +componentanalysisdlg.rollTableModel.Col.rollforc = \u529B\u4FC2\u6570\uFF08\u30ED\u30FC\u30EB\uFF09 +componentanalysisdlg.rollTableModel.Col.rolldamp = \u6E1B\u8870\u4FC2\u6570\uFF08\u30ED\u30FC\u30EB\uFF09 +componentanalysisdlg.rollTableModel.Col.total = Total Cl +componentanalysisdlg.rollTableModel = Roll dynamics +componentanalysisdlg.rollTableModel.ttip = Roll dynamics +componentanalysisdlg.println.closingmethod = Closing method called: +componentanalysisdlg.println.settingnam = SETTING NAN VALUES +componentanalysisdlg.lbl.reflenght = \u4EE3\u8868\u9577\u3055\uFF1A +componentanalysisdlg.lbl.refarea = \u4EE3\u8868\u9762\u7A4D\uFF1A +!componentanalysisdlg.But.close +componentanalysisdlg.TabStability.Col.Component = \u90E8\u54C1 +componentanalysisdlg.TOTAL = Total +componentanalysisdlg.noWarnings = \u30A8\u30F3\u30B8\u30F3\u30D6\u30ED\u30C3\u30AF\u306F\u30E2\u30FC\u30BF\u30FC\u30DE\u30A6\u30F3\u30C8\u30C1\u30E5\u30FC\u30D6\u306E\u4E2D\u3067\u30E2\u30FC\u30BF\u30FC\u304C\u524D\u306B\u52D5\u304F\u306E\u3092\u6B62\u3081\u308B\u5F79\u5272\u3002

\u30E2\u30FC\u30BF\u30FC\u3092\u8FFD\u52A0\u3059\u308B\u306B\u306F \u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6\u3082\u3057\u304F\u306F\u30A4\u30F3\u30CA\u30FC\u30C1\u30E5\u30FC\u30D6\u3092\u4F5C\u3063\u3066 and mark it as a motor mount in the \u30E2\u30FC\u30BF\u30FC\u30BF\u30D6\u3067\u30E2\u30FC\u30BF\u30FC\u30DE\u30A6\u30F3\u30C8\u3068\u3057\u3066\u30C1\u30A7\u30C3\u30AF\u3059\u308B\u3002 +ringcompcfg.note.desc = \u30E1\u30E2\uFF1A\u30A4\u30F3\u30CA\u30FC\u30C1\u30E5\u30FC\u30D6\u306F\u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6\u306E\u5916\u5074\u306B\u51FA\u306A\u3044\u9650\u308A\u306F\u7A7A\u529B\u3078\u306E\u5F71\u97FF\u306F\u7121\u3044 + + +! Body Tube Config +BodyTubecfg.lbl.Bodytubelength = \u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6\u9577\u3055\uFF1A +BodyTubecfg.lbl.Outerdiameter = \u5916\u5F84\uFF1A +BodyTubecfg.lbl.Innerdiameter = \u5185\u5F84\uFF1A +BodyTubecfg.lbl.Wallthickness = \u8089\u539A\uFF1A +BodyTubecfg.tab.General = \u4E00\u822C +BodyTubecfg.tab.Generalproperties = \u4E00\u822C\u9805\u76EE +BodyTubecfg.tab.Motor = \u30E2\u30FC\u30BF\u30FC +BodyTubecfg.tab.Motormountconf = \u30E2\u30FC\u30BF\u30FC\u30DE\u30A6\u30F3\u30C8\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3 +BodyTubecfg.checkbox.Automatic = \u81EA\u52D5 +BodyTubecfg.checkbox.Filled = \u4E2D\u5B9F + +! FinSetConfig +FinSetConfig.tab.Fintabs = \u30D5\u30A3\u30F3\u30BF\u30D6 +FinSetConfig.tab.Through-the-wall = \u30DC\u30C7\u30A3\u3092\u8CAB\u304F\u30D5\u30A3\u30F3\u30BF\u30D6 +FinSetConfig.but.Converttofreeform = \u81EA\u7531\u5F62\u306B\u5909\u63DB +FinSetConfig.but.Converttofreeform.ttip = \u81EA\u7531\u5F62\u30D5\u30A3\u30F3\u306B\u5909\u63DB +FinSetConfig.Convertfinset = \u5909\u63DB +FinSetConfig.but.Splitfins = \u5206\u5272 +FinSetConfig.but.Splitfins.ttip = \u5225\u3005\u306E\u30D5\u30A3\u30F3\u306B\u5206\u5272 +FinSetConfig.but.AutoCalc = \u81EA\u52D5\u7684\u306B\u8A08\u7B97 +FinSetConfig.lbl.Through-the-wall = \u30DC\u30C7\u30A3\u3092\u8CAB\u304F\u30D5\u30A3\u30F3\u30BF\u30D6 +FinSetConfig.lbl.Tablength = \u30BF\u30D6\u9577\u3055\uFF1A +FinSetConfig.ttip.Tablength = \u30D5\u30A3\u30F3\u30BF\u30D6\u306E\u9577\u3055\uFF1A +FinSetConfig.lbl.Tabheight = \u30BF\u30D6\u9AD8\u3055\uFF1A +FinSetConfig.ttip.Tabheight = \u30B9\u30D1\u30F3\u65B9\u5411\u306E\u30D5\u30A3\u30F3\u30BF\u30D6\u306E\u9AD8\u3055 +FinSetConfig.lbl.Tabposition = \u30BF\u30D6\u4F4D\u7F6E\uFF1A +FinSetConfig.ttip.Tabposition = \u30D5\u30A3\u30F3\u30BF\u30D6\u306E\u4F4D\u7F6E +FinSetConfig.lbl.relativeto = relative to + +!FinMarkingGuide +FinMarkingGuide.lbl.Front = Front + +! MotorDatabaseLoadingDialog +MotorDbLoadDlg.title = Loading motors +MotorDbLoadDlg.Loadingmotors = Loading motors... + +! RocketConfig +RocketCfg.lbl.Designname = \u30C7\u30B6\u30A4\u30F3\u540D\uFF1A +RocketCfg.lbl.Designer = \u8A2D\u8A08\u8005\uFF1A +RocketCfg.lbl.Comments = \u30B3\u30E1\u30F3\u30C8\uFF1A +RocketCfg.lbl.Revisionhistory = \u30EA\u30D3\u30B8\u30E7\u30F3\u5C65\u6B74\uFF1A +RocketCfg.lbl.Material = \u6750\u6599\uFF1A + +! ShockCordConfig +ShockCordCfg.lbl.Shockcordlength = \u30B7\u30E7\u30C3\u30AF\u30B3\u30FC\u30C9\u9577\u3055\uFF1A + +! RocketComponentConfig +RocketCompCfg.lbl.Componentname = \u90E8\u54C1\u540D\uFF1A +RocketCompCfg.ttip.Thecomponentname = \u90E8\u54C1\u306E\u540D\u524D +RocketCompCfg.tab.Override = \u518D\u5B9A\u7FA9 +RocketCompCfg.tab.MassandCGoverride = \u8CEA\u91CF\u3068CG\u3092\u518D\u5B9A\u7FA9\u3059\u308B\u30AA\u30D7\u30B7\u30E7\u30F3 +RocketCompCfg.tab.Figure = \u56F3\u793A +RocketCompCfg.tab.Figstyleopt = \u56F3\u793A\u306E\u30B9\u30BF\u30A4\u30EB\u30AA\u30D7\u30B7\u30E7\u30F3 +RocketCompCfg.tab.Comment = \u30B3\u30E1\u30F3\u30C8 +RocketCompCfg.tab.Specifyacomment = \u90E8\u54C1\u3078\u306E\u30B3\u30E1\u30F3\u30C8\u3092\u8A18\u8FF0 +RocketCompCfg.lbl.Mass = \u8CEA\u91CF\uFF1A +RocketCompCfg.lbl.Componentmass = \u90E8\u54C1\u8CEA\u91CF\uFF1A +RocketCompCfg.lbl.overriddento = (overridden to +RocketCompCfg.lbl.overriddenby = (overridden by +RocketCompCfg.lbl.Componentmaterial = \u90E8\u54C1\u6750\u6599\uFF1A +RocketCompCfg.lbl.Componentfinish = \u90E8\u54C1\u4ED5\u4E0A\u3052\u65B9\uFF1A +RocketCompCfg.lbl.ttip.componentmaterialaffects = \u90E8\u54C1\u6750\u6599\u306F\u91CD\u91CF\u306B\u5F71\u97FF\u3059\u308B +RocketCompCfg.combo.ttip.componentmaterialaffects = \u90E8\u54C1\u6750\u6599\u306F\u91CD\u91CF\u306B\u5F71\u97FF\u3059\u308B +RocketCompCfg.lbl.longA1 = \u4ED5\u4E0A\u3052\u65B9\u306F\u6297\u529B\u4FC2\u6570\u306B\u5F71\u97FF\u3059\u308B
+RocketCompCfg.lbl.longA2 = \u3053\u306E\u5024\u306F\u8868\u9762\u7C97\u3055\u306E\u5E73\u5747\u5024\u3092\u793A\u3059 +RocketCompCfg.but.Setforall = \u5168\u3066\u306B\u9069\u7528 +RocketCompCfg.but.ttip.Setforall = \u3053\u306E\u4ED5\u4E0A\u3052\u65B9\u3092\u5168\u3066\u306E\u90E8\u54C1\u306B\u9069\u7528\u3059\u308B +RocketCompCfg.lbl.Overridemassorcenter = \u8CEA\u91CF\u3084\u91CD\u5FC3\u306E\u518D\u5B9A\u7FA9\u3000 +RocketCompCfg.checkbox.Overridemass = \u8CEA\u91CF\u306E\u518D\u5B9A\u7FA9\uFF1A +RocketCompCfg.checkbox.Overridecenterofgrav = \u91CD\u5FC3\u306E\u518D\u5B9A\u7FA9\uFF1A +RocketCompCfg.checkbox.OverridemassandCG = \u5168\u3066\u306E\u30B5\u30D6\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u306E\u8CEA\u91CF\u3068CG\u3092\u518D\u5B9A\u7FA9\uFF1A +RocketCompCfg.lbl.longB1 = \u518D\u5B9A\u7FA9\u3055\u308C\u305F\u8CEA\u91CF\u306B\u306F\u30E2\u30FC\u30BF\u30FC\u306F\u542B\u307E\u308C\u306A\u3044
+RocketCompCfg.lbl.longB2 = \u91CD\u5FC3\u306F\u90E8\u54C1\u306E\u524D\u65B9\u7AEF\u304B\u3089\u3001\u90E8\u54C1\uFF1A +RocketCompCfg.lbl.Commentsonthe = \u30B3\u30E1\u30F3\u30C8\uFF1A +RocketCompCfg.lbl.Figurestyle = \u30B9\u30BF\u30A4\u30EB\uFF1A +RocketCompCfg.lbl.Componentcolor = \u90E8\u54C1\u306E\u8272\uFF1A +RocketCompCfg.lbl.Choosecolor = \u8272\u3092\u9078\u3076 +RocketCompCfg.checkbox.Usedefaultcolor = \u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u8272\u3092\u4F7F\u3046 +RocketCompCfg.lbl.Complinestyle = \u7DDA\u30B9\u30BF\u30A4\u30EB\uFF1A +RocketCompCfg.but.Saveasdefstyle = \u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u30B9\u30BF\u30A4\u30EB\u3068\u3057\u3066\u4FDD\u5B58 +RocketCompCfg.lbl.Diameter = \u76F4\u5F84\uFF1A +RocketCompCfg.lbl.Length = \u9577\u3055\uFF1A +RocketCompCfg.lbl.Thickness = \u539A\u3055\uFF1A +RocketCompCfg.checkbox.Endcapped = \u7AEF\u306B\u30D5\u30BF\u3092\u3059\u308B +RocketCompCfg.ttip.Endcapped = \u30D5\u30BF\u304C\u3042\u308B\u304B\u3069\u3046\u304B +RocketCompCfg.title.Noseconeshoulder = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u30B7\u30E7\u30EB\u30C0\u30FC +RocketCompCfg.title.Aftshoulder = \u5F8C\u65B9\u30B7\u30E7\u30EB\u30C0\u30FC +RocketCompCfg.border.Foreshoulder = \u524D\u65B9\u30B7\u30E7\u30EB\u30C0\u30FC +!RocketCompCfg.lbl.Length + +! BulkheadConfig +BulkheadCfg.tab.Diameter = \u534A\u5F84\uFF1A +BulkheadCfg.tab.Thickness = \u539A\u3055\uFF1A +BulkheadCfg.tab.General = \u4E00\u822C +BulkheadCfg.tab.Generalproperties = \u4E00\u822C + +!CenteringRingConfig +CenteringRingCfg.tab.Outerdiam = \u5916\u5F84\uFF1A +CenteringRingCfg.tab.Innerdiam = \u5185\u5F84\uFF1A +CenteringRingCfg.tab.Thickness = \u539A\u3055\uFF1A +CenteringRingCfg.tab.General = \u4E00\u822C +CenteringRingCfg.tab.Generalproperties = \u4E00\u822C + +!ComponentConfigDialog +ComponentCfgDlg.configuration = \u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3 +ComponentCfgDlg.configuration1 = +ComponentCfgDlg.Modify = \u5909\u66F4 + +!StageConfig +StageConfig.tab.Separation = \u5206\u96E2 +StageConfig.tab.Separation.ttip = \u30B9\u30C6\u30FC\u30B8\u5206\u96E2\u30AA\u30D7\u30B7\u30E7\u30F3 +StageConfig.separation.lbl.title = \u30B9\u30C6\u30FC\u30B8\u304C\u5206\u96E2\u3059\u308B\u6642\u523B\u306E\u9078\u629E\uFF1A +StageConfig.separation.lbl.plus = \u30D7\u30E9\u30B9 +StageConfig.separation.lbl.seconds = \u79D2 + +!EllipticalFinSetConfig +EllipticalFinSetCfg.Nbroffins = \u30D5\u30A3\u30F3\u306E\u6570\uFF1A +EllipticalFinSetCfg.Rotation = \u56DE\u8EE2\uFF1A +EllipticalFinSetCfg.Fincant = \u50BE\u659C\uFF1A +EllipticalFinSetCfg.Rootchord = \u6839\u672C\u9577\u3055\uFF1A +EllipticalFinSetCfg.Height = \u9AD8\u3055\uFF1A +EllipticalFinSetCfg.Positionrelativeto = \u4F4D\u7F6E\uFF1A +EllipticalFinSetCfg.plus = \u30D7\u30E9\u30B9 +EllipticalFinSetCfg.FincrossSection = \u30D5\u30A3\u30F3\u65AD\u9762\u7A4D\uFF1A +EllipticalFinSetCfg.Thickness = \u539A\u3055\uFF1A +EllipticalFinSetCfg.General = \u4E00\u822C +EllipticalFinSetCfg.Generalproperties = \u4E00\u822C +EllipticalFinSetCfg.ttip.Fincant = \u30DC\u30C7\u30A3\u304B\u3089\u307F\u3066\u30D5\u30A3\u30F3\u306E\u50BE\u3044\u3066\u3044\u308B\u89D2\u5EA6 + +!FreeformFinSetConfig +FreeformFinSetCfg.tab.General = \u4E00\u822C +FreeformFinSetCfg.tab.ttip.General = \u4E00\u822C +FreeformFinSetCfg.tab.Shape = \u5F62\u72B6 +FreeformFinSetCfg.tab.ttip.Finshape = \u30D5\u30A3\u30F3\u5F62\u72B6 +FreeformFinSetCfg.lbl.Numberoffins = \u30D5\u30A3\u30F3\u306E\u6570\uFF1A +FreeformFinSetCfg.lbl.Finrotation = \u56DE\u8EE2\uFF1A +FreeformFinSetCfg.lbl.Fincant = \u50BE\u659C\uFF1A +FreeformFinSetCfg.lbl.ttip.Fincant = \u30DC\u30C7\u30A3\u304B\u3089\u307F\u3066\u30D5\u30A3\u30F3\u306E\u50BE\u3044\u3066\u3044\u308B\u89D2\u5EA6 +FreeformFinSetCfg.lbl.Posrelativeto = \u4F4D\u7F6E\uFF1A +FreeformFinSetCfg.lbl.plus = \u30D7\u30E9\u30B9 +FreeformFinSetCfg.lbl.FincrossSection = \u30D5\u30A3\u30F3\u65AD\u9762\u7A4D\uFF1A +FreeformFinSetCfg.lbl.Thickness = \u539A\u3055\uFF1A +! doubleClick1 + 2 form the message "Double-click to edit", split approximately at the middle +FreeformFinSetConfig.lbl.doubleClick1 = \u30C0\u30D6\u30EB\u30AF\u30EA\u30C3\u30AF\u3067 +FreeformFinSetConfig.lbl.doubleClick2 = \u7DE8\u96C6 +FreeformFinSetConfig.lbl.clickDrag = Click+drag: \u30DD\u30A4\u30F3\u30C8\u306E\u8FFD\u52A0\u3068\u79FB\u52D5 +FreeformFinSetConfig.lbl.ctrlClick = Ctrl+click: \u30DD\u30A4\u30F3\u30C8\u306E\u524A\u9664 +FreeformFinSetConfig.lbl.scaleFin = Scale Fin + + +!InnerTubeConfig +InnerTubeCfg.tab.Motor = \u30E2\u30FC\u30BF\u30FC +InnerTubeCfg.tab.ttip.Motor = \u30E2\u30FC\u30BF\u30FC\u30DE\u30A6\u30F3\u30C8\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3 +InnerTubeCfg.tab.Cluster = \u30AF\u30E9\u30B9\u30BF\u30FC +InnerTubeCfg.tab.ttip.Cluster = \u30AF\u30E9\u30B9\u30BF\u30FC\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3 +InnerTubeCfg.tab.Radialpos = \u534A\u5F84\u65B9\u5411\u4F4D\u7F6E +InnerTubeCfg.tab.ttip.Radialpos = \u534A\u5F84\u65B9\u5411\u4F4D\u7F6E +InnerTubeCfg.lbl.Selectclustercfg = \u30AF\u30E9\u30B9\u30BF\u30FC\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u9078\u629E\uFF1A +InnerTubeCfg.lbl.TubeSep = \u8DDD\u96E2\u9593\u9694\uFF1A +InnerTubeCfg.lbl.ttip.TubeSep = \u30C1\u30E5\u30FC\u30D6\u9593\u306E\u8DDD\u96E2,1.0 = \u63A5\u3057\u3066\u3044\u308B +InnerTubeCfg.lbl.Rotation = \u56DE\u8EE2\uFF1A +InnerTubeCfg.lbl.ttip.Rotation = \u30AF\u30E9\u30B9\u30BF\u30FC\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u56DE\u8EE2\u89D2 +InnerTubeCfg.lbl.Rotangle = \u30AF\u30E9\u30B9\u30BF\u30FC\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u306E\u56DE\u8EE2\u89D2 +InnerTubeCfg.but.Splitcluster = \u30AF\u30E9\u30B9\u30BF\u30FC\u3092\u5206\u96E2 +InnerTubeCfg.lbl.longA1 = \u30AF\u30E9\u30B9\u30BF\u30FC\u3092\u5225\u3005\u306E\u90E8\u54C1\u306B\u5206\u96E2
+InnerTubeCfg.lbl.longA2 = \u30A4\u30F3\u30CA\u30FC\u30C1\u30E5\u30FC\u30D6\u306B\u53D6\u308A\u4ED8\u3051\u8907\u88FD\u3059\u308B +InnerTubeCfg.but.Resetsettings = \u30EA\u30BB\u30C3\u30C8 +InnerTubeCfg.but.ttip.Resetsettings = \u5206\u96E2\u3068\u56DE\u8EE2\u89D2\u3092\u30C7\u30D5\u30A9\u30EB\u30C8\u5024\u306B\u30EA\u30BB\u30C3\u30C8 + +! LaunchLugConfig +LaunchLugCfg.lbl.Length = \u9577\u3055\uFF1A +LaunchLugCfg.lbl.Outerdiam = \u5916\u5F84\uFF1A +LaunchLugCfg.lbl.Innerdiam = \u5185\u5F84\uFF1A +LaunchLugCfg.lbl.Thickness = \u539A\u3055\uFF1A +LaunchLugCfg.lbl.Radialpos = \u56DE\u8EE2\u65B9\u5411\u4F4D\u7F6E\uFF1A +LaunchLugCfg.lbl.Posrelativeto = \u4F4D\u7F6E\uFF1A +LaunchLugCfg.lbl.plus = \u30D7\u30E9\u30B9 +LaunchLugCfg.tab.General = \u4E00\u822C +LaunchLugCfg.tab.Generalprop = \u4E00\u822C + +! MassComponentConfig +MassComponentCfg.lbl.Mass = \u8CEA\u91CF +MassComponentCfg.lbl.Density = \u6982\u7B97\u5BC6\u5EA6\uFF1A +MassComponentCfg.lbl.Length = \u9577\u3055\uFF1A +MassComponentCfg.lbl.Diameter = \u76F4\u5F84\uFF1A +MassComponentCfg.lbl.PosRelativeto = \u4F4D\u7F6E\uFF1A +MassComponentCfg.lbl.plus = \u30D7\u30E9\u30B9 +MassComponentCfg.tab.General = \u4E00\u822C +MassComponentCfg.tab.ttip.General = \u4E00\u822C +MassComponentCfg.tab.Radialpos = \u534A\u5F84\u65B9\u5411\u4F4D\u7F6E +MassComponentCfg.tab.ttip.Radialpos = \u534A\u5F84\u65B9\u5411\u4F4D\u7F6E +MassComponentCfg.lbl.Radialdistance = \u8DDD\u96E2\uFF1A +MassComponentCfg.lbl.Radialdirection = \u65B9\u5411\uFF1A +MassComponentCfg.but.Reset = \u30EA\u30BB\u30C3\u30C8 + +! MotorConfig +MotorCfg.checkbox.compmotormount = \u3053\u306E\u90E8\u54C1\u304C\u30E2\u30FC\u30BF\u30FC\u30DE\u30A6\u30F3\u30C8 +MotorCfg.lbl.Motorcfg = \u30E2\u30FC\u30BF\u30FC\u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\uFF1A +MotorCfg.but.New = \u65B0\u898F +MotorCfg.lbl.Currentmotor = \u73FE\u5728\u306E\u30E2\u30FC\u30BF\u30FC\uFF1A +MotorCfg.lbl.Motoroverhang = \u30E2\u30FC\u30BF\u30FC\u306E\u5F35\u308A\u51FA\u3057\uFF1A +MotorCfg.lbl.Ignitionat = \u70B9\u706B\u30BF\u30A4\u30DF\u30F3\u30B0\uFF1A +MotorCfg.lbl.plus = \u30D7\u30E9\u30B9 +MotorCfg.lbl.seconds = \u79D2 +MotorCfg.lbl.longA1 = \u73FE\u5728\u306E\u30C7\u30B6\u30A4\u30F3\u3067\u306F\u30B9\u30C6\u30FC\u30B8\u306F\u4E00\u3064\uFF0E +MotorCfg.lbl.longA2 = \u30B9\u30C6\u30FC\u30B8\u306F\"\u65B0\u3057\u3044\u30B9\u30C6\u30FC\u30B8\"\u3067\u8FFD\u52A0\u3059\u308B\u3053\u3068\u304C\u51FA\u6765\u308B +MotorCfg.lbl.longB1 = \u73FE\u5728\u306E\u30C7\u30B6\u30A4\u30F3\u3067\u306F +MotorCfg.lbl.longB2 = \u30B9\u30C6\u30FC\u30B8\u304C\u3042\u308B +MotorCfg.but.Selectmotor = \u30E2\u30FC\u30BF\u30FC\u306E\u9078\u629E +MotorCfg.but.Removemotor = \u30E2\u30FC\u30BF\u30FC\u306E\u524A\u9664 +MotorCfg.lbl.motorLabel = None + +! NoseConeConfig +NoseConeCfg.lbl.Noseconeshape = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u5F62\u72B6\uFF1A +NoseConeCfg.lbl.Shapeparam = \u5F62\u72B6\u4FC2\u6570\uFF1A +NoseConeCfg.lbl.Noseconelength = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u9577\u3055\uFF1A +NoseConeCfg.lbl.Basediam = \u30D9\u30FC\u30B9\u76F4\u5F84\uFF1A +NoseConeCfg.checkbox.Automatic = \u81EA\u52D5 +NoseConeCfg.lbl.Wallthickness = \u539A\u3055\uFF1A +NoseConeCfg.checkbox.Filled = \u4E2D\u5B9F +NoseConeCfg.tab.General = \u4E00\u822C +NoseConeCfg.tab.ttip.General = \u4E00\u822C +NoseConeCfg.tab.Shoulder = \u30B7\u30E7\u30EB\u30C0\u30FC +NoseConeCfg.tab.ttip.Shoulder = \u30B7\u30E7\u30EB\u30C0\u30FC + +! ParachuteConfig +ParachuteCfg.lbl.Canopy = \u5098\uFF1A +ParachuteCfg.lbl.Diameter = \u76F4\u5F84\uFF1A +ParachuteCfg.lbl.Material = \u6750\u6599\uFF1A +ParachuteCfg.combo.MaterialModel = \u90E8\u54C1\u6750\u6599\u306F\u91CD\u91CF\u306B\u5F71\u97FF\u3059\u308B +ParachuteCfg.lbl.longA1 = \u6297\u529B\u4FC2\u6570 CD\uFF1A +ParachuteCfg.lbl.longB1 = \u6297\u529B\u4FC2\u6570\u306F\u30D1\u30E9\u30B7\u30E5\u30FC\u30C8\u306E\u7DCF\u9762\u7A4D\u306B\u95A2\u4FC2\u3059\u308B
+ParachuteCfg.lbl.longB2 = \u6297\u529B\u4FC2\u6570\u304C\u5927\u304D\u3044\u3068\u964D\u4E0B\u901F\u5EA6\u3092\u9045\u304F\u306A\u308B\uFF0E +ParachuteCfg.lbl.longB3 = \u6A19\u6E96\u7684\u306A\u5024\u306F0.8 +ParachuteCfg.but.Reset = \u30EA\u30BB\u30C3\u30C8 +ParachuteCfg.lbl.Shroudlines = \u30B7\u30E5\u30E9\u30A6\u30C9\u30E9\u30A4\u30F3\uFF1A +ParachuteCfg.lbl.Numberoflines = \u30E9\u30A4\u30F3\u306E\u6570\uFF1A +ParachuteCfg.lbl.Linelength = \u30E9\u30A4\u30F3\u9577\u3055\uFF1A +ParachuteCfg.lbl.Material = \u6750\u6599\uFF1A +ParachuteCfg.lbl.Posrelativeto = \u4F4D\u7F6E\uFF1A +ParachuteCfg.lbl.plus = \u30D7\u30E9\u30B9\uFF1A +ParachuteCfg.lbl.Packedlength = \u53CE\u7D0D\u9577\u3055\uFF1A +ParachuteCfg.lbl.Packeddiam = \u53CE\u7D0D\u76F4\u5F84\uFF1A +ParachuteCfg.lbl.Deploysat = \u5C55\u958B\u30BF\u30A4\u30DF\u30F3\u30B0\uFF1A +ParachuteCfg.lbl.seconds = \u79D2 +ParachuteCfg.lbl.Altitude = \u9AD8\u5EA6\uFF1A +ParachuteCfg.tab.General = \u4E00\u822C +ParachuteCfg.tab.ttip.General = \u4E00\u822C +ParachuteCfg.tab.Radialpos = \u534A\u5F84\u65B9\u5411\u4F4D\u7F6E +ParachuteCfg.tab.ttip.Radialpos = \u534A\u5F84\u65B9\u5411\u4F4D\u7F6E +ParachuteCfg.lbl.Radialdistance = \u8DDD\u96E2\uFF1A +ParachuteCfg.lbl.Radialdirection = \u65B9\u5411\uFF1A +ParachuteCfg.but.Reset = \u30EA\u30BB\u30C3\u30C8 +ParachuteCfg.lbl.plusdelay = \u30D7\u30E9\u30B9 + +! ShockCordConfig +ShockCordCfg.lbl.Shockcordlength = \u30B7\u30E7\u30C3\u30AF\u30B3\u30FC\u30C9\u9577\u3055 +ShockCordCfg.lbl.Shockcordmaterial = \u30B7\u30E7\u30C3\u30AF\u30B3\u30FC\u30C9\u6750\u6599\uFF1A +ShockCordCfg.lbl.Posrelativeto = \u4F4D\u7F6E\uFF1A +ShockCordCfg.lbl.plus = \u30D7\u30E9\u30B9 +ShockCordCfg.lbl.Packedlength = \u53CE\u7D0D\u9577\u3055\uFF1A +ShockCordCfg.lbl.Packeddiam = \u53CE\u7D0D\u76F4\u5F84\uFF1A +ShockCordCfg.tab.General = \u4E00\u822C +ShockCordCfg.tab.ttip.General = \u4E00\u822C + +!SleeveConfig +SleeveCfg.tab.Outerdiam = \u5916\u5F84\uFF1A +SleeveCfg.tab.Innerdiam = \u5185\u5F84\uFF1A +SleeveCfg.tab.Wallthickness = \u539A\u3055\uFF1A +SleeveCfg.tab.Length = \u9577\u3055\uFF1A +SleeveCfg.tab.General = \u4E00\u822C +SleeveCfg.tab.Generalproperties = \u4E00\u822C + +! StreamerConfig +StreamerCfg.lbl.Striplength = \u9577\u3055\uFF1A +StreamerCfg.lbl.Stripwidth = \u5E45\uFF1A +StreamerCfg.lbl.Striparea = \u9762\u7A4D\uFF1A +StreamerCfg.lbl.Aspectratio = \u30A2\u30B9\u30DA\u30AF\u30C8\u6BD4\uFF1A +StreamerCfg.lbl.Material = \u6750\u6599\uFF1A +StreamerCfg.combo.ttip.MaterialModel = \u90E8\u54C1\u6750\u6599\u306F\u91CD\u91CF\u306B\u5F71\u97FF\u3059\u308B +StreamerCfg.lbl.longA1 = \u6297\u529B\u4FC2\u6570 CD\uFF1A +StreamerCfg.lbl.longB1 = \u6297\u529B\u4FC2\u6570\u306F\u30B9\u30C8\u30EA\u30FC\u30DE\u306E\u7DCF\u9762\u7A4D\u306B\u95A2\u4FC2\u3059\u308B
+StreamerCfg.lbl.longB2 = \u6297\u529B\u4FC2\u6570\u304C\u5927\u304D\u3044\u3068\u964D\u4E0B\u901F\u5EA6\u3092\u9045\u304F\u306A\u308B\uFF0E +StreamerCfg.lbl.Automatic = \u81EA\u52D5 +StreamerCfg.lbl.longC1 = \u6297\u529B\u4FC2\u6570\u306F\u30B9\u30C8\u30EA\u30FC\u30DE\u306E\u9762\u7A4D\u306B\u95A2\u4FC2\u3059\u308B +StreamerCfg.lbl.Posrelativeto = \u4F4D\u7F6E\uFF1A +StreamerCfg.lbl.plus = \u30D7\u30E9\u30B9 +StreamerCfg.lbl.Packedlength = \u53CE\u7D0D\u9577\u3055\uFF1A +StreamerCfg.lbl.Packeddiam = \u53CE\u7D0D\u76F4\u5F84\uFF1A +StreamerCfg.lbl.Deploysat = \u5C55\u958B\u30BF\u30A4\u30DF\u30F3\u30B0\uFF1A +StreamerCfg.lbl.seconds = \u79D2 +StreamerCfg.lbl.Altitude = \u9AD8\u5EA6\uFF1A +StreamerCfg.tab.General = \u4E00\u822C +StreamerCfg.tab.ttip.General = \u4E00\u822C +StreamerCfg.tab.Radialpos = \u534A\u5F84\u65B9\u5411\u4F4D\u7F6E +StreamerCfg.tab.ttip.Radialpos = \u534A\u5F84\u65B9\u5411\u4F4D\u7F6E +StreamerCfg.lbl.Radialdistance = \u8DDD\u96E2\uFF1A +StreamerCfg.lbl.Radialdirection = \u65B9\u5411\uFF1A +StreamerCfg.but.Reset = \u30EA\u30BB\u30C3\u30C8 +StreamerCfg.lbl.plusdelay = \u30D7\u30E9\u30B9 + +! ThicknessRingComponentConfig +ThicknessRingCompCfg.tab.Outerdiam = \u5916\u5F84\uFF1A +ThicknessRingCompCfg.tab.Innerdiam = \u5185\u5F84\uFF1A +ThicknessRingCompCfg.tab.Wallthickness = \u539A\u3055\uFF1A +ThicknessRingCompCfg.tab.Length = \u9577\u3055\uFF1A +ThicknessRingCompCfg.tab.General = \u4E00\u822C +ThicknessRingCompCfg.tab.Generalprop = \u4E00\u822C + +! TransitionConfig +TransitionCfg.lbl.Transitionshape = \u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u5F62\u72B6\uFF1A +TransitionCfg.checkbox.Clipped = Clipped +TransitionCfg.lbl.Shapeparam = \u5F62\u72B6\u4FC2\u6570\uFF1A +TransitionCfg.lbl.Transitionlength = \u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u9577\u3055\uFF1A +TransitionCfg.lbl.Forediam = \u524D\u65B9\u76F4\u5F84\uFF1A +TransitionCfg.checkbox.Automatic = \u81EA\u52D5 +TransitionCfg.lbl.Aftdiam = \u5F8C\u65B9\u76F4\u5F84\uFF1A +TransitionCfg.lbl.Wallthickness = \u539A\u3055\uFF1A +TransitionCfg.checkbox.Filled = \u4E2D\u5B9F +TransitionCfg.tab.General = \u4E00\u822C +TransitionCfg.tab.Generalproperties = \u4E00\u822C +TransitionCfg.tab.Shoulder = \u30B7\u30E7\u30EB\u30C0\u30FC +TransitionCfg.tab.Shoulderproperties = \u30B7\u30E7\u30EB\u30C0\u30FC + +! TrapezoidFinSetConfig +TrapezoidFinSetCfg.lbl.Nbroffins = \u30D5\u30A3\u30F3\u306E\u6570 +TrapezoidFinSetCfg.lbl.ttip.Nbroffins = \u30D5\u30A3\u30F3\u306E\u6570 +TrapezoidFinSetCfg.lbl.Finrotation = \u56DE\u8EE2\uFF1A +TrapezoidFinSetCfg.lbl.ttip.Finrotation = \u30D5\u30A3\u30F3\u306E\u89D2\u5EA6 +TrapezoidFinSetCfg.lbl.Fincant = \u50BE\u659C\uFF1A +TrapezoidFinSetCfg.lbl.ttip.Fincant = \u30DC\u30C7\u30A3\u306B\u5BFE\u3057\u3066\u306E\u30D5\u30A3\u30F3\u306E\u50BE\u659C\u89D2\u5EA6 +TrapezoidFinSetCfg.lbl.Rootchord = \u6839\u672C\u9577\u3055\uFF1A +TrapezoidFinSetCfg.lbl.Tipchord = \u7AEF\u90E8\u9577\u3055\uFF1A +TrapezoidFinSetCfg.lbl.Height = \u9AD8\u3055\uFF1A +TrapezoidFinSetCfg.lbl.Sweeplength = \u5F8C\u9000\u9577\u3055\uFF1A +TrapezoidFinSetCfg.lbl.Sweepangle = \u5F8C\u9000\u89D2\uFF1A +TrapezoidFinSetCfg.lbl.FincrossSection = \u30D5\u30A3\u30F3\u65AD\u9762\u7A4D\uFF1A +TrapezoidFinSetCfg.lbl.Thickness = \u539A\u3055\uFF1A +TrapezoidFinSetCfg.lbl.Posrelativeto = \u4F4D\u7F6E\uFF1A +TrapezoidFinSetCfg.lbl.plus = \u30D7\u30E9\u30B9 +TrapezoidFinSetCfg.tab.General = \u4E00\u822C +TrapezoidFinSetCfg.tab.Generalproperties = \u4E00\u822C + +!MotorConfigurationModel +MotorCfgModel.Editcfg = \u7DE8\u96C6 + +! StorageOptionChooser +StorageOptChooser.lbl.Simdatatostore = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u3057\u305F\u30C7\u30FC\u30BF\u306E\u4FDD\u5B58\uFF1A +StorageOptChooser.rdbut.Allsimdata = \u5168\u3066\u306E\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u30C7\u30FC\u30BF +StorageOptChooser.lbl.longA1 = \u5168\u3066\u306E\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u30C7\u30FC\u30BF\u3092\u4FDD\u5B58
+StorageOptChooser.lbl.longA2 = \u3068\u3066\u3082\u5927\u304D\u306A\u30D5\u30A1\u30A4\u30EB\u306B\u306A\u308A\u307E\u3059 +StorageOptChooser.rdbut.Every = +StorageOptChooser.lbl.longB1 = \u8FD1\u4F3C\u3057\u3066\u4FDD\u5B58
+StorageOptChooser.lbl.longB2 = \u5927\u304D\u306A\u5024\u306B\u3059\u308B\u3068\u30D5\u30A1\u30A4\u30EB\u306F\u5C0F\u3055\u304F\u306A\u308B +StorageOptChooser.lbl.seconds = \u79D2\u6BCE +StorageOptChooser.rdbut.Onlyprimfig = \u4E3B\u8981\u306A\u5024\u306E\u307F +StorageOptChooser.lbl.longC1 = \u8868\u306B\u66F8\u304B\u308C\u3066\u3044\u308B\u5024\u306E\u307F\u4FDD\u5B58
+StorageOptChooser.lbl.longC2 = \u30D5\u30A1\u30A4\u30EB\u306F\u4E00\u756A\u5C0F\u3055\u304F\u306A\u308B +StorageOptChooser.checkbox.Compfile = \u30D5\u30A1\u30A4\u30EB\u306E\u5727\u7E2E +StorageOptChooser.lbl.UsingComp = \u30D5\u30A1\u30A4\u30EB\u30B5\u30A4\u30BA\u3092\u5727\u7E2E\u3059\u308B +StorageOptChooser.lbl.longD1 = \u73FE\u5728\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u3067\u3069\u308C\u307B\u3069\u5727\u7E2E\u3067\u304D\u308B\u304B\u306E\u63A8\u5B9A +StorageOptChooser.ttip.Saveopt = \u4FDD\u5B58\u30AA\u30D7\u30B7\u30E7\u30F3 +StorageOptChooser.lbl.Estfilesize = \u63A8\u5B9A\u30D5\u30A1\u30A4\u30EB\u30B5\u30A4\u30BA\uFF1A +StorageOptChooser.lbl.Saveopt = \u4FDD\u5B58\u30AA\u30D7\u30B7\u30E7\u30F3 + +! ThrustCurveMotorSelectionPanel +TCMotorSelPan.lbl.Selrocketmotor = \u30ED\u30B1\u30C3\u30C8\u30E2\u30FC\u30BF\u30FC\u306E\u9078\u629E\uFF1A +TCMotorSelPan.checkbox.hideSimilar = \u4F3C\u3066\u3044\u308B\u63A8\u529B\u5C65\u6B74\u3092\u96A0\u3059 +TCMotorSelPan.SHOW_DESCRIPTIONS.desc1 = \u5168\u3066\u306E\u30E2\u30FC\u30BF\u30FC\u3092\u8868\u793A +TCMotorSelPan.SHOW_DESCRIPTIONS.desc2 = \u30E2\u30FC\u30BF\u30FC\u30DE\u30A6\u30F3\u30C8\u306E\u76F4\u5F84\u4EE5\u4E0B\u306E\u30E2\u30FC\u30BF\u30FC\u3092\u8868\u793A +TCMotorSelPan.SHOW_DESCRIPTIONS.desc3 = \u30E2\u30FC\u30BF\u30FC\u30DE\u30A6\u30F3\u30C8\u306E\u76F4\u5F84\u3068\u540C\u3058\u30E2\u30FC\u30BF\u30FC\u3092\u8868\u793A +TCMotorSelPan.lbl.Motormountdia = \u30E2\u30FC\u30BF\u30FC\u30DE\u30A6\u30F3\u30C8\u76F4\u5F84\uFF1A +TCMotorSelPan.lbl.Search = \u691C\u7D22\uFF1A +TCMotorSelPan.lbl.Selectthrustcurve = \u63A8\u529B\u5C65\u6B74\uFF1A +TCMotorSelPan.lbl.Ejectionchargedelay = Ejection charge delay: +TCMotorSelPan.equalsIgnoreCase.None = None +TCMotorSelPan.lbl.NumberofsecondsorNone = (Number of seconds or \"None\") +TCMotorSelPan.lbl.Totalimpulse = \u30C8\u30FC\u30BF\u30EB\u30A4\u30F3\u30D1\u30EB\u30B9\uFF1A +TCMotorSelPan.lbl.Avgthrust = \u5E73\u5747\u63A8\u529B\uFF1A +TCMotorSelPan.lbl.Maxthrust = \u6700\u5927\u63A8\u529B\uFF1A +TCMotorSelPan.lbl.Burntime = \u71C3\u713C\u6642\u9593\uFF1A +TCMotorSelPan.lbl.Launchmass = \u70B9\u706B\u6642\u8CEA\u91CF\uFF1A +TCMotorSelPan.lbl.Emptymass = \u71C3\u713C\u5F8C\u8CEA\u91CF\uFF1A +TCMotorSelPan.lbl.Datapoints = \u30C7\u30FC\u30BF\u70B9\uFF1A +TCMotorSelPan.lbl.Digest = \u30C0\u30A4\u30B8\u30A7\u30B9\u30C8\uFF1A +TCMotorSelPan.title.Thrustcurve = \u63A8\u529B\u5C65\u6B74\uFF1A +TCMotorSelPan.title.Thrust = \u63A8\u529B +TCMotorSelPan.delayBox.None = None +TCMotorSelPan.noDescription = No description available. + + +! PlotDialog +PlotDialog.title.Flightdataplot = \u30D5\u30E9\u30A4\u30C8\u30C7\u30FC\u30BF\u30D7\u30ED\u30C3\u30C8 +PlotDialog.Chart.Simulatedflight = \u30D5\u30E9\u30A4\u30C8\u30B7\u30DF\u30E5\u30EC\u30FC\u30C8 +PlotDialog.CheckBox.Showdatapoints = \u30C7\u30FC\u30BF\u70B9\u306E\u8868\u793A +PlotDialog.lbl.Chart = \u30AF\u30EA\u30C3\u30AF+\u30C9\u30E9\u30C3\u30B0 \u4E0B+\u53F3 \u3067\u30BA\u30FC\u30E0\u30A4\u30F3, \u4E0A+\u5DE6 \u3067\u30BA\u30FC\u30E0\u30A2\u30A6\u30C8 + + +! "main" prefix is used for the main application dialog + +# FIXME: Rename the description keys + +main.menu.file = \u30D5\u30A1\u30A4\u30EB +main.menu.file.desc = File-handling related tasks +main.menu.file.new = \u65B0\u898F +main.menu.file.new.desc = \u65B0\u3057\u3044\u30C7\u30B6\u30A4\u30F3\u3092\u4F5C\u308B +main.menu.file.open = \u958B\u304F\u2026 +BasicFrame.item.Openrocketdesign = \u30C7\u30B6\u30A4\u30F3\u3092\u958B\u304F +main.menu.file.openRecent = \u524D\u56DE\u958B\u3044\u305F\u30C7\u30B6\u30A4\u30F3\u3092\u958B\u304F\u2026 +BasicFrame.item.Openrecentrocketdesign = \u524D\u56DE\u306E\u30C7\u30B6\u30A4\u30F3\u3092\u958B\u304F +main.menu.file.openExample = \u30B5\u30F3\u30D7\u30EB\u30C7\u30B6\u30A4\u30F3\u3092\u958B\u304F... +BasicFrame.item.Openexamplerocketdesign = \u30B5\u30F3\u30D7\u30EB\u306E\u30ED\u30B1\u30C3\u30C8\u30C7\u30B6\u30A4\u30F3\u3092\u958B\u304F +main.menu.file.save = \u4FDD\u5B58 +BasicFrame.item.SavecurRocketdesign = \u73FE\u5728\u306E\u30ED\u30B1\u30C3\u30C8\u30C7\u30B6\u30A4\u30F3\u3092\u4FDD\u5B58 +main.menu.file.saveAs = \u540D\u524D\u3092\u4ED8\u3051\u3066\u4FDD\u5B58... +BasicFrame.item.SavecurRocketdesnewfile = \u73FE\u5728\u306E\u30ED\u30B1\u30C3\u30C8\u30C7\u30B6\u30A4\u30F3\u3092\u65B0\u3057\u3044\u30D5\u30A1\u30A4\u30EB\u306B\u4FDD\u5B58 +main.menu.file.print = \u5370\u5237/PDF\u306B\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\u2026 +main.menu.file.print.desc = \u5370\u5237\u3084PDF +main.menu.file.close = \u9589\u3058\u308B +BasicFrame.item.Closedesign = \u73FE\u5728\u306E\u30ED\u30B1\u30C3\u30C8\u30C7\u30B6\u30A4\u30F3\u3092\u9589\u3058\u308B +main.menu.file.quit = \u7D42\u4E86 +BasicFrame.item.Quitprogram = \u30D7\u30ED\u30B0\u30E9\u30E0\u3092\u7D42\u4E86 + +main.menu.edit = \u7DE8\u96C6 +BasicFrame.menu.Rocketedt = \u7DE8\u96C6 +main.menu.edit.undo = \u623B\u308B +main.menu.edit.undo.desc = \u524D\u306E\u64CD\u4F5C\u3092\u3084\u308A\u76F4\u3059 +main.menu.edit.redo = \u9032\u3080 +main.menu.edit.redo.desc = \u3084\u308A\u76F4\u3057\u305F\u64CD\u4F5C\u3092\u53D6\u308A\u6D88\u3059 +main.menu.edit.cut = \u5207\u308A\u53D6\u308A +main.menu.edit.copy = \u30B3\u30D4\u30FC +main.menu.edit.paste = \u8CBC\u308A\u4ED8\u3051 +main.menu.edit.delete = \u524A\u9664 +main.menu.edit.resize = \u62E1\u5927\u7E2E\u5C0F... +main.menu.edit.resize.desc = \u90E8\u54C1\u306E\u5927\u304D\u3055\u3092\u8CB7\u3048\u308B +main.menu.edit.editpreset = Component Preset File\u306E\u7DE8\u96C6 +main.menu.edit.preferences = \u8A2D\u5B9A +main.menu.edit.preferences.desc = \u30A2\u30D7\u30EA\u306E\u8A2D\u5B9A\u3092\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7 + +main.menu.analyze = \u89E3\u6790 +main.menu.analyze.desc = \u30ED\u30B1\u30C3\u30C8\u89E3\u6790 +main.menu.analyze.componentAnalysis = \u90E8\u54C1\u89E3\u6790 +main.menu.analyze.componentAnalysis.desc = \u90E8\u54C1\u3092\u5206\u3051\u3066\u89E3\u6790 +main.menu.analyze.optimization = \u30ED\u30B1\u30C3\u30C8\u6700\u9069\u5316 +main.menu.analyze.optimization.desc = \u5168\u4F53\u3092\u6700\u9069\u5316 +main.menu.analyze.customExpressions = \u30AB\u30B9\u30BF\u30E0\u5F0F +main.menu.analyze.customExpressions.desc = \u65B0\u3057\u3044\u30D5\u30E9\u30A4\u30C8\u30C7\u30FC\u30BF\u30BF\u30A4\u30D7\u3092\u30AB\u30B9\u30BF\u30E0\u3057\u305F\u6570\u5F0F\u3067\u5B9A\u7FA9 + +main.menu.help = \u30D8\u30EB\u30D7 +main.menu.help.desc = OpenRocket\u306B\u3064\u3044\u3066\u306E\u60C5\u5831 +main.menu.help.tours = \u30C4\u30A2\u30FC\u30AC\u30A4\u30C9 +main.menu.help.tours.desc = OpenRocket\u306E\u30AC\u30A4\u30C9 +main.menu.help.license = \u30E9\u30A4\u30BB\u30F3\u30B9 +main.menu.help.license.desc = OpenRocket\u30E9\u30A4\u30BB\u30F3\u30B9\u60C5\u5831 +main.menu.help.bugReport = \u30D0\u30B0\u30EC\u30DD\u30FC\u30C8 +main.menu.help.bugReport.desc = \u30D0\u30B0\u30EC\u30DD\u30FC\u30C8\u60C5\u5831 +main.menu.help.debugLog = \u30C7\u30D0\u30C3\u30B0\u30ED\u30B0 +main.menu.help.debugLog.desc = OpenRocket\u306E\u30C7\u30D0\u30C3\u30B0\u30ED\u30B0\u306E\u8868\u793A +main.menu.help.about = OpenRocket\u306B\u3064\u3044\u3066 +main.menu.help.about.desc = OpenRocket\u306B\u3064\u3044\u3066\u306E\u30B3\u30D4\u30FC\u30E9\u30A4\u30C8 + +main.menu.debug = \u30C7\u30D0\u30C3\u30B0 +main.menu.debug.whatisthismenu = What is this menu? +main.menu.debug.createtestrocket = Create test rocket + +! database +! Translate here all material database +! + +Material.CUSTOM = \u30AB\u30B9\u30BF\u30E0 + +! Material database +Databases.materials.types.Bulk = \u30D0\u30EB\u30AF +Databases.materials.types.Line = \u7DDA +Databases.materials.types.Surface = \u9762 + +! BULK_MATERIAL +material.acrylic = \u30A2\u30AF\u30EA\u30EB +material.aluminum = \u30A2\u30EB\u30DF +material.balsa = \u30D0\u30EB\u30B5\u6750 +material.basswood = Basswood +material.birch = \u30AB\u30D0\u6750 +material.brass = \u771F\u936E +material.cardboard = \u30DC\u30FC\u30EB\u7D19 +material.carbon_fiber = \u30AB\u30FC\u30DC\u30F3\u30D5\u30A1\u30A4\u30D0\u30FC +material.cork = \u30B3\u30EB\u30AF\u6750 +material.depron_xps = Depron (XPS) +material.fiberglass = \u30AC\u30E9\u30B9\u30D5\u30A1\u30A4\u30D0\u30FC +material.kraft_phenolic = \u7D19\u30D5\u30A7\u30CE\u30FC\u30EB +material.maple = \u30AB\u30A8\u30C7\u6750 +material.paper_office = \u7D19\uFF08\u30AA\u30D5\u30A3\u30B9\u7528\uFF09 +material.pine = \u30DE\u30C4\u6750 +material.plywood_birch = \u5408\u677F\uFF08\u30AB\u30D0\u6750\uFF09 +material.polycarbonate_lexan = \u30DD\u30EA\u30AB\u30FC\u30DC\u30CD\u30FC\u30C8 (Lecan) +material.polystyrene = \u30DD\u30EA\u30B9\u30C1\u30EC\u30F3 +material.pvc = \u30DD\u30EA\u5869\u5316\u30D3\u30CB\u30EB +material.spruce = \u30B9\u30D7\u30EB\u30FC\u30B9\u6750 +material.steel = \u9244 +material.styrofoam_generic_eps = \u30B9\u30BF\u30A4\u30ED\u30D5\u30A9\u30FC\u30E0(EPS) +material.styrofoam_blue_foam_xps = \u30B9\u30BF\u30A4\u30ED\u30D5\u30A9\u30FC\u30E0\u9752(XPS) +material.titanium = \u30C1\u30BF\u30F3 +material.quantum_tubing = Quantum tubing +material.blue_tube = Blue tube +!SURFACE_MATERIAL +material.ripstop_nylon = \u30CA\u30A4\u30ED\u30F3\u30EA\u30C3\u30D7\u30B9\u30C8\u30C3\u30D7 +material.mylar = \u30DE\u30A4\u30E9\u30FC +material.polyethylene_thin = \u30DD\u30EA\u30B9\u30C1\u30EC\u30F3 (thin) +material.polyethylene_heavy = \u30DD\u30EA\u30B9\u30C1\u30EC\u30F3 (heavy) +material.silk = \u7D79 +material.paper_office = \u7D19\uFF08\u30AA\u30D5\u30A3\u30B9\u7528\uFF09 +material.cellophane = \u30BB\u30ED\u30D5\u30A1\u30F3 +material.crepe_paper = \u30AF\u30EC\u30FC\u30D7\u30DA\u30FC\u30D1\u30FC +! LINE_MATERIAL +material.thread_heavy_duty = \u7CF8\uFF08\u9811\u4E08\u306A\uFF09 +material.elastic_cord_round_2_mm_1_16_in = \u30B4\u30E0\u3072\u3082(\u4E382mm) +material.elastic_cord_flat_6_mm_1_4_in = \u30B4\u30E0\u3072\u3082(\u5E736mm) +material.elastic_cord_flat_12_mm_1_2_in = \u30B4\u30E0\u3072\u3082(\u5E7312mm) +material.elastic_cord_flat_19_mm_3_4_in = \u30B4\u30E0\u3072\u3082(\u5E7319mm) +material.elastic_cord_flat_25_mm_1_in = \u30B4\u30E0\u3072\u3082(\u5E7325mm) +material.braided_nylon_2_mm_1_16_in = \u7DE8\u307F\u8FBC\u307F\u30CA\u30A4\u30ED\u30F3(2 mm) +material.braided_nylon_3_mm_1_8_in = \u7DE8\u307F\u8FBC\u307F\u30CA\u30A4\u30ED\u30F3(3 mm) +material.tubular_nylon_11_mm_7_16_in = \u7BA1\u72B6\u30CA\u30A4\u30ED\u30F3(11 mm) +material.tubular_nylon_14_mm_9_16_in = \u7BA1\u72B6\u30CA\u30A4\u30ED\u30F3(14 mm) +material.tubular_nylon_25_mm_1_in = \u7BA1\u72B6\u30CA\u30A4\u30ED\u30F3(25 mm) + +! ExternalComponent +ExternalComponent.Rough = \u7C97\u3044 +ExternalComponent.Unfinished = \u672A\u4ED5\u4E0A\u3052 +ExternalComponent.Regularpaint = \u901A\u5E38\u306E\u5857\u88C5 +ExternalComponent.Smoothpaint = \u306A\u3081\u3089\u304B\u306A\u5857\u88C5 +ExternalComponent.Polished = \u78E8\u304B\u308C\u305F + +! LineStyle +LineStyle.Solid = \u5B9F\u7DDA +LineStyle.Dashed = \u7834\u7DDA +LineStyle.Dotted = \u70B9\u7DDA +LineStyle.Dash-dotted = \u4E00\u70B9\u9396\u7DDA +LineStyle.Defaultstyle = \u30C7\u30D5\u30A9\u30EB\u30C8\u30B9\u30BF\u30A4\u30EB + +! Shape +Shape.Conical = \u5186\u9310\u5F62 +Shape.Conical.desc1 = \u5186\u9310\u5F62\u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306F\u4E09\u89D2\u5F62\u306E\u65AD\u9762\u3092\u3057\u3066\u3044\u308B +Shape.Conical.desc2 = \u5186\u9310\u5F62\u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u306E\u5074\u9762\u306F\u76F4\u7DDA\u7684 +Shape.Ogive = \u30AA\u30B8\u30FC\u30D6 +Shape.Ogive.desc1 = \u30AA\u30B8\u30FC\u30D6\u306E\u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306F\u5186\u306E\u4E00\u90E8\u5206\u306E\u8F2A\u90ED\u3092\u3057\u3066\u3044\u308B\u3002\u5F62\u72B6\u4FC2\u6570\u304C1\u3067\u306F\u63A5\u7DDA\u30AA\u30B8\u30FC\u30D6\u304C\u4F5C\u3089\u308C\u3001\u3053\u308C\u306F\u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6\u3068\u306A\u3081\u3089\u304B\u306B\u63A5\u7D9A\u3055\u308C\u308B\u30021\u4EE5\u4E0B\u3067\u306F\u5272\u7DDA\u30AA\u30B8\u30FC\u30D6\u304C\u4F5C\u3089\u308C\u308B +Shape.Ogive.desc2 = \u30AA\u30B8\u30FC\u30D6\u306E\u30BF\u30F3\u30B8\u30A7\u30F3\u30C8\u306F\u5186\u306E\u4E00\u90E8\u5206\u306E\u8F2A\u90ED\u3092\u3057\u3066\u3044\u308B\u3002\u5F62\u72B6\u4FC2\u6570\u304C1\u3067\u306F\u63A5\u7DDA\u30AA\u30B8\u30FC\u30D6\u304C\u4F5C\u3089\u308C\u3001\u3053\u308C\u306F\u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6\u3068\u306A\u3081\u3089\u304B\u306B\u63A5\u7D9A\u3055\u308C\u308B\u30021\u4EE5\u4E0B\u3067\u306F\u5272\u7DDA\u30AA\u30B8\u30FC\u30D6\u304C\u4F5C\u3089\u308C\u308B +Shape.Ellipsoid = \u6955\u5186\u4F53 +Shape.Ellipsoid.desc1 = \u6955\u5186\u4F53\u306E\u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306F\u6955\u5186\u3092\u534A\u5206\u306B\u3057\u305F\u8F2A\u90ED\u3092\u3057\u3066\u3044\u3066\u8EF8\u306E\u9577\u3055\u306F2×\u9577\u3055\u3068\u76F4\u5F84 +Shape.Ellipsoid.desc2 = \u6955\u5186\u4F53\u306E\u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u306F\u6955\u5186\u3092\u534A\u5206\u306B\u3057\u305F\u8F2A\u90ED\u3092\u3057\u3066\u3044\u3066\u8EF8\u306E\u9577\u3055\u306F2×\u9577\u3055\u3068\u76F4\u5F84\u3002\u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u304C\u30AF\u30EA\u30C3\u30D7\u3055\u308C\u3066\u3044\u306A\u304B\u3063\u305F\u3089\u4E00\u81F4\u3059\u308B\u534A\u5F84\u307E\u3067\u62E1\u5F35\u3055\u308C\u308B +Shape.Powerseries = Power series +Shape.Powerseries.desc1 = Power series\u306E\u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306F \u534A\u5F84 × (x / \u9577\u3055)k \u3053\u3053\u3067k\u306F\u5F62\u72B6\u4FC2\u6570\u3002 k +Shape.Powerseries.desc2 = Power series\u306E\u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u306F \u534A\u5F84 × (x / \u9577\u3055)k \u3053\u3053\u3067k\u306F\u5F62\u72B6\u4FC2\u6570\u3002 k +Shape.Parabolicseries = Parabolic series +Shape.Parabolicseries.desc1 = Parabolic series\u306E\u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306F\u653E\u7269\u7DDA\u306E\u8F2A\u90ED\u3092\u3057\u3066\u3044\u308B\u3002\u5F62\u72B6\u4FC2\u6570\u306F\u653E\u7269\u7DDA\u306E\u4E00\u90E8\u5206\u3092\u5229\u7528\u3057\u3066\u5B9A\u7FA9\u3057\u3066\u3044\u308B\u3002\u5F62\u72B6\u4FC2\u65701.0\u3067\u306F\u653E\u7269\u7DDA\u306E\u5168\u90E8\u3067\u3053\u308C\u306F\u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6\u306B\u306A\u3081\u3089\u304B\u306B\u7E4B\u304C\u308B\u30010.75\u3067\u306F\u653E\u7269\u7DDA\u306E3/4\u30010.5\u3067\u306F\u653E\u7269\u7DDA\u306E1/2\u305D\u3057\u30660\u3067\u306F \u5186\u9310\u5F62\u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306B\u306A\u308B\u3002 +Shape.Parabolicseries.desc2 = Parabolic series\u306E\u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u306F\u653E\u7269\u7DDA\u306E\u8F2A\u90ED\u3092\u3057\u3066\u3044\u308B\u3002\u5F62\u72B6\u4FC2\u6570\u306F\u653E\u7269\u7DDA\u306E\u4E00\u90E8\u5206\u3092\u5229\u7528\u3057\u3066\u5B9A\u7FA9\u3057\u3066\u3044\u308B\u3002\u5F62\u72B6\u4FC2\u65701.0\u3067\u306F\u653E\u7269\u7DDA\u306E\u5168\u90E8\u3067\u3053\u308C\u306F\u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6\u306B\u306A\u3081\u3089\u304B\u306B\u7E4B\u304C\u308B\u30010.75\u3067\u306F\u653E\u7269\u7DDA\u306E3/4\u30010.5\u3067\u306F\u653E\u7269\u7DDA\u306E1/2\u305D\u3057\u30660\u3067\u306F \u5186\u9310\u5F62\u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u306B\u306A\u308B\u3002 +Shape.Haackseries = Haack series +Shape.Haackseries.desc1 = Haack series\u306E\u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306F\u6297\u529B\u304C\u6700\u5C0F\u306B\u306A\u308B\u3088\u3046\u306B\u8A2D\u8A08\u3055\u308C\u3066\u3044\u308B\u3002\u5F62\u72B6\u4FC2\u65700\u3067\u306FLD-Haack\u307E\u305F\u306FVon Karman\u3068\u547C\u3070\u308C\u308B\u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306B\u306A\u308B\u3001\u3053\u308C\u306F\u4E00\u5B9A\u306E\u9577\u3055\u3068\u76F4\u5F84\u306B\u304A\u3044\u3066\u306F\u6700\u5C0F\u306E\u6297\u529B\u306B\u306A\u308B\u3002\u5F62\u72B6\u4FC2\u65700.333\u3067\u306FLV-Haack\u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306B\u306A\u308B\u3002\u3053\u308C\u306F\u4E00\u5B9A\u306E\u9577\u3055\u3068\u5BB9\u7A4D\u3067\u306F\u6700\u5C0F\u306E\u6297\u529B\u306B\u306A\u308B\u3002 +Shape.Haackseries.desc2 = Haack series\u306E\u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306F\u6297\u529B\u304C\u6700\u5C0F\u306B\u306A\u308B\u3088\u3046\u306B\u8A2D\u8A08\u3055\u308C\u3066\u3044\u308B\u3002\u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u3067\u3082\u5F62\u72B6\u306F\u540C\u7B49\u3067\u3042\u308B\u304C\u3001\u5FC5\u305A\u3057\u3082\u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u306E\u6700\u9069\u306A\u6297\u529B\u306B\u306A\u308B\u3068\u306F\u9650\u3089\u306A\u3044\u3002\u5F62\u72B6\u4FC2\u65700\u3067\u306FLD-Haack\u307E\u305F\u306FVon Karman\u3068\u547C\u3070\u308C\u308B\u5F62\u72B6\u306B\u306A\u308B\u3002\u5F62\u72B6\u4FC2\u65700.333\u3067\u306FLV-Haack\u5F62\u72B6\u306B\u306A\u308B\u3002 + + +! RocketComponent +RocketComponent.Position.TOP = \u73FE\u5728\u306E\u90E8\u54C1\u306E\u5148\u7AEF\u304B\u3089 +RocketComponent.Position.MIDDLE = \u73FE\u5728\u306E\u90E8\u54C1\u306E\u4E2D\u5FC3\u304B\u3089 +RocketComponent.Position.BOTTOM = \u73FE\u5728\u306E\u90E8\u54C1\u306E\u5F8C\u7AEF\u304B\u3089 +RocketComponent.Position.AFTER = \u73FE\u5728\u306E\u90E8\u54C1\u306E\u5F8C\u65B9\u3078 +RocketComponent.Position.ABSOLUTE = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306E\u5148\u7AEF\u304B\u3089 + +! LaunchLug +LaunchLug.Launchlug = \u30ED\u30FC\u30F3\u30C1\u30E9\u30B0 +! NoseCone +NoseCone.NoseCone = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3 +! Transition +Transition.Transition = \u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3 +!Stage +Stage.Stage = \u30B9\u30C6\u30FC\u30B8 + +Stage.SeparationEvent.UPPER_IGNITION = \u4E0A\u6BB5\u30B9\u30C6\u30FC\u30B8\u306E\u30E2\u30FC\u30BF\u30FC\u70B9\u706B +Stage.SeparationEvent.IGNITION = \u73FE\u5728\u306E\u30B9\u30C6\u30FC\u30B8\u306E\u30E2\u30FC\u30BF\u30FC\u70B9\u706B +Stage.SeparationEvent.BURNOUT = \u73FE\u5728\u306E\u30B9\u30C6\u30FC\u30B8\u306E\u30E2\u30FC\u30BF\u30FC\u71C3\u713C\u7D42\u4E86 +Stage.SeparationEvent.EJECTION = \u73FE\u5728\u306E\u30B9\u30C6\u30FC\u30B8\u306E\u653E\u51FA\u85AC\u70B9\u706B +Stage.SeparationEvent.LAUNCH = \u767A\u5C04 +Stage.SeparationEvent.NEVER = Never + +! BodyTube +BodyTube.BodyTube = \u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6 +! TubeCoupler +TubeCoupler.TubeCoupler = \u30C1\u30E5\u30FC\u30D6\u30AB\u30D7\u30E9\u30FC +!InnerTube +InnerTube.InnerTube = \u30A4\u30F3\u30CA\u30FC\u30C1\u30E5\u30FC\u30D6 +! TrapezoidFinSet +TrapezoidFinSet.TrapezoidFinSet = \u53F0\u5F62\u30D5\u30A3\u30F3 +! FreeformFinSet +FreeformFinSet.FreeformFinSet = \u81EA\u7531\u5F62\u30D5\u30A3\u30F3 +!MassComponent +MassComponent.MassComponent = \u304A\u3082\u308A +! Parachute +Parachute.Parachute = \u30D1\u30E9\u30B7\u30E5\u30FC\u30C8 +! ShockCord +ShockCord.ShockCord = \u30B7\u30E7\u30C3\u30AF\u30B3\u30FC\u30C9 +! Bulkhead +Bulkhead.Bulkhead = \u30D0\u30EB\u30AF\u30D8\u30C3\u30C9 +! CenteringRing +CenteringRing.CenteringRing = \u30BB\u30F3\u30BF\u30FC\u30EA\u30F3\u30B0 +! EngineBlock +EngineBlock.EngineBlock = \u30A8\u30F3\u30B8\u30F3\u30D6\u30ED\u30C3\u30AF +! Streamer +Streamer.Streamer = \u30B9\u30C8\u30EA\u30FC\u30DE\u30FC +! Sleeve +Sleeve.Sleeve = Sleeve + +!Rocket +Rocket.motorCount.Nomotor = No motors +Rocket.compname.Rocket = Rocket + +!MotorMount +MotorMount.IgnitionEvent.AUTOMATIC = \u81EA\u52D5(\u767A\u5C04\u3082\u3057\u304F\u306F\u30B9\u30C6\u30FC\u30B8\u306E\u70B9\u706B) +MotorMount.IgnitionEvent.LAUNCH = \u767A\u5C04 +MotorMount.IgnitionEvent.EJECTION_CHARGE = \u524D\u6BB5\u306E\u30B9\u30C6\u30FC\u30B8\u306E\u653E\u51FA\u85AC\u70B9\u706B +MotorMount.IgnitionEvent.BURNOUT = \u524D\u6BB5\u306E\u30B9\u30C6\u30FC\u30B8\u306E\u71C3\u713C\u7D42\u4E86 +MotorMount.IgnitionEvent.NEVER = Never + +!ComponentIcons +ComponentIcons.Nosecone = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3 +ComponentIcons.Bodytube = \u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6 +ComponentIcons.Transition = \u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3 +ComponentIcons.Trapezoidalfinset = \u53F0\u5F62\u30D5\u30A3\u30F3 +ComponentIcons.Ellipticalfinset = \u6955\u5186\u5F62\u30D5\u30A3\u30F3 +ComponentIcons.Freeformfinset = \u81EA\u7531\u5F62\u30D5\u30A3\u30F3 +ComponentIcons.Launchlug = \u30ED\u30FC\u30F3\u30C1\u30E9\u30B0 +ComponentIcons.Innertube = \u30A4\u30F3\u30CA\u30FC\u30C1\u30E5\u30FC\u30D6 +ComponentIcons.Tubecoupler = \u30C1\u30E5\u30FC\u30D6\u30AB\u30D7\u30E9\u30FC +ComponentIcons.Centeringring = \u30BB\u30F3\u30BF\u30FC\u30EA\u30F3\u30B0 +ComponentIcons.Bulkhead = \u30D0\u30EB\u30AF\u30D8\u30C3\u30C9 +ComponentIcons.Engineblock = \u30A8\u30F3\u30B8\u30F3\u30D6\u30ED\u30C3\u30AF +ComponentIcons.Parachute = \u30D1\u30E9\u30B7\u30E5\u30FC\u30C8 +ComponentIcons.Streamer = \u30B9\u30C8\u30EA\u30FC\u30DE +ComponentIcons.Shockcord = \u30B7\u30E7\u30C3\u30AF\u30B3\u30FC\u30C9 +ComponentIcons.Masscomponent = \u304A\u3082\u308A +ComponentIcons.disabled = (disabled) + +! StageAction +StageAction.Stage = \u30B9\u30C6\u30FC\u30B8 + +! RecoveryDevice +RecoveryDevice.DeployEvent.LAUNCH = \u767A\u5C04(\u30D7\u30E9\u30B9NN\u79D2) +RecoveryDevice.DeployEvent.EJECTION = \u3053\u306E\u30B9\u30C6\u30FC\u30B8\u306E\u653E\u51FA\u85AC\u70B9\u706B +RecoveryDevice.DeployEvent.APOGEE = \u6700\u9AD8\u5230\u9054\u70B9 +RecoveryDevice.DeployEvent.ALTITUDE = \u964D\u4E0B\u4E2D\u306E\u7279\u5B9A\u306E\u9AD8\u5EA6 +RecoveryDevice.DeployEvent.CURRENT_STAGE_SEPARATION = \u73FE\u5728\u306E\u30B9\u30C6\u30FC\u30B8\u5206\u96E2 +RecoveryDevice.DeployEvent.LOWER_STAGE_SEPARATION = \u4E0B\u6BB5\u30B9\u30C6\u30FC\u30B8\u5206\u96E2 +RecoveryDevice.DeployEvent.NEVER = Never + +! FlightEvent +FlightEvent.Type.LAUNCH = \u30ED\u30FC\u30F3\u30C1 +FlightEvent.Type.IGNITION = \u30E2\u30FC\u30BF\u30FC\u70B9\u706B +FlightEvent.Type.LIFTOFF = \u30EA\u30D5\u30C8\u30AA\u30D5 +FlightEvent.Type.LAUNCHROD = \u30ED\u30FC\u30F3\u30C1\u30ED\u30C3\u30C9\u96E2\u8131 +FlightEvent.Type.BURNOUT = \u30E2\u30FC\u30BF\u30FC\u71C3\u713C\u7D42\u4E86 +FlightEvent.Type.EJECTION_CHARGE = \u653E\u51FA\u85AC +FlightEvent.Type.STAGE_SEPARATION = \u30B9\u30C6\u30FC\u30B8\u5206\u96E2 +FlightEvent.Type.APOGEE = \u6700\u9AD8\u5230\u9054\u70B9 +FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT = \u30EA\u30AB\u30D0\u30EA\u30FC\u88C5\u7F6E\u5C55\u958B +FlightEvent.Type.GROUND_HIT = \u7740\u5730 +FlightEvent.Type.SIMULATION_END = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u7D42\u4E86 +FlightEvent.Type.ALTITUDE = \u59FF\u52E2\u5909\u66F4 + +! ThrustCurveMotorColumns +TCurveMotorCol.MANUFACTURER = \u30E1\u30FC\u30AB\u30FC +TCurveMotorCol.DESIGNATION = \u8A18\u53F7 +TCurveMotorCol.TYPE = \u30BF\u30A4\u30D7 +TCurveMotorCol.DIAMETER = \u76F4\u5F84 +TCurveMotorCol.LENGTH = \u9577\u3055 + +TCurveMotor.ttip.diameter = \u76F4\u5F84\uFF1A +TCurveMotor.ttip.length = \u9577\u3055\uFF1A +TCurveMotor.ttip.maxThrust = \u6700\u5927\u63A8\u529B\uFF1A +TCurveMotor.ttip.avgThrust = \u5E73\u5747\u63A8\u529B\uFF1A +TCurveMotor.ttip.burnTime = \u71C3\u713C\u6642\u9593\uFF1A +TCurveMotor.ttip.totalImpulse = \u30C8\u30FC\u30BF\u30EB\u30A4\u30F3\u30D1\u30EB\u30B9\uFF1A +TCurveMotor.ttip.launchMass = \u70B9\u706B\u6642\u8CEA\u91CF\uFF1A +TCurveMotor.ttip.emptyMass = \u7A7A\u865A\u8CEA\u91CF\uFF1A + +! RocketInfo +RocketInfo.lengthLine.Length = \u9577\u3055 +RocketInfo.lengthLine.maxdiameter = , \u6700\u5927\u76F4\u5F84 +RocketInfo.massText1 = \u30E2\u30FC\u30BF\u30FC\u8FBC\u307F\u91CD\u91CF +RocketInfo.massText2 = \u30E2\u30FC\u30BF\u30FC\u7121\u3057\u91CD\u91CF +RocketInfo.at = at M= +RocketInfo.cgText = CG: +RocketInfo.cpText = CP: +RocketInfo.stabText = \u5B89\u5B9A\u6027: +RocketInfo.Warning = \u30A8\u30E9\u30FC: +RocketInfo.Calculating = \u8A08\u7B97\u4E2D... +RocketInfo.Apogee = \u6700\u5927\u9AD8\u5EA6\uFF1A +RocketInfo.Maxvelocity = \u6700\u5927\u901F\u5EA6\uFF1A +RocketInfo.Maxacceleration = \u6700\u5927\u52A0\u901F\u5EA6\uFF1A +RocketInfo.apogeeValue = N/A +RocketInfo.Mach = (Mach +RocketInfo.velocityValue = N/A +RocketInfo.accelerationValue = N/A + +! FinSet +FinSet.CrossSection.SQUARE = \u77E9\u5F62 +FinSet.CrossSection.ROUNDED = \u4E38\u307F\u3092\u5E2F\u3073\u305F +FinSet.CrossSection.AIRFOIL = \u7FFC\u578B +FinSet.TabRelativePosition.FRONT = Root chord leading edge +FinSet.TabRelativePosition.CENTER = Root chord midpoint +FinSet.TabRelativePosition.END = Root chord trailing edge + +! FlightDataType +FlightDataType.TYPE_TIME = \u6642\u9593 +FlightDataType.TYPE_ALTITUDE = \u9AD8\u5EA6 +FlightDataType.TYPE_VELOCITY_Z = \u5782\u76F4\u65B9\u5411\u901F\u5EA6 +FlightDataType.TYPE_ACCELERATION_Z = \u5782\u76F4\u65B9\u5411\u52A0\u901F\u5EA6 +FlightDataType.TYPE_VELOCITY_TOTAL = Total \u901F\u5EA6 +FlightDataType.TYPE_ACCELERATION_TOTAL = Total \u52A0\u901F\u5EA6 +FlightDataType.TYPE_POSITION_X = \u98A8\u4E0A\u65B9\u5411\u4F4D\u7F6E +FlightDataType.TYPE_POSITION_Y = \u98A8\u3068\u5782\u76F4\u65B9\u5411\u4F4D\u7F6E +FlightDataType.TYPE_POSITION_XY = \u6C34\u5E73\u65B9\u5411\u8DDD\u96E2 +FlightDataType.TYPE_POSITION_DIRECTION = \u6C34\u5E73\u65B9\u5411\u5411\u304D +FlightDataType.TYPE_VELOCITY_XY = \u901F\u5EA6\uFF08\u5074\u9762\uFF09 +FlightDataType.TYPE_ACCELERATION_XY = \u52A0\u901F\u5EA6\uFF08\u5074\u9762\uFF09 +FlightDataType.TYPE_AOA = \u8FCE\u3048\u89D2 +FlightDataType.TYPE_ROLL_RATE = \u89D2\u901F\u5EA6\uFF08\u30ED\u30FC\u30EB\uFF09 +FlightDataType.TYPE_PITCH_RATE = \u89D2\u901F\u5EA6\uFF08\u30D4\u30C3\u30C1\uFF09 +FlightDataType.TYPE_YAW_RATE = \u89D2\u901F\u5EA6\uFF08\u30E8\u30FC\uFF09 +FlightDataType.TYPE_MASS = \u8CEA\u91CF +FlightDataType.TYPE_PROPELLANT_MASS = \u8EF8\u65B9\u5411\u6163\u6027\u30E2\u30FC\u30E1\u30F3\u30C8 +FlightDataType.TYPE_LONGITUDINAL_INERTIA = \u9577\u624B\u65B9\u5411\u6163\u6027\u30E2\u30FC\u30E1\u30F3\u30C8 +FlightDataType.TYPE_ROTATIONAL_INERTIA = \u30ED\u30FC\u30EB\u65B9\u5411\u6163\u6027\u30E2\u30FC\u30E1\u30F3\u30C8 +FlightDataType.TYPE_CP_LOCATION = CP\u4F4D\u7F6E +FlightDataType.TYPE_CG_LOCATION = CG\u4F4D\u7F6E +FlightDataType.TYPE_STABILITY = Stability margin calibers +FlightDataType.TYPE_MACH_NUMBER = \u30DE\u30C3\u30CF\u6570 +FlightDataType.TYPE_REYNOLDS_NUMBER = \u30EC\u30A4\u30CE\u30EB\u30BA\u6570 +FlightDataType.TYPE_THRUST_FORCE = \u63A8\u529B +FlightDataType.TYPE_DRAG_FORCE = \u6297\u529B +FlightDataType.TYPE_DRAG_COEFF = \u6297\u529B\u4FC2\u6570 +FlightDataType.TYPE_AXIAL_DRAG_COEFF = \u8EF8\u65B9\u5411\u6297\u529B\u4FC2\u6570 +FlightDataType.TYPE_FRICTION_DRAG_COEFF = \u6469\u64E6\u6297\u529B\u4FC2\u6570 +FlightDataType.TYPE_PRESSURE_DRAG_COEFF = \u5727\u529B\u6297\u529B\u4FC2\u6570 +FlightDataType.TYPE_BASE_DRAG_COEFF = \u30D9\u30FC\u30B9\u6297\u529B\u4FC2\u6570 +FlightDataType.TYPE_NORMAL_FORCE_COEFF = \u529B\u4FC2\u6570\uFF08\u6CD5\u7DDA\u65B9\u5411\uFF09 +FlightDataType.TYPE_PITCH_MOMENT_COEFF = \u30E2\u30FC\u30E1\u30F3\u30C8\u4FC2\u6570\uFF08\u30D4\u30C3\u30C1\uFF09 +FlightDataType.TYPE_YAW_MOMENT_COEFF = \u30E2\u30FC\u30E1\u30F3\u30C8\u4FC2\u6570\uFF08\u30E8\u30FC\uFF09 +FlightDataType.TYPE_SIDE_FORCE_COEFF = \u6A2A\u529B\u4FC2\u6570 +FlightDataType.TYPE_ROLL_MOMENT_COEFF = \u30E2\u30FC\u30E1\u30F3\u30C8\u4FC2\u6570\uFF08\u30ED\u30FC\u30EB\uFF09 +FlightDataType.TYPE_ROLL_FORCING_COEFF = \u529B\u4FC2\u6570\uFF08\u30ED\u30FC\u30EB\uFF09 +FlightDataType.TYPE_ROLL_DAMPING_COEFF = \u6E1B\u8870\u4FC2\u6570\uFF08\u30ED\u30FC\u30EB\uFF09 +FlightDataType.TYPE_PITCH_DAMPING_MOMENT_COEFF = \u6E1B\u8870\u4FC2\u6570\uFF08\u30D4\u30C3\u30C1\uFF09 +FlightDataType.TYPE_YAW_DAMPING_MOMENT_COEFF = \u6E1B\u8870\u4FC2\u6570\uFF08\u30E8\u30FC\uFF09 +FlightDataType.TYPE_REFERENCE_LENGTH = \u4EE3\u8868\u9577\u3055 +FlightDataType.TYPE_REFERENCE_AREA = \u4EE3\u8868\u9762\u7A4D +FlightDataType.TYPE_ORIENTATION_THETA = \u5782\u76F4\u65B9\u5411\uFF08\u5929\u9802\u89D2\uFF09 +FlightDataType.TYPE_ORIENTATION_PHI = \u6C34\u5E73\u65B9\u5411\uFF08\u65B9\u4F4D\u89D2\uFF09 +FlightDataType.TYPE_WIND_VELOCITY = \u98A8\u901F +FlightDataType.TYPE_AIR_TEMPERATURE = \u6E29\u5EA6 +FlightDataType.TYPE_AIR_PRESSURE = \u6C17\u5727 +FlightDataType.TYPE_SPEED_OF_SOUND = \u97F3\u901F +FlightDataType.TYPE_TIME_STEP = \u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u6642\u9593\u30B9\u30C6\u30C3\u30D7 +FlightDataType.TYPE_COMPUTATION_TIME = \u8A08\u7B97\u6642\u9593 +FlightDataType.TYPE_LATITUDE = \u7DEF\u5EA6 +FlightDataType.TYPE_LONGITUDE = \u7D4C\u5EA6 +FlightDataType.TYPE_CORIOLIS_ACCELERATION = \u30B3\u30EA\u30AA\u30EA\u52A0\u901F\u5EA6 +FlightDataType.TYPE_GRAVITY = \u91CD\u529B\u52A0\u901F\u5EA6 + +! PlotConfiguration +PlotConfiguration.Verticalmotion = \u5782\u76F4\u904B\u52D5 vs. \u6642\u9593 +PlotConfiguration.Totalmotion = \u904B\u52D5 vs. \u6642\u9593 +PlotConfiguration.Flightside = \u6A2A\u304B\u3089\u898B\u305F\u30D5\u30E9\u30A4\u30C8\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB +PlotConfiguration.Stability = \u5B89\u5B9A\u6027 vs. \u6642\u9593 +PlotConfiguration.Dragcoef = \u6297\u529B\u4FC2\u6570 vs. \u30DE\u30C3\u30CF\u6570 +PlotConfiguration.Rollcharacteristics = \u30ED\u30FC\u30EB\u65B9\u5411\u5909\u6570 +PlotConfiguration.Angleofattack = \u8FCE\u3048\u89D2\u3068\u65B9\u4F4D vs. \u6642\u9593 +PlotConfiguration.Simulationtime = \u6642\u9593\u30B9\u30C6\u30C3\u30D7\u3068\u8A08\u7B97\u6642\u9593 + +! Warning +Warning.LargeAOA.str1 = \u8FCE\u3048\u89D2\u304C\u5927\u304D\u3059\u304E\u307E\u3059 +Warning.LargeAOA.str2 = \u8FCE\u3048\u89D2\u304C\u5927\u304D\u3059\u304E\u307E\u3059( +Warning.DISCONTINUITY = \u30DC\u30C7\u30A3\u306E\u76F4\u5F84\u304C\u4E0D\u9023\u7D9A\u3067\u3059 +Warning.THICK_FIN = \u539A\u3044\u30D5\u30A3\u30F3\u306F\u6B63\u78BA\u306B\u30E2\u30C7\u30EA\u30F3\u30B0\u3067\u304D\u307E\u305B\u3093 +Warning.JAGGED_EDGED_FIN = \u30AE\u30B6\u30AE\u30B6\u306E\u7E01\u306E\u30D5\u30A3\u30F3\u306E\u4E88\u6E2C\u306F\u6B63\u78BA\u306B\u3067\u304D\u307E\u305B\u3093 +Warning.LISTENERS_AFFECTED = Listeners modified the flight simulation +Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING = \u30EA\u30D0\u30AB\u30EA\u30FC\u88C5\u7F6E\u304C\u71C3\u713C\u4E2D\u306B\u958B\u3044\u3066\u3044\u307E\u3059 +Warning.FILE_INVALID_PARAMETER = \u7121\u52B9\u306A\u30D1\u30E9\u30E1\u30FC\u30BF\u3067\u3059\uFF0C\u7121\u8996\u3057\u307E\u3059 +Warning.PARALLEL_FINS = \u30D5\u30A3\u30F3\u304C\u591A\u3059\u304E\u307E\u3059 +Warning.SUPERSONIC = \u8D85\u97F3\u901F\u3067\u306E\u30DC\u30C7\u30A3\u306E\u8A08\u7B97\u306F\u5168\u304F\u6B63\u78BA\u3067\u306F\u3042\u308A\u307E\u305B\u3093 +Warning.RECOVERY_LAUNCH_ROD = \u30EA\u30AB\u30D0\u30EA\u30FC\u88C5\u7F6E\u304C\u30ED\u30FC\u30F3\u30C1\u30AC\u30A4\u30C9\u306B\u3042\u308B\u3068\u3053\u308D\u3067\u5C55\u958B\u3057\u3066\u3044\u307E\u3059 +Warning.RECOVERY_HIGH_SPEED = \u30EA\u30AB\u30D0\u30EA\u30FC\u88C5\u7F6E\u304C\u65E9\u3044\u901F\u5EA6\u3067\u5C55\u958B\u3057\u3066\u3044\u307E\u3059 + + +! Scale dialog +ScaleDialog.lbl.scaleRocket = \u5168\u4F53 +ScaleDialog.lbl.scaleSubselection = \u3059\u3079\u3066\u306E\u90E8\u54C1\u3092\u9078\u629E +ScaleDialog.lbl.scaleSelection = \u9078\u629E\u3057\u305F\u90E8\u54C1\u306E\u307F +ScaleDialog.title = Scale design +ScaleDialog.lbl.scale = \u30B9\u30B1\u30FC\u30EB\uFF1A +ScaleDialog.lbl.scale.ttip = \u62E1\u5927\u7E2E\u5C0F\u3092\u5168\u4F53\u3067\u884C\u3046\u304B\u3001\u73FE\u5728\u9078\u629E\u3057\u3066\u3044\u308B\u90E8\u54C1\u3060\u3051\u306A\u306E\u304B\u9078\u629E +ScaleDialog.lbl.scaling = \u62E1\u5927\u7E2E\u5C0F\u7387\uFF1A +ScaleDialog.lbl.scaling.ttip = 100\uFF05\u4EE5\u4E0B\u3067\u306F\u7E2E\u5C0F\u3057\u3001100%\u4EE5\u4E0A\u3067\u306F\u62E1\u5927 +! The scaleFrom/scaleTo pair creates a phrase "Scale from [...] to [...]" +ScaleDialog.lbl.scaleFrom = \u62E1\u5927\u7E2E\u5C0F from +ScaleDialog.lbl.scaleTo = to +ScaleDialog.lbl.scaleFromTo.ttip = \u5143\u3068\u62E1\u5927\u7E2E\u5C0F\u3057\u305F\u7D50\u679C\u306E\u9577\u3055\u306B\u3088\u308B\u5B9A\u7FA9 +ScaleDialog.checkbox.scaleMass = \u8CEA\u91CF\u306E\u6B63\u78BA\u306A\u66F4\u65B0 +ScaleDialog.checkbox.scaleMass.ttip = \u62E1\u5927\u7E2E\u5C0F\u7387\u306E3\u4E57\u3067\u8CEA\u91CF\u306E\u5024\u3092\u518D\u5B9A\u7FA9 +ScaleDialog.button.scale = \u62E1\u5927\u7E2E\u5C0F +ScaleDialog.undo.scaleRocket = \u30ED\u30B1\u30C3\u30C8\u306E\u62E1\u5927\u7E2E\u5C0F +ScaleDialog.undo.scaleComponent = \u90E8\u54C1\u306E\u62E1\u5927\u7E2E\u5C0F +ScaleDialog.undo.scaleComponents = \u90E8\u54C1\u306E\u62E1\u5927\u7E2E\u5C0F + +!icons +Icons.Undo = \u623B\u308B +Icons.Redo = \u9032\u3080 + +OpenRocketPrintable.Partsdetail = \u90E8\u54C1\u8A73\u7D30 +OpenRocketPrintable.Fintemplates = \u30D5\u30A3\u30F3\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8 +OpenRocketPrintable.Transitiontemplates = \u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8 +OpenRocketPrintable.Noseconetemplates = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8 +OpenRocketPrintable.Finmarkingguide = \u30D5\u30A3\u30F3\u306E\u76EE\u5370 +OpenRocketPrintable.DesignReport = \u30C7\u30B6\u30A4\u30F3\u30EC\u30DD\u30FC\u30C8 +OpenRocketPrintable.Centeringringtemplates = \u30BB\u30F3\u30BF\u30FC\u30EA\u30F3\u30B0\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8 + +OpenRocketDocument.Redo = \u623B\u308B +OpenRocketDocument.Undo = \u9032\u3080 + +!EllipticalFinSet +EllipticalFinSet.Ellipticalfinset = Elliptical fin set + +! Optimization + +! Modifiers + +optimization.modifier.nosecone.length = \u9577\u3055 +optimization.modifier.nosecone.length.desc = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306E\u9577\u3055\u306E\u6700\u9069\u5316 +optimization.modifier.nosecone.diameter = \u76F4\u5F84 +optimization.modifier.nosecone.diameter.desc = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306E\u76F4\u5F84\u306E\u6700\u9069\u5316 +optimization.modifier.nosecone.thickness = \u539A\u3055 +optimization.modifier.nosecone.thickness.desc = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306E\u539A\u3055\u306E\u6700\u9069\u5316 +optimization.modifier.nosecone.shapeparameter = \u5F62\u72B6\u30D1\u30E9\u30E1\u30FC\u30BF +optimization.modifier.nosecone.shapeparameter.desc = \u30CE\u30FC\u30BA\u30B3\u30FC\u30F3\u306E\u5F62\u72B6\u30D1\u30E9\u30E1\u30FC\u30BF\u306E\u6700\u9069\u5316 + +optimization.modifier.transition.length = \u9577\u3055 +optimization.modifier.transition.length.desc = \u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u306E\u9577\u3055\u306E\u6700\u9069\u5316 +optimization.modifier.transition.forediameter = \u524D\u65B9\u76F4\u5F84 +optimization.modifier.transition.forediameter.desc = \u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u306E\u524D\u65B9\u76F4\u5F84\u306E\u6700\u9069\u5316 +optimization.modifier.transition.aftdiameter = \u5F8C\u65B9\u76F4\u5F84 +optimization.modifier.transition.aftdiameter.desc = \u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u306E\u5F8C\u65B9\u76F4\u5F84\u306E\u6700\u9069\u5316 +optimization.modifier.transition.thickness = \u539A\u3055 +optimization.modifier.transition.thickness.desc = \u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u306E\u8089\u539A\u306E\u6700\u9069\u5316 +optimization.modifier.transition.shapeparameter = \u5F62\u72B6\u30D1\u30E9\u30E1\u30FC\u30BF +optimization.modifier.transition.shapeparameter.desc = \u30C8\u30E9\u30F3\u30B8\u30B7\u30E7\u30F3\u306E\u5F62\u72B6\u30D1\u30E9\u30E1\u30FC\u30BF\u306E\u6700\u9069\u5316 + +optimization.modifier.bodytube.length = \u9577\u3055 +optimization.modifier.bodytube.length.desc = \u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6\u306E\u9577\u3055\u306E\u6700\u9069\u5316 +optimization.modifier.bodytube.outerDiameter = \u5916\u5F84 +optimization.modifier.bodytube.outerDiameter.desc = \u8089\u539A\u3092\u5909\u3048\u305A\u306B\u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6\u306E\u5916\u5F84\u306E\u6700\u9069\u5316 +optimization.modifier.bodytube.thickness = \u539A\u3055 +optimization.modifier.bodytube.thickness.desc = \u30DC\u30C7\u30A3\u30C1\u30E5\u30FC\u30D6\u306E\u8089\u539A\u306E\u6700\u9069\u5316 + +optimization.modifier.trapezoidfinset.rootChord = \u6839\u672C\u9577\u3055 +optimization.modifier.trapezoidfinset.rootChord.desc = \u30D5\u30A3\u30F3\u306E\u6839\u672C\u306E\u9577\u3055\u306E\u6700\u9069\u5316 (length of fin at the rocket body). +optimization.modifier.trapezoidfinset.tipChord = \u7AEF\u90E8\u9577\u3055 +optimization.modifier.trapezoidfinset.tipChord.desc = \u30D5\u30A3\u30F3\u306E\u7AEF\u90E8\u306E\u9577\u3055\u306E\u6700\u9069\u5316 (length of fin at outer edge). +optimization.modifier.trapezoidfinset.sweep = Sweep +optimization.modifier.trapezoidfinset.sweep.desc = \u30D5\u30A3\u30F3\u306ESweep\u306E\u6700\u9069\u5316 (distance that the leading edge sweeps backwards). +optimization.modifier.trapezoidfinset.height = \u9AD8\u3055 +optimization.modifier.trapezoidfinset.height.desc = \u30D5\u30A3\u30F3\u306E\u9AD8\u3055\u306E\u6700\u9069\u5316 + +optimization.modifier.ellipticalfinset.length = \u6839\u672C\u9577\u3055 +optimization.modifier.ellipticalfinset.length.desc = \u30D5\u30A3\u30F3\u306E\u6839\u672C\u9577\u3055\u306E\u6700\u9069\u5316 +optimization.modifier.ellipticalfinset.height = \u9AD8\u3055 +optimization.modifier.ellipticalfinset.height.desc = \u30D5\u30A3\u30F3\u306E\u9AD8\u3055\u306E\u6700\u9069\u5316 + +optimization.modifier.finset.cant = \u50BE\u659C\u89D2 +optimization.modifier.finset.cant.desc = \u30D5\u30A3\u30F3\u306E\u50BE\u659C\u89D2\u306E\u6700\u9069\u5316 +optimization.modifier.finset.position = \u4F4D\u7F6E +optimization.modifier.finset.position.desc = \u30D5\u30A3\u30F3\u306E\u30ED\u30B1\u30C3\u30C8\u30DC\u30C7\u30A3\u306E\u5BFE\u3057\u3066\u306E\u4F4D\u7F6E\u306E\u6700\u9069\u5316 + +optimization.modifier.launchlug.length = \u9577\u3055 +optimization.modifier.launchlug.length.desc = \u30ED\u30FC\u30F3\u30C1\u30E9\u30B0\u306E\u9577\u3055\u306E\u6700\u9069\u5316 +optimization.modifier.launchlug.outerDiameter = \u5916\u5F84 +optimization.modifier.launchlug.outerDiameter.desc = \u30ED\u30FC\u30F3\u30C1\u30E9\u30B0\u306E\u5916\u5F84\u306E\u6700\u9069\u5316 +optimization.modifier.launchlug.thickness = \u539A\u3055 +optimization.modifier.launchlug.thickness.desc = \u30ED\u30FC\u30F3\u30C1\u30E9\u30B0\u306E\u5916\u5F84\u3092\u4FDD\u3063\u305F\u307E\u307E\u539A\u3055\u306E\u6700\u9069\u5316 +optimization.modifier.launchlug.position = \u4F4D\u7F6E +optimization.modifier.launchlug.position.desc = \u30ED\u30FC\u30F3\u30C1\u30E9\u30B0\u306E\u30ED\u30B1\u30C3\u30C8\u30DC\u30C7\u30A3\u306E\u5BFE\u3057\u3066\u306E\u4F4D\u7F6E\u306E\u6700\u9069\u5316 + + +optimization.modifier.internalcomponent.position = \u4F4D\u7F6E +optimization.modifier.internalcomponent.position.desc = \u90E8\u54C1\u306E\u4F4D\u7F6E\u306E\u6700\u9069\u5316 + +optimization.modifier.masscomponent.mass = \u8CEA\u91CF +optimization.modifier.masscomponent.mass.desc = \u304A\u3082\u308A\u306E\u8CEA\u91CF\u306E\u6700\u9069\u5316 + +optimization.modifier.parachute.diameter = \u76F4\u5F84 +optimization.modifier.parachute.diameter.desc = \u30D1\u30E9\u30B7\u30E5\u30FC\u30C8\u306E\u5098\u76F4\u5F84\u306E\u6700\u9069\u5316 +optimization.modifier.parachute.coefficient = \u6297\u529B\u4FC2\u6570 +optimization.modifier.parachute.coefficient.desc = \u30D1\u30E9\u30B7\u30E5\u30FC\u30C8\u306E\u6297\u529B\u4FC2\u6570\u306E\u6700\u9069\u5316\u3002\u901A\u5E38\u306E\u30D1\u30E9\u30B7\u30E5\u30FC\u30C8\u3067\u306F0.8\u3067\u3042\u308B + +optimization.modifier.streamer.length = \u9577\u3055 +optimization.modifier.streamer.length.desc = \u30B9\u30C8\u30EA\u30FC\u30DE\u30FC\u306E\u9577\u3055\u306E\u6700\u9069\u5316 +optimization.modifier.streamer.width = \u5E45 +optimization.modifier.streamer.width.desc = \u30B9\u30C8\u30EA\u30FC\u30DE\u30FC\u306E\u5E45\u306E\u6700\u9069\u5316 +optimization.modifier.streamer.aspectRatio = \u30A2\u30B9\u30DA\u30AF\u30C8\u6BD4 +optimization.modifier.streamer.aspectRatio.desc = \u30B9\u30C8\u30EA\u30FC\u30DE\u30FC\u306E\u30A2\u30B9\u30DA\u30AF\u30C8\u6BD4\uFF08\u9577\u3055/\u5E45\uFF09\u306E\u6700\u9069\u5316\u3002\u30A2\u30B9\u30DA\u30AF\u30C8\u6BD4\u3068\u9577\u3055\u3084\u5E45\u3092\u540C\u6642\u306B\u9078\u629E\u3057\u3066\u306F\u306A\u3089\u306A\u3044 +optimization.modifier.streamer.coefficient = \u6297\u529B\u4FC2\u6570 +optimization.modifier.streamer.coefficient.desc = \u30B9\u30C8\u30EA\u30FC\u30DE\u30FC\u306E\u6297\u529B\u4FC2\u6570\u306E\u6700\u9069\u5316 + +optimization.modifier.recoverydevice.deployDelay = \u5C55\u958B\u9045\u308C\u6642\u9593 +optimization.modifier.recoverydevice.deployDelay.desc = \u30EA\u30AB\u30D0\u30EA\u30FC\u88C5\u7F6E\u306E\u5C55\u958B\u9045\u308C\u306E\u6700\u9069\u5316 +optimization.modifier.recoverydevice.deployAltitude = \u5C55\u958B\u9AD8\u5EA6 +optimization.modifier.recoverydevice.deployAltitude.desc = \u30EA\u30AB\u30D0\u30EA\u30FC\u88C5\u7F6E\u306E\u5C55\u958B\u9AD8\u5EA6\u306E\u6700\u9069\u5316 + +optimization.modifier.rocketcomponent.overrideMass = \u8CEA\u91CF\u306E\u4E0A\u66F8\u304D +optimization.modifier.rocketcomponent.overrideMass.desc = \u90E8\u54C1\u306E\u8CEA\u91CF\u306E\u4E0A\u66F8\u304D\u306E\u6700\u9069\u5316 +optimization.modifier.rocketcomponent.overrideCG = CG\u306E\u4E0A\u66F8\u304D +optimization.modifier.rocketcomponent.overrideCG.desc = \u90E8\u54C1\u306E\u91CD\u5FC3\u306E\u4E0A\u66F8\u304D\u306E\u6700\u9069\u5316 + +optimization.modifier.motormount.overhang = \u30E2\u30FC\u30BF\u30FC\u306E\u5F35\u308A\u51FA\u3057 +optimization.modifier.motormount.overhang.desc = \u30E2\u30FC\u30BF\u30FC\u306E\u5F35\u308A\u51FA\u3057\u306E\u6700\u9069\u5316 +optimization.modifier.motormount.delay = \u30E2\u30FC\u30BF\u30FC\u306E\u70B9\u706B\u306E\u9045\u308C +optimization.modifier.motormount.delay.desc = \u30E2\u30FC\u30BF\u30FC\u70B9\u706B\u306E\u9045\u308C\u306E\u6700\u9069\u5316 + + + + +! General rocket design optimization dialog + +GeneralOptimizationDialog.title = Rocket optimization +GeneralOptimizationDialog.goal.maximize = \u6700\u5927\u5024 +GeneralOptimizationDialog.goal.minimize = \u6700\u5C0F\u5024 +GeneralOptimizationDialog.goal.seek = \u5024\u306E\u6307\u5B9A +GeneralOptimizationDialog.btn.start = \u6700\u9069\u5316\u958B\u59CB +GeneralOptimizationDialog.btn.stop = \u6700\u9069\u5316\u4E2D\u6B62 +GeneralOptimizationDialog.lbl.paramsToOptimize = \u6700\u9069\u5316\u30D1\u30E9\u30E1\u30FC\u30BF\uFF1A +GeneralOptimizationDialog.btn.add = \u8FFD\u52A0 +GeneralOptimizationDialog.btn.add.ttip = \u6700\u9069\u5316\u306B\u9078\u629E\u3057\u305F\u30D1\u30E9\u30E1\u30FC\u30BF\u8FFD\u52A0 +GeneralOptimizationDialog.btn.remove = \u524A\u9664 +GeneralOptimizationDialog.btn.remove.ttip = \u6700\u9069\u5316\u304B\u3089\u9078\u629E\u3057\u305F\u30D1\u30E9\u30E1\u30FC\u30BF\u524A\u9664 +GeneralOptimizationDialog.btn.removeAll = \u5168\u3066\u524A\u9664 +GeneralOptimizationDialog.btn.removeAll.ttip = \u6700\u9069\u5316\u304B\u3089\u3059\u3079\u3066\u306E\u30D1\u30E9\u30E1\u30FC\u30BF\u524A\u9664 +GeneralOptimizationDialog.lbl.availableParams = \u9078\u629E\u53EF\u80FD\u306A\u30D1\u30E9\u30E1\u30FC\u30BF\uFF1A +GeneralOptimizationDialog.lbl.optimizationOpts = \u6700\u9069\u5316\u30AA\u30D7\u30B7\u30E7\u30F3 +GeneralOptimizationDialog.lbl.optimizeSim = \u6700\u9069\u5316\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3 +GeneralOptimizationDialog.lbl.optimizeSim.ttip = \u9078\u629E\u3057\u305F\u30B7\u30DF\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3\u3092\u6700\u9069\u5316 +GeneralOptimizationDialog.lbl.optimizeValue = \u6700\u9069\u5316\u3059\u308B\u5024\uFF1A +GeneralOptimizationDialog.lbl.optimizeValue.ttip = \u6700\u9069\u5316\u3059\u308B\u5024\u306E\u9078\u629E +GeneralOptimizationDialog.lbl.optimizeGoal = \u6700\u9069\u5316\u30B4\u30FC\u30EB +GeneralOptimizationDialog.lbl.optimizeGoal.ttip = \u6700\u9069\u5316\u306E\u30B4\u30FC\u30EB\u306E\u9078\u629E +GeneralOptimizationDialog.lbl.optimizeGoalValue.ttip = \u5024\u306E\u63A2\u7D22 +GeneralOptimizationDialog.lbl.requireStability = \u6C42\u3081\u3089\u308C\u308B\u5B89\u5B9A\u6027 +GeneralOptimizationDialog.lbl.requireMinStability = \u6700\u5C0F\u5B89\u5B9A\u6027\uFF1A +GeneralOptimizationDialog.lbl.requireMinStability.ttip = \u6C42\u3081\u3089\u308C\u308B\u6700\u5C0F\u306E\u9759\u5B89\u5B9A\u4F59\u88D5 +GeneralOptimizationDialog.lbl.requireMaxStability = \u6700\u5927\u5B89\u5B9A\u6027\uFF1A +GeneralOptimizationDialog.lbl.requireMaxStability.ttip = \u6C42\u3081\u3089\u308C\u308B\u6700\u5927\u306E\u9759\u5B89\u5B9A\u4F59\u88D5 +GeneralOptimizationDialog.status.bestValue = \u6700\u9069\u5024\uFF1A +GeneralOptimizationDialog.status.bestValue.ttip = \u73FE\u72B6\u898B\u3064\u304B\u308B\u30D9\u30B9\u30C8\u306A\u6700\u9069\u5024 +GeneralOptimizationDialog.status.stepCount = \u30B9\u30C6\u30C3\u30D7\u6570\uFF1A +GeneralOptimizationDialog.status.stepCount.ttip = \u6700\u9069\u5316\u306E\u305F\u3081\u306B\u5B9F\u884C\u3055\u308C\u305F\u30B9\u30C6\u30C3\u30D7\u6570 +GeneralOptimizationDialog.status.evalCount = \u8A55\u4FA1\uFF1A +GeneralOptimizationDialog.status.evalCount.ttip = \u5B9F\u884C\u3055\u308C\u305F\u8A55\u4FA1\u95A2\u6570\u306E\u7DCF\u6570 +GeneralOptimizationDialog.status.stepSize = \u30B9\u30C6\u30C3\u30D7\u30B5\u30A4\u30BA\uFF1A +GeneralOptimizationDialog.status.stepSize.ttip = \u73FE\u5728\u306E\u6700\u9069\u5316\u306E\u30B9\u30C6\u30C3\u30D7\u30B5\u30A4\u30BA\uFF08\u6700\u9069\u5316\u306E\u30D1\u30E9\u30E1\u30FC\u30BF\u7BC4\u56F2\u3068\u95A2\u4FC2\uFF09 +GeneralOptimizationDialog.btn.plotPath = \u7D4C\u8DEF\u306E\u30D7\u30ED\u30C3\u30C8 +GeneralOptimizationDialog.btn.plotPath.ttip = \u6700\u9069\u5316\u7D4C\u8DEF\u306E\u30B0\u30E9\u30D5\uFF08\u4E00\u3064\u304B2\u3064\u306E\u6B21\u5143\u306E\u307F\u6700\u9069\u5316\uFF09 +GeneralOptimizationDialog.btn.save = \u7D4C\u8DEF\u306E\u4FDD\u5B58 +GeneralOptimizationDialog.btn.save.ttip = \u8A55\u4FA1\u95A2\u6570\u306E\u7D50\u679C\u3092CSV\u30D5\u30A1\u30A4\u30EB\u3068\u3057\u3066\u4FDD\u5B58 +GeneralOptimizationDialog.btn.apply = \u6700\u9069\u5316\u306E\u9069\u7528 +GeneralOptimizationDialog.btn.apply.ttip = \u30ED\u30B1\u30C3\u30C8\u30C7\u30B6\u30A4\u30F3\u306B\u6700\u9069\u5316\u3057\u305F\u7D50\u679C\u3092\u9069\u7528 +GeneralOptimizationDialog.btn.reset = \u30EA\u30BB\u30C3\u30C8 +GeneralOptimizationDialog.btn.reset.ttip = \u73FE\u5728\u306E\u30ED\u30B1\u30C3\u30C8\u30C7\u30B6\u30A4\u30F3\u306B\u30EA\u30BB\u30C3\u30C8 +GeneralOptimizationDialog.btn.close = \u9589\u3058\u308B +GeneralOptimizationDialog.btn.close.ttip = \u30ED\u30B1\u30C3\u30C8\u30C7\u30B6\u30A4\u30F3\u306B\u5909\u66F4\u3092\u52A0\u3048\u305A\u306B\u30C0\u30A4\u30A2\u30ED\u30B0\u3092\u9589\u3058\u308B +GeneralOptimizationDialog.error.selectParams.text = \u306F\u3058\u3081\u306B\u5229\u7528\u53EF\u80FD\u306A\u30D1\u30E9\u30E1\u30FC\u30BF\u304B\u3089\u6700\u9069\u5316\u3059\u308B\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044 +GeneralOptimizationDialog.error.selectParams.title = \u6700\u9069\u5316\u30D1\u30E9\u30E1\u30FC\u30BF\u306E\u9078\u629E +GeneralOptimizationDialog.error.optimizationFailure.text = \u6700\u9069\u5316\u306E\u5B9F\u884C\u5931\u6557\uFF1A +GeneralOptimizationDialog.error.optimizationFailure.title = \u6700\u9069\u5316\u306F\u5931\u6557\u3057\u307E\u3057\u305F +GeneralOptimizationDialog.undoText = \u6700\u9069\u5316\u306E\u9069\u7528 +GeneralOptimizationDialog.basicSimulationName = Basic simulation +GeneralOptimizationDialog.noSimulationName = No simulation +GeneralOptimizationDialog.table.col.parameter = \u30D1\u30E9\u30E1\u30FC\u30BF +GeneralOptimizationDialog.table.col.current = \u73FE\u5728 +GeneralOptimizationDialog.table.col.min = \u6700\u5C0F\u5024 +GeneralOptimizationDialog.table.col.max = \u6700\u5927\u5024 +GeneralOptimizationDialog.export.header = \u30D8\u30C3\u30C0\u30FC\u3092\u542B\u3080 +GeneralOptimizationDialog.export.header.ttip = \u30D5\u30A3\u30FC\u30EB\u30C9\u306E\u8AAC\u660E\u3092\u6700\u521D\u306E\u884C\u306B\u30D8\u30C3\u30C0\u30FC\u3068\u3057\u3066\u542B\u3080 +GeneralOptimizationDialog.export.stability = \u5B89\u5B9A\u6027 + + +! Dialog for plotting optimization results +OptimizationPlotDialog.title = \u6700\u9069\u5316\u7D50\u679C +OptimizationPlotDialog.lbl.zoomInstructions = \u30AF\u30EA\u30C3\u30AF\u3068\u30C9\u30E9\u30C3\u30B0 \u4E0B+\u53F3 \u3067\u30BA\u30FC\u30E0\u30A4\u30F3, \u4E0A+\u5DE6 \u3067\u30BA\u30FC\u30E0\u30A2\u30A6\u30C8 +OptimizationPlotDialog.plot1d.title = \u6700\u9069\u5316\u7D50\u679C +OptimizationPlotDialog.plot1d.series = \u6700\u9069\u5316\u7D50\u679C +OptimizationPlotDialog.plot2d.title = \u6700\u9069\u5316\u7D4C\u904E +OptimizationPlotDialog.plot2d.path = \u6700\u9069\u5316\u7D4C\u904E +OptimizationPlotDialog.plot2d.evals = \u8A55\u4FA1 +OptimizationPlotDialog.plot.ttip.stability = \u5B89\u5B9A\u6027\uFF1A +OptimizationPlotDialog.plot.label.optimum = \u6700\u9069\u6761\u4EF6 + +! Optimization parameters +MaximumAltitudeParameter.name = \u6700\u9AD8\u9AD8\u5EA6 +MaximumVelocityParameter.name = \u6700\u5927\u901F\u5EA6 +MaximumAccelerationParameter.name = \u6700\u5927\u52A0\u901F\u5EA6 +StabilityParameter.name = \u5B89\u5B9A\u6027 +GroundHitVelocityParameter.name = \u7740\u5730\u901F\u5EA6 +LandingDistanceParameter.name = \u7740\u5730\u8DDD\u96E2 +TotalFlightTimeParameter.name = \u7DCF\u30D5\u30E9\u30A4\u30C8\u6642\u9593 +DeploymentVelocityParameter.name = \u30D1\u30E9\u30B7\u30E5\u30FC\u30C8\u5C55\u958B\u6642\u901F\u5EA6 + + +! Compass directions drawn on a compass rose. +CompassRose.lbl.north = N +CompassRose.lbl.east = E +CompassRose.lbl.south = S +CompassRose.lbl.west = W + +! Compass directions with subdirections. These might not be localized even if the directions on the compass rose are. +CompassSelectionButton.lbl.N = N +CompassSelectionButton.lbl.NE = NE +CompassSelectionButton.lbl.E = E +CompassSelectionButton.lbl.SE = SE +CompassSelectionButton.lbl.S = S +CompassSelectionButton.lbl.SW = SW +CompassSelectionButton.lbl.W = W +CompassSelectionButton.lbl.NW = NW + + +SlideShowDialog.btn.next = \u6B21 +SlideShowDialog.btn.prev = \u524D + +SlideShowLinkListener.error.title = \u30C4\u30A2\u30FC\u30AC\u30A4\u30C9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093 +SlideShowLinkListener.error.msg = \u9078\u629E\u3055\u308C\u305F\u30C4\u30A2\u30FC\u306F\u307E\u3060\u66F8\u304B\u308C\u3066\u3044\u307E\u305B\u3093 + +GuidedTourSelectionDialog.title = \u30C4\u30A2\u30FC\u30AC\u30A4\u30C9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093 +GuidedTourSelectionDialog.lbl.selectTour = \u30C4\u30A2\u30FC\u306E\u9078\u629E\uFF1A +GuidedTourSelectionDialog.lbl.description = \u30C4\u30A2\u30FC\u306E\u8AAC\u660E\uFF1A +GuidedTourSelectionDialog.lbl.length = \u30B9\u30E9\u30A4\u30C9\u679A\u6570\uFF1A +GuidedTourSelectionDialog.btn.start = \u30C4\u30A2\u30FC\u30B9\u30BF\u30FC\u30C8\uFF01 + + +! Custom Fin BMP Importer +CustomFinImport.button.label = \u753B\u50CF\u304B\u3089\u30A4\u30F3\u30DD\u30FC\u30C8 +CustomFinImport.badFinImage = \u7121\u52B9\u306A\u30D5\u30A3\u30F3\u753B\u50CF\u3067\u3059\u3002\u9ED2\u3084\u6697\u3044\u8272\u306E\u5B9F\u7DDA\u304C\u753B\u50CF\u306E\u5E95\u9762\u306B\u63A5\u3057\u3066\u3044\u308B\u304B\u78BA\u8A8D\u3057\u3066\u4E0B\u3055\u3044 +CustomFinImport.errorLoadingFile = \u30D5\u30A1\u30A4\u30EB\u306E\u8AAD\u307F\u8FBC\u307F\u30A8\u30E9\u30FC\uFF1A +CustomFinImport.errorParsingFile = \u30D5\u30A3\u30F3\u753B\u50CF\u69CB\u9020\u30A8\u30E9\u30FC\uFF1A +CustomFinImport.undo = \u81EA\u7531\u5F62\u30D5\u30A3\u30F3\u306E\u5F62\u72B6\u30A4\u30F3\u30DD\u30FC\u30C8 +CustomFinImport.error.title = \u30D5\u30A3\u30F3\u8F2A\u90ED\u306E\u8AAD\u307F\u8FBC\u307F\u30A8\u30E9\u30FC +CustomFinImport.error.badimage = \u753B\u50CF\u304B\u3089\u30D5\u30A3\u30F3\u5F62\u72B6\u3092\u63A8\u5B9A\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u305B\u3093 +CustomFinImport.description = \u753B\u50CF\u306F\u5185\u90E8\u3067\u767D\u80CC\u666F\u3068\u9ED2\u7DDA\u306B\u5909\u63DB\u3055\u308C\u307E\u3059\u3002\u306A\u306E\u3067\u30D5\u30A3\u30F3\u306B\u306F\u6697\u3044\u8272\u306E\u5B9F\u7DDA\u3001\u80CC\u666F\u306B\u306F\u767D\u304B\u660E\u308B\u3044\u8272\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30D5\u30A3\u30F3\u306F\u753B\u50CF\u306E\u5E95\u9762\u306B\u63A5\u3057\u3066\u3044\u306A\u304F\u3066\u306F\u3044\u3051\u307E\u305B\u3093\u3001\u3053\u308C\u306F\u30D5\u30A3\u30F3\u306E\u5E95\u9762\u306B\u306A\u308A\u307E\u3059\u3002 + + +PresetModel.lbl.select = Select preset +PresetModel.lbl.database = From database... + + +! Component Preset Chooser Dialog +ComponentPresetChooserDialog.title = Choose component preset +ComponentPresetChooserDialog.filter.label = Filter by text: +ComponentPresetChooserDialog.checkbox.filterAftDiameter = \u5F8C\u65B9\u306E\u76F4\u5F84\u306B\u5408\u308F\u305B\u308B +ComponentPresetChooserDialog.checkbox.filterForeDiameter = \u524D\u65B9\u306E\u76F4\u5F84\u306B\u5408\u308F\u305B\u308B +ComponentPresetChooserDialog.menu.sortAsc = \u6607\u9806\u30BD\u30FC\u30C8 +ComponentPresetChooserDialog.menu.sortDesc = \u964D\u9806\u30BD\u30FC\u30C8 +ComponentPresetChooserDialog.menu.units = \u5358\u4F4D +ComponentPresetChooserDialog.checkbox.showAllCompatible = \u3059\u3079\u3066\u306E\u90E8\u54C1\u3092\u8868\u793A +ComponentPresetChooserDialog.lbl.favorites = Select to add preset to drop-down menu +table.column.Favorite = \u304A\u6C17\u306B\u5165\u308A +table.column.Manufacturer = \u88FD\u9020\u4F1A\u793E +table.column.PartNo = \u30D1\u30FC\u30C4\u6570 +table.column.Description = \u8AAC\u660E +table.column.Type = \u30BF\u30A4\u30D7 +table.column.Length = \u9577\u3055 +table.column.Width = \u5E45 +table.column.InnerDiameter = \u5185\u5F84 +table.column.OuterDiameter = \u5916\u5F84 +table.column.AftOuterDiameter = \u5F8C\u65B9\u5916\u5F84 +table.column.AftShoulderLength = \u5F8C\u65B9\u30B7\u30E7\u30EB\u30C0\u30FC\u9577\u3055 +table.column.AftShoulderDiameter = \u5F8C\u65B9\u30B7\u30E7\u30EB\u30C0\u30FC\u76F4\u5F84 +table.column.ForeShoulderLength = \u524D\u65B9\u30B7\u30E7\u30EB\u30C0\u30FC\u9577\u3055 +table.column.ForeShoulderDiameter = \u524D\u65B9\u30B7\u30E7\u30EB\u30C0\u30FC\u76F4\u5F84 +table.column.ForeOuterDiameter = \u524D\u65B9\u5916\u5F84 +table.column.Shape = \u5F62\u72B6 +table.column.Material = \u6750\u6599 +table.column.Finish = \u4ED5\u4E0A\u3052 +table.column.Thickness = \u539A\u3055 +table.column.Filled = \u4E2D\u5B9F +table.column.Mass = \u8CEA\u91CF +table.column.Diameter = \u76F4\u5F84 +table.column.Sides = Sides +table.column.LineCount = \u7DDA\u306E\u672C\u6570 +table.column.LineLength = \u7DDA\u306E\u9577\u3055 +table.column.LineMaterial = \u7DDA\u306E\u6750\u6599 diff --git a/core/resources/l10n/messages_pl.properties b/core/resources/l10n/messages_pl.properties index a25fecd6a..d58ccec52 100644 --- a/core/resources/l10n/messages_pl.properties +++ b/core/resources/l10n/messages_pl.properties @@ -171,14 +171,14 @@ ! Edit Motor configuration dialog - edtmotorconfdlg.but.removemotor = Usu\u0144 silnik - edtmotorconfdlg.but.Selectmotor = Wybierz silnik + MotorConfigurationPanel.btn.removeMotor = Usu\u0144 silnik + MotorConfigurationPanel.btn.selectMotor = Wybierz silnik edtmotorconfdlg.but.Removeconfiguration = Usu\u0144 konfiguracj\u0119 edtmotorconfdlg.but.Newconfiguration = Nowa konfiguracja - edtmotorconfdlg.lbl.Motormounts = Gniazda silnikowe: + MotorConfigurationPanel.lbl.motorMounts = Gniazda silnikowe: edtmotorconfdlg.title.Editmotorconf = Edytuj konfiguracje silnika edtmotorconfdlg.selectcomp = Wybierz cz\u0119\u015Bci, które pe\u0142ni\u0105 funkcj\u0119 gniazd silnikowych: - edtmotorconfdlg.lbl.Motorconfig = Konfiguracje silników: + MotorConfigurationPanel.lbl.motorConfiguration = Konfiguracje silników: edtmotorconfdlg.lbl.Configname = Nazwa konfiguracji: edtmotorconfdlg.lbl.Leavenamedefault = Pozostaw bez nazwy w celu u\u017Cycia nazwy domy\u015Blnej. @@ -1161,7 +1161,7 @@ Bulkhead.Bulkhead = Przegroda !Rocket - Rocket.motorCount.Nomotor = [bez silników] + Rocket.motorCount.Nomotor = bez silników Rocket.compname.Rocket = Rakieta !MotorMount diff --git a/core/resources/l10n/messages_pt.properties b/core/resources/l10n/messages_pt.properties new file mode 100644 index 000000000..d8d8da9de --- /dev/null +++ b/core/resources/l10n/messages_pt.properties @@ -0,0 +1,1576 @@ +# Portuguese base translation file +# Should you need to add new logical keys here is the proposed method +# className.ComponentType.componentName +# Text tokens within braces should not be translated, e.g. +# "The file '{filename}' exists." +# They are pieces that are inserted dynamically. + +# Set to the name of the current translation file (used for debugging purposes) +debug.currentFile = messages.properties +# RocketActions +RocketActions.checkbox.Donotaskmeagain = Não me pergunte novamente +RocketActions.lbl.Youcanchangedefop = Você pode alterar a operação padrão em Preferências. +RocketActions.showConfirmDialog.lbl1 = Excluir as simulações selecionadas? +RocketActions.showConfirmDialog.lbl2 = Esta operação não poderá ser desfeita. +RocketActions.showConfirmDialog.title = Excluir simulações +RocketActions.DelCompAct.Delete = Excluir +RocketActions.DelCompAct.ttip.Delete = Excluir o componente selecionado. +RocketActions.DelSimuAct.Delete = Excluir +RocketActions.DelSimuAct.ttip.Delete = Excluir a simulação selecionada. +RocketActions.DelAct.Delete = Excluir +RocketActions.DelAct.ttip.Delete = Excluir o componente ou a simulação selecionada. +RocketActions.CutAction.Cut = Cortar +RocketActions.CutAction.ttip.Cut = Corte este componente ou simulação para a área de transferência e remover deste projeto +RocketActions.CopyAct.Copy = Copiar +RocketActions.CopyAct.ttip.Copy = Copiar este componente (e subcomponentes) para a área de transferência. +RocketActions.PasteAct.Paste = Colar +RocketActions.PasteAct.ttip.Paste = Cole o componente ou simulação na área da transferência para o projeto. +RocketActions.EditAct.Edit = Editar +RocketActions.EditAct.ttip.Edit = Edite o componente selecionado. +RocketActions.NewStageAct.Newstage = Novo estágio +RocketActions.NewStageAct.ttip.Newstage = Adicionar um novo estágio ao projeto do foguete. +RocketActions.ActBoosterstage = Estágio de reforço +RocketActions.MoveUpAct.Moveup = Mover para cima +RocketActions.MoveUpAct.ttip.Moveup = Mover este componente para cima. +RocketActions.MoveDownAct.Movedown = Mover para baixo +RocketActions.MoveDownAct.ttip.Movedown = Mover este componente para baixo. +# RocketPanel +RocketPanel.FigTypeAct.Sideview = Vista lateral +RocketPanel.FigTypeAct.ttip.Sideview = Vista lateral +RocketPanel.FigTypeAct.Backview = Vista traseira +RocketPanel.FigTypeAct.ttip.Backview = Vista traseira +RocketPanel.FigViewAct.2D = Vista 2D +RocketPanel.FigViewAct.ttip.2D = Vista 2D +RocketPanel.FigViewAct.3D = Vista 3D +RocketPanel.FigViewAct.ttip.3D = Vista 3D +RocketPanel.lbl.Motorcfg = Configuração do motor: +RocketPanel.lbl.infoMessage = Clique para selecionar    Shift + clique para selecionar outro    Duplo-clique para editar    Clique + arraste para mover +# BasicFrame +BasicFrame.tab.Rocketdesign = Projeto do foguete\n +BasicFrame.tab.Flightsim = Simulações de voo +BasicFrame.title.Addnewcomp = Adicionar novo componente +BasicFrame.dlg.lbl1 = Projeto +BasicFrame.dlg.lbl2 = ' ainda não foi salvo. +BasicFrame.dlg.lbl3 = Você deseja salvá-lo? +BasicFrame.dlg.title = O projeto não está salvo +BasicFrame.StageName.Sustainer = Sustentador +BasicFrame.WarningDialog.txt1 = Os seguintes problemas foram encontrados durante a abertura +BasicFrame.WarningDialog.txt2 = Algumas funcionalidades podem não ter sido carregadas corretamente. +BasicFrame.WarningDialog.title = Avisos ao abrir arquivo +# General error messages used in multiple contexts +error.fileExists.title = Arquivo existe +error.fileExists.desc = Arquivo '{nome}' existe. Você deseja substituí-lo? +error.writing.title = Erro ao gravar arquivo +error.writing.desc = Ocorreu um erro ao gravar o arquivo: +# Labels used in buttons of dialog windows +# TODO: Rename these to "btn.xxx" +button.ok = Ok +button.cancel = Cancelar +button.close = Fechar +# Common labels used in buttons of dialog windows +dlg.but.ok = Ok +dlg.but.cancel = Cancelar +dlg.but.close = Fechar +# General file type names +filetypes.pdf = Arquivos PDF (*.pdf) +BasicFrame.SimpleFileFilter1 = Todos os projetos dos foguetes (*.ork; *.rtk) +BasicFrame.SimpleFileFilter2 = Projetos OpenRocket (*.ork) +BasicFrame.SimpleFileFilter3 = Desenhos RockSim (*.rtk) +BasicFrame.SimpleFileFilter4 = Pré-definições OpenRocket (*.orc) +filetypes.images = Arquivos de imagem +# About Dialog +AboutDialog.lbl.version = Versão +# The texts below provide additional credits for the translation maintainer +# - In AboutDialog.lbl.translation replace "English" with the current language. +# - AboutDialog.lbl.translator is the translator / group name (may be empty) +# - AboutDialog.lbl.translatorWebsite is a URL to the translator / group (may be empty) +# - AboutDialog.lbl.translatorIcon is the file name of an icon under pix/translators/ (may be empty) +AboutDialog.lbl.translation = Tradução para o Português por: +AboutDialog.lbl.translator = Roberto (Lobé) Durrer Leite +AboutDialog.lbl.translatorWebsite = http://hackerlab.com.br +AboutDialog.lbl.translatorIcon = +# Print dialog +PrintDialog.title = Imprimir ou exportar +PrintDialog.but.previewAndPrint = Visualização e impressão +PrintDialog.checkbox.showByStage = Mostrar por estágio +PrintDialog.lbl.selectElements = Selecionar elementos para incluir: +printdlg.but.saveaspdf = Salvar como PDF +printdlg.but.preview = Visualizar +printdlg.but.settings = Configurações +PrintDialog.error.preview.title = Não é possível abrir visualização +PrintDialog.error.preview.desc1 = Não é possível abrir visualização de PDF. +PrintDialog.error.preview.desc2 = Por favor, use a opção \"Salvar como PDF\". +# PrintSettingsDialog +PrintSettingsDialog.title = Configurações para impressão +PrintSettingsDialog.lbl.Templatefillcolor = Cor modelo de preenchimento: +PrintSettingsDialog.lbl.Templatebordercolor = Cor da borda modelo: +PrintSettingsDialog.lbl.Papersize = Tamanho do papel: +PrintSettingsDialog.lbl.Paperorientation = Orientação do papel: +PrintSettingsDialog.but.Reset = Restaurar +PrintSettingsDialog.but.Close = Fechar +# Bug Report dialog +bugreport.dlg.title = Relatório de erro +bugreport.dlg.but.Sendbugreport = Enviar relatório de erro +bugreport.dlg.but.Sendbugreport.Ttip = Automaticamente enviar o relatório de erros para os desenvolvedores do OpenRocket. +bugreport.dlg.successmsg1 = Relatório de erro enviado com sucesso. +bugreport.dlg.successmsg2 = Obrigado por ajudar a tornar OpenRocket melhor! +bugreport.dlg.successmsg3 = Relatório de erro enviado +bugreport.dlg.connectedInternet = Se estiver conectado à Internet, você pode simplesmente clicar Enviar relatório de erro. +bugreport.dlg.otherwise = Caso contrário, envie o texto abaixo para o endereço: +bugreport.lbl.Theinformation = A informação acima mencionada pode ser incluída em um relatório de erro público. Certifique-se de que não contém qualquer informação sensível que você não queira que se torne pública. +bugreport.dlg.failedmsg1 = OpenRocket foi incapaz de enviar o relatório de erro: +bugreport.dlg.failedmsg2 = Por favor, enviar o relatório manualmente para +bugreport.dlg.failedmsg3 = Erro ao enviar relatório +bugreport.reportDialog.txt = Você pode relatar um erro no OpenRocket preenchendo e enviando o formulário abaixo.
Você também pode relatar erro e incluir anexos no site do projeto. +bugreport.reportDialog.txt2 = Favor incluir uma breve descrição sobre o que você estava fazendo quando ocorreu a exceção. +bugreport.dlg.provideDescription = Favor fornecer uma descrição do erro primeiro. +bugreport.dlg.provideDescription.title = Falta descrição do erro +# Debug log dialog +debuglogdlg.but.clear = Limpar +debuglogdlg.OpenRocketdebuglog = Relatório de depuração do OpenRocket +debuglogdlg.Displayloglines = Exibir linhas do relatório: +debuglogdlg.Follow = Siga +debuglogdlg.col.Time = Horas +debuglogdlg.col.Level = Nível +debuglogdlg.col.Location = Localização +debuglogdlg.col.Message = Mensagem +debuglogdlg.lbl.Loglinenbr = Entrar o número da linha do relatório: +debuglogdlg.lbl.Time = Hora: +debuglogdlg.lbl.Level = Nível: +debuglogdlg.lbl.Location = Localização: +debuglogdlg.lbl.Logmessage = Relatório de mensagens: +debuglogdlg.lbl.Stacktrace = Rastreamento de pilha: +# MotorChooserDialog +MotorChooserDialog.title = Selecione um motor do foguete +# Edit Motor configuration dialog +MotorConfigurationPanel.btn.removeMotor = Remover o motor +MotorConfigurationPanel.btn.selectMotor = Selecionar motor +edtmotorconfdlg.but.Removeconfiguration = Remover configuração +edtmotorconfdlg.but.Newconfiguration = Nova configuração +MotorConfigurationPanel.lbl.motorMounts = Montagem do motor: +edtmotorconfdlg.title.Editmotorconf = Editar as configurações de motor +edtmotorconfdlg.selectcomp = Selecione os componentes que funcionam como suportes de motor: +MotorConfigurationPanel.lbl.motorConfiguration = Configurações de motor: +edtmotorconfdlg.lbl.Configname = Nome de configuração: +edtmotorconfdlg.lbl.Leavenamedefault = Deixe o nome vazio por padrão. +# Example design dialog +exdesigndlg.but.open = Abrir +exdesigndlg.lbl.Selectexample = Selecione projeto de exemplo para abrir: +exdesigndlg.lbl.Openexampledesign = Abrir exemplo de projeto +exdesigndlg.lbl.Exampledesignsnotfound = Projetos de exemplo não foram encontrados +exdesigndlg.lbl.Examplesnotfound = Exemplos não encontrados +# Material edit panel +matedtpan.but.new = Novo +matedtpan.but.edit = Editar +matedtpan.but.delete = Excluir +matedtpan.but.revertall = Reverter tudo +matedtpan.col.Material = Material +matedtpan.col.Type = Tipo +matedtpan.col.Density = Densidade +matedtpan.col.but.ttip.New = Adicionar um novo material +matedtpan.title.Addcustmaterial = Adicionar um material personalizado +matedtpan.but.ttip.edit = Editar um material existente +matedtpan.title.Editmaterial = Editar material +matedtpan.title2.Editmaterial = As matérias primas não podem ser modificadas. +matedtpan.but.ttip.delete = Excluir um material definido pelo usuário +matedtpan.but.ttip.revertall = Eliminar todos os materiais definidos pelo usuário +matedtpan.title.Deletealluser-defined = Eliminar todos os materiais definidos pelo usuário? +matedtpan.title.Revertall = Reverter tudo? +matedtpan.lbl.edtmaterials = Materiais editados não vão afetar os projetos já existentes dos foguetes. +# MaterialModel +MaterialModel.title.Material = Material +MaterialModel.title.Defcustmat = Definir material personalizado +# Preference dialog +pref.dlg.but.add = Adicionar +pref.dlg.but.reset = Restaurar +pref.dlg.but.checknow = Verificar agora +pref.dlg.but.openlast = Abrir o arquivo do último projeto na inicialização +pref.dlg.but.defaultmetric = Padrão métrico +pref.dlg.but.defaultimperial = Padrão imperial +pref.dlg.title.Preferences = Preferências +pref.dlg.tab.Units = Unidades +pref.dlg.tab.Defaultunits = Unidades padrão +pref.dlg.tab.Materials = Materiais +pref.dlg.tab.Custommaterials = Materiais personalizados +pref.dlg.tab.Options = Opções +pref.dlg.tab.Miscellaneousoptions = Opções diversas +pref.dlg.lbl.Positiontoinsert = Posição para inserir novos componentes do corpo: +pref.dlg.lbl.Confirmdeletion = Confirmar a exclusão de simulações: +pref.dlg.lbl.User-definedthrust = Curvas axiais definidas pelo usuário: +pref.dlg.lbl.Windspeed = Velocidade do vento +pref.dlg.Allthrustcurvefiles = Todos os arquivos de curva de empuxo (*.eng; *.rse; *.zip; directories) +pref.dlg.RASPfiles = Arquivo de motor RASP (*.eng) +pref.dlg.RockSimfiles = Arquivos de motores do RockSim (*.rse) +pref.dlg.ZIParchives = Arquivos ZIP (*.zip) +pref.dlg.checkbox.Checkupdates = Verificar atualizações de software na inicialização +pref.dlg.ttip.Checkupdatesnow = Verificar agora por atualizações de software +pref.dlg.lbl.Selectprefunits = Selecione suas unidades preferidas: +pref.dlg.lbl.Rocketdimensions = Dimensões do foguete: +pref.dlg.lbl.Linedensity = Densidade da linha: +pref.dlg.lbl.Motordimensions = Dimensões do motor: +pref.dlg.lbl.Surfacedensity = Densidade de superfície: +pref.dlg.lbl.Distance = Distância: +pref.dlg.lbl.Bulkdensity = Densidade da massa: +pref.dlg.lbl.Velocity = Velocidade: +pref.dlg.lbl.Surfaceroughness = Rugosidade da superfície: +pref.dlg.lbl.Acceleration = Aceleração: +pref.dlg.lbl.Area = Área: +pref.dlg.lbl.Mass = Massa: +pref.dlg.lbl.Angle = Ângulo: +pref.dlg.lbl.Force = Força: +pref.dlg.lbl.Rollrate = Taxa de rotação: +pref.dlg.lbl.Totalimpulse = Impulso total: +pref.dlg.lbl.Temperature = Temperatura: +pref.dlg.lbl.Momentofinertia = Momento de inércia: +pref.dlg.lbl.Pressure = Pressão +pref.dlg.lbl.Stability = Estabilidade: +pref.dlg.lbl.FlightTime = Tempo de voo: +pref.dlg.lbl.effect1 = Os efeitos terá lugar na próxima vez que abrir uma janela. +pref.dlg.lbl.Checkingupdates = Verificar atualizações... +pref.dlg.lbl.msg1 = Ocorreu um erro durante a comunicação com o servidor. +pref.dlg.lbl.msg2 = Não é possível recuperar informações de atualização. +pref.dlg.lbl.msg3 = Você está executando a versão mais recente do OpenRocket. +pref.dlg.lbl.msg4 = Não há atualizações disponíveis +pref.dlg.PrefChoiseSelector1 = Sempre perguntar +pref.dlg.PrefChoiseSelector2 = Inserir no meio +pref.dlg.PrefChoiseSelector3 = Adicionar para terminar +pref.dlg.PrefBooleanSelector1 = Excluir +pref.dlg.PrefBooleanSelector2 = Confirme +pref.dlg.Add = Adicionar +pref.dlg.DescriptionArea.Adddirectories = Adicionar diretórios, arquivos de motores RASP (*.eng), arquivos de motor RockSim (*.rse) ou arquivos ZIP, separados por um ponto e vírgula (;) para carregar curvas de empuxo externas. As alterações terão efeito na próxima vez que você iniciar o OpenRocket. +PreferencesDialog.lbl.language = Idioma da interface: +PreferencesDialog.languages.default = Padrão do sistema +PreferencesDialog.lbl.languageEffect = A linguagem vai mudar na próxima vez que você iniciar o OpenRocket. +# Simulation edit dialog +simedtdlg.but.runsimulation = Executar a simulação +simedtdlg.but.resettodefault = Restaurar padrão +simedtdlg.but.add = Adicionar +simedtdlg.but.remove = Remover +simedtdlg.title.Editsim = Editar simulação +simedtdlg.lbl.Simname = Nome da simulação: +simedtdlg.tab.Launchcond = Condições do lançamento +simedtdlg.tab.Simopt = Opções de simulação +simedtdlg.tab.Plotdata = Plotar dados +simedtdlg.tab.CustomExpressions = Expressões personalizadas +simedtdlg.tab.Exportdata = Exportar dados +simedtdlg.lbl.Motorcfg = Configuração do motor: +simedtdlg.lbl.ttip.Motorcfg = Selecione a configuração do motor a ser usado. +simedtdlg.combo.ttip.motorconf = Selecione a configuração do motor a ser usado. +simedtdlg.lbl.Wind = Vento +simedtdlg.lbl.Averwindspeed = Velocidade média do vento: +simedtdlg.lbl.ttip.Averwindspeed = Velocidade do vento em relação ao solo. +simedtdlg.lbl.Stddeviation = Desvio padrão: +simedtdlg.lbl.ttip.Stddeviation = Desvio padrão da. velocidade do vento.
A velocidade do vento está dentro de duas vezes o desvio padrão da média para 95% do tempo. +simedtdlg.lbl.Turbulenceintensity = Intensidade de turbulência: +simedtdlg.lbl.ttip.Turbulenceintensity1 = A intensidade de turbulência é o desvio padrão dividido pela velocidade média do vento.
+simedtdlg.lbl.ttip.Turbulenceintensity2 = Os valores típicos variam entre +simedtdlg.lbl.ttip.Turbulenceintensity3 = a +simedtdlg.border.Atmoscond = Condições atmosféricas +simedtdlg.checkbox.InterStdAtmosphere = Use Atmosfera Padrão Internacional +simedtdlg.checkbox.ttip.InterStdAtmosphere1 = Selecione para usar o modelo de Atmosfera Padrão Internacional.
Este modelo tem uma temperatura de +simedtdlg.checkbox.ttip.InterStdAtmosphere2 = e uma pressão de +simedtdlg.checkbox.ttip.InterStdAtmosphere3 = ao nível do mar. +simedtdlg.lbl.Temperature = Temperatura: +simedtdlg.lbl.ttip.Temperature = Temperatura no local de lançamento. +simedtdlg.lbl.Pressure = Pressão: +simedtdlg.lbl.ttip.Pressure = Pressão atmosférica no local de lançamento. +simedtdlg.lbl.Launchsite = Local do lançamento +simedtdlg.lbl.Latitude = Latitude: +simedtdlg.lbl.ttip.Latitude = A latitude local de lançamento afeta a atração gravitacional da Terra.
Valores positivos estão no hemisfério Norte, valores negativos estão no hemisfério Sul. +simedtdlg.lbl.Longitude = Longitude: +simedtdlg.lbl.ttip.Longitude = Necessário para previsão do tempo e modelos de elevação. +simedtdlg.lbl.Altitude = Altitude: +simedtdlg.lbl.ttip.Altitude = Altitude lançamento acima do nível do mar.
Isso afeta a posição do foguete no modelo atmosférico. +simedtdlg.border.Launchrod = Haste de lançamento +simedtdlg.lbl.Length = Comprimento: +simedtdlg.lbl.ttip.Length = Comprimento da haste de lançamento. +simedtdlg.lbl.Angle = Ângulo: +simedtdlg.lbl.ttip.Angle = Ângulo da haste de lançamento com a vertical. +simedtdlg.lbl.Direction = Direção: +simedtdlg.lbl.ttip.Direction1 = Direção da haste de lançamento em relação ao vento.
+simedtdlg.lbl.ttip.Direction2 = direção do vento. +simedtdlg.lbl.ttip.Direction3 = a favor do vento. +simedtdlg.border.Simopt = Opções do simulador +simedtdlg.lbl.Calcmethod = Método de cálculo: +simedtdlg.lbl.ttip.Calcmethod = O método Barrowman estendido calcula as forças aerodinâmicas de acordo com
as equações Barrowman para acomodar mais componentes. +simedtdlg.lbl.ExtBarrowman = Barrowman Estendido +simedtdlg.lbl.Simmethod = Método de simulação: +simedtdlg.lbl.ttip.Simmethod1 = O simulador de seis graus de liberdade permite a liberdade total do foguete durante o voo.
+simedtdlg.lbl.ttip.Simmethod2 = A integração é realizada utilizando a 4a ordem de Runge-Kutta 4 de integração numérica. +simedtdlg.lbl.GeodeticMethod = Cálculos geodésicos: +simedtdlg.lbl.ttip.GeodeticMethodTip = Referem-se ao cálculo das coordenadas na Terra. Isto também permite cálculos do efeito de Coriolis. +simedtdlg.lbl.Timestep = Passo de tempo: +simedtdlg.lbl.ttip.Timestep1 = Tempo entre as etapas de simulação.
Menor etapa de tempo propicia uma simulação mais precisa, porém mais lenta.
+simedtdlg.lbl.ttip.Timestep2 = A 4 a ordem do método de simulação é bastante preciso com um passo de tempo de +simedtdlg.but.ttip.resettodefault = Reajuste o passo de tempo para seu valor padrão ( +simedtdlg.border.Simlist = Observação da simulação +simedtdlg.txt.longA1 = Observação da simulação é um recurso avançado que permite que o usuário escreva código para observar e interagir com a simulação. +simedtdlg.txt.longA2 = Para mais detalhes sobre como escrever observadores de simulação, consulte a documentação técnica do OpenRocket. +simedtdlg.lbl.Curlist = Observações atuais: +simedtdlg.lbl.Addsimlist = Adicionar observação de simulação +simedtdlg.lbl.Noflightdata = Não há dados de voo disponível +simedtdlg.lbl.runsimfirst = Por favor, execute primeiro a simulação. +simedtdlg.chart.Simflight = voo simulado +simedtdlg.dlg.Simres = Resultados da simulação +simedtdlg.IntensityDesc.None = Nenhum +simedtdlg.IntensityDesc.Verylow = Muito baixa +simedtdlg.IntensityDesc.Low = Baixo +simedtdlg.IntensityDesc.Medium = Média +simedtdlg.IntensityDesc.High = Alta +simedtdlg.IntensityDesc.Veryhigh = Muito alta +simedtdlg.IntensityDesc.Extreme = Extrema +GeodeticComputationStrategy.flat.name = Terra plana +GeodeticComputationStrategy.flat.desc = Realizar cálculos com a aproximação de Terra plana. Suficiente para voos de baixa altitude. +GeodeticComputationStrategy.spherical.name = Aproximação esférica +GeodeticComputationStrategy.spherical.desc = Realizar cálculos geodésicos assumindo que a Terra seja esférica.
É suficientemente precisa para quase todos os fins. +GeodeticComputationStrategy.wgs84.name = Elipsóide WGS84 +GeodeticComputationStrategy.wgs84.desc = Realizar cálculos geodésicos no elipsóide de referência WGS84 utilizando o método de Vincenty.
Lento e desnecessário na maioria dos casos. +# Simulation Panel +simpanel.but.newsimulation = Nova simulação +simpanel.but.editsimulation = Editar simulação +simpanel.but.runsimulations = Executar simulações +simpanel.but.deletesimulations = Excluir simulações +simpanel.but.plotexport = Plotar / exportar +simpanel.but.ttip.newsimulation = Adicionar uma nova simulação +simpanel.but.ttip.editsim = Editar a simulação selecionada +simpanel.but.ttip.runsimu = Re-executar as simulações selecionados +simpanel.but.ttip.deletesim = Excluir as simulações selecionadas +simpanel.checkbox.donotask = Não me pergunte novamente +simpanel.lbl.defpref = Você pode alterar a operação padrão em Preferências. +simpanel.dlg.lbl.DeleteSim1 = Excluir as simulações selecionadas? +simpanel.dlg.lbl.DeleteSim2 = Esta operação não poderá ser desfeita. +simpanel.dlg.lbl.DeleteSim3 = Excluir simulações +simpanel.col.Name = Nome +simpanel.col.Motors = Motores +simpanel.col.Velocityoffrod = Velocidade fora da haste +simpanel.col.Velocityatdeploy = Velocidade no lançamento +simpanel.col.Apogee = Apogeu +simpanel.col.Maxvelocity = Velocidade máxima +simpanel.col.Maxacceleration = Aceleração máxima +simpanel.col.Timetoapogee = Tempo para o apogeu +simpanel.col.Flighttime = Tempo de voo +simpanel.col.Groundhitvelocity = Velocidade ao atingir o solo +simpanel.ttip.uptodate = Até à data +simpanel.ttip.loaded = Dados carregado de um arquivo +simpanel.ttip.outdated = Os dados estão desatualizados
CliqueExecutar simulações para simular. +simpanel.ttip.external = Dados importados +simpanel.ttip.notSimulated = Não simulado ainda
CliqueExecutar simulações para simular. +simpanel.ttip.noData = Não há dados disponíveis para simulação. +simpanel.ttip.noWarnings = Nenhuma advertência. +simpanel.ttip.warnings = Avisos: +# SimulationRunDialog +SimuRunDlg.title.RunSim = Simulações de execução... +SimuRunDlg.lbl.Running = Executando... +SimuRunDlg.lbl.Simutime = Tempo de simulação: +SimuRunDlg.lbl.Altitude = Altitude: +SimuRunDlg.lbl.Velocity = Velocidade: +SimuRunDlg.msg.Unabletosim = Incapaz de simular: +SimuRunDlg.msg.errorOccurred = Ocorreu um erro durante a simulação: +SimuRunDlg.msg.AnException1 = Uma exceção ocorreu durante a simulação: +SimuRunDlg.msg.AnException2 = Por favor, reporte isso como um erro junto com os detalhes abaixo. +SimuRunDlg.msg.AssertionError1 = Ocorreu um erro de cálculo durante a simulação. +SimuRunDlg.msg.AssertionError2 = Por favor, reporte isso como um erro junto com os detalhes abaixo. +SimuRunDlg.msg.unknownerror1 = Foi encontrado um erro desconhecido durante a simulação. +SimuRunDlg.msg.unknownerror2 = O programa pode estar instável, você deve guardar todos os seus projetos e reiniciar OpenRocket agora! +RK4SimulationStepper.error.valuesTooLarge = Valores de simulação excedeu os limites. Tente selecionar um passo de tempo mais curto. +SimulationModifierTree.OptimizationParameters = Parâmetros de otimização +# SimulationExportPanel +SimExpPan.desc = Arquivos Separados por Vírgulas (*.csv) +SimExpPan.border.Vartoexport = Variáveis para exportar +SimExpPan.but.Selectall = Selecionar todos +SimExpPan.but.Selectnone = Limpar seleção +SimExpPan.border.Fieldsep = Separador de campo +SimExpPan.lbl.Fieldsepstr = Caracteres separadores de campo: +SimExpPan.lbl.longA1 = Cadeia de caracteres utilizada para separar os campos no arquivo exportado.
+SimExpPan.lbl.longA2 = Use \",\" para Arquivos Separados por Vírgulas (*.csv). +SimExpPan.checkbox.Includesimudesc = Incluir a descrição de simulação +SimExpPan.checkbox.ttip.Includesimudesc = Incluir um comentário no começo do arquivo descrevendo a simulação. +SimExpPan.border.Comments = Comentários +SimExpPan.checkbox.Includefielddesc = Incluir descrições de campo +SimExpPan.checkbox.ttip.Includefielddesc = Incluir uma linha de comentário com as descrições das variáveis exportadas. +SimExpPan.checkbox.Incflightevents = Incluir eventos de voo +SimExpPan.checkbox.ttip.Incflightevents = Incluir uma linha de comentário para cada evento de voo. +SimExpPan.lbl.Commentchar = Caracter de comentário: +SimExpPan.lbl.ttip.Commentchar = O(s) caracter(es) que marca(m) uma linha de comentário. +SimExpPan.but.Exporttofile = Exportar para arquivo... +SimExpPan.Fileexists.desc1 = Arquivo \" +SimExpPan.Fileexists.desc2 = \" existe. Sobreescrever? +SimExpPan.Fileexists.title = Arquivo existe +SimExpPan.ExportingVar.desc1 = Exportando uma variável de +SimExpPan.ExportingVar.desc2 = Exportando +SimExpPan.ExportingVar.desc3 = variáveis fora de +SimExpPan.Col.Variable = Variável +SimExpPan.Col.Unit = Unidade +CsvOptionPanel.separator.space = ESPAÇO +CsvOptionPanel.separator.tab = TAB +# Custom expression general stuff +customExpression.Name = Nome +customExpression.Symbol = Símbolo +customExpression.Expression = Expressão +customExpression.Units = Unidades +customExpression.Operator = Operador +customExpression.Description = Descrição +# Custom expression panel +customExpressionPanel.but.NewExpression = Nova expressão +customExpressionPanel.but.ttip.NewExpression = Adicionar uma nova expressão personalizada +customExpressionPanel.but.Import = Importar +customExpressionPanel.but.ttip.Import = Importar expressões personalizadas de outro arquivo .ork +customExpressionPanel.lbl.UpdateNote = Você deve executar a simulação antes dos dados estarem disponíveis para plotagem. +customExpressionPanel.lbl.CalcNote = Expressões serão calculadas na ordem mostrada. +customExpressionPanel.lbl.CustomExpressions = Expressões personalizadas +customExpression.Units.but.ttip.Remove = Remover esta expressão +customExpression.Units.but.ttip.Edit = Editar esta expressão +customExpression.Units.but.ttip.MoveUp = Mova acima a expressão na ordem de cálculo +customExpression.Units.but.ttip.MoveDown = Mova para baixo expressão na ordem de cálculo +# Custom expression builder window +ExpressionBuilderDialog.title = Construtor de Expressões +ExpressionBuilderDialog.InsertVariable = Inserir Variável +ExpressionBuilderDialog.InsertOperator = Inserir Operador +ExpressionBuilderDialog.led.ttip.Name = Nome não deve ter sido utilizados +ExpressionBuilderDialog.led.ttip.Symbol = Símbolo não deve ter sido utilizados +ExpressionBuilderDialog.led.ttip.Expression = Expressão deve usar somente símbolos e operadores conhecidos +ExpressionBuilderDialog.CopyToOtherSimulations = Copiar para outras simulações +ExpressionBuilderDialog.CopyToOtherSimulations.ttip = Faça uma cópia desta expressão em outras simulações deste documento.
Não irá substituir ou modificar quaisquer expressões existentes em outras simulações. +# Custom expression variable selector +CustomVariableSelector.title = Seletor de Variável +# Custom operator selector +CustomOperatorSelector.title = Seletor de Operador +# Operators +Operator.plus = Adição +Operator.minus = Subtração +Operator.star = Multiplicação +Operator.div = Divisão +Operator.mod = Módulo +Operator.pow = Potenciação +Operator.abs = Valor absoluto +Operator.ceil = Arredonda para cima (valor inteiro seguinte) +Operator.floor = Arredonda para baixo (valor inteiro anterior) +Operator.sqrt = Raiz quadrada +Operator.cbrt = Raiz cúbica +Operator.exp = Número de Euler elevado ao valor (e^x) +Operator.ln = Logaritmo natural +Operator.sin = Seno +Operator.cos = Cosseno +Operator.tan = Tangente +Operator.asin = Arco seno +Operator.acos = Arco cosseno +Operator.atan = Tangente +Operator.hsin = Seno hiperbólico +Operator.hcos = Cosseno hiperbólico +Operator.htan = Tangente hiperbólica +Operator.log10 = Logaritmo base 10 +Operator.round = Arredondar para valor inteiro mais próximo +Operator.random = Número aleatório entre zero e valor dado +Operator.expm1 = O mesmo que exp (x)-1, porém mais preciso para pequenos valores de x +Operator.mean = Média aritmética de um determinado intervalo +Operator.min = Valor mínimo em um determinado intervalo +Operator.max = Valor máximo em um determinado intervalo +Operator.var = Variância de um determinado intervalo +Operator.stdev = Desvio padrão de um dado intervalo +Operator.rms = Valor da raiz média quadrática de um determinado intervalo +Operator.lclip = Fixa um valor (1o.parâmetro) para ser maior ou igual do que um determinado valor (2o.parâmetro) +Operator.uclip = Fixa um valor (1o.parâmetro) para ser menor ou igual do que um determinado valor (2o.parâmetro) +Operator.binf = Dá a fração de valores em um determinado intervalo (1o.parâmetro) dentro de um +Operator.trapz = Integra o intervalo dado usando a integração trapezoidal +Operator.tnear = Encontrar o intervalo de tempo correspondente ao ponto de uma faixa (1o. parâmetro) mais próxima a um dado valor (2o. parâmetro) +# MotorPlot +MotorPlot.title.Motorplot = Plotagem do Motor +MotorPlot.but.Select = Selecionar +MotorPlot.Chart.Motorthrustcurve = Curva de empuxo do motor +MotorPlot.Chart.Time = Hora /s +MotorPlot.Chart.Thrust = Empuxo / N +MotorPlot.txt.Designation = Designação: +MotorPlot.txt.Manufacturer = Fabricante: +MotorPlot.txt.Type = Tipo: +MotorPlot.txt.Delays = Atrasos: +MotorPlot.txt.Comment = Comentários:\n +# Simulation plot panel +simplotpanel.lbl.Presetplotconf = Pré-configurações da plotagem: +simplotpanel.lbl.Xaxistype = Tipo de eixo X: +simplotpanel.lbl.Unit = Unidade +simplotpanel.lbl.Yaxistypes = Tipos de eixo Y: +simplotpanel.lbl.Flightevents = Eventos de voo: +simplotpanel.but.All = Todos +simplotpanel.but.None = Nenhum +simplotpanel.but.NewYaxisplottype = Novo tipo de plotagem para o eixo Y +simplotpanel.but.Plotflight = Plotagem do voo +simplotpanel.lbl.Axis = Eixos: +simplotpanel.but.ttip.Removethisplot = Remover esta plotagem +simplotpanel.Desc = Os dados serão plotados em ordem de tempo, mesmo que o tipo de eixo X não seja tempo. +simplotpanel.OptionPane.lbl1 = É permitido um máximo de 15 plotagens. +simplotpanel.OptionPane.lbl2 = Não é possível adicionar plotagem +simplotpanel.AUTO_NAME = Auto +simplotpanel.LEFT_NAME = Esquerda +simplotpanel.RIGHT_NAME = Direita. +simplotpanel.CUSTOM = Personalizado +SimulationPlotPanel.error.noPlotSelected = Por favor, adicione uma ou mais variáveis para plotar no eixo Y. +SimulationPlotPanel.error.noPlotSelected.title = Nada a plotar +# Component add buttons +compaddbuttons.Bodycompandfinsets = Componentes do corpo e conjuntos de aletas +compaddbuttons.Nosecone = Ogiva +compaddbuttons.Bodytube = Tubo do corpo +compaddbuttons.Transition = Transição +compaddbuttons.Trapezoidal = Trapezoidal +compaddbuttons.Elliptical = Elíptico +compaddbuttons.Freeform = Forma livre +compaddbuttons.Launchlug = Guia de lançamento +compaddbuttons.Innercomponent = Componente interno +compaddbuttons.Innertube = Tubo interno +compaddbuttons.Coupler = Acoplador +compaddbuttons.Centeringring = Anel centralizador +compaddbuttons.Bulkhead = Anteparo +compaddbuttons.Engineblock = Bloco do motor +compaddbuttons.Massobjects = Objetos de massa +compaddbuttons.Parachute = Pára-quedas +compaddbuttons.Streamer = Fita +compaddbuttons.Shockcord = Cabo de choque +compaddbuttons.Masscomponent = Componente de massa +compaddbuttons.Donotaskmeagain = Não me pergunte novamente +compaddbuttons.Selectcomppos = Selecione a posição do componente +compaddbuttons.lbl.Youcanchange = Você pode alterar a operação padrão em Preferências. +compaddbuttons.lbl.insertcomp = Inserir o componente depois do componente de corrente ou como o último componente? +compaddbuttons.askPosition.Inserthere = Inserir aqui +compaddbuttons.askPosition.Addtotheend = Adicionar à extremidade +compaddbuttons.askPosition.Cancel = Cancelar +# Component Analysis Dialog +componentanalysisdlg.componentanalysis = Análise dos componentes +componentanalysisdlg.lbl.winddir = Direção do vento: +componentanalysisdlg.TitledBorder.warnings = Avisos: +componentanalysisdlg.ToggleBut.worst = Pior +componentanalysisdlg.lbl.angleofattack = Ângulo de ataque: +componentanalysisdlg.lbl.machnumber = Número de Mach: +componentanalysisdlg.lbl.rollrate = Taxa de rotação: +componentanalysisdlg.lbl.activestages = Estágios ativos: +componentanalysisdlg.lbl.motorconf = Configuração do motor: +componentanalysisdlg.TabStability.Col = Componente +componentanalysisdlg.TabStability.Col.CG = CG +componentanalysisdlg.TabStability.Col.Mass = Massa. +componentanalysisdlg.TabStability.Col.CP = CP +componentanalysisdlg.TabStability = Estabilidade +componentanalysisdlg.TabStability.ttip = Informações sobre a estabilidade +componentanalysisdlg.dragTableModel.Col.Component = Componente +componentanalysisdlg.dragTableModel.Col.Pressure = Pressão CD +componentanalysisdlg.dragTableModel.Col.Base = Base CD +componentanalysisdlg.dragTableModel.Col.friction = Atrito CD +componentanalysisdlg.dragTableModel.Col.total = Total CD +componentanalysisdlg.dragTabchar = Arraste as características +componentanalysisdlg.dragTabchar.ttip = Arraste as características +componentanalysisdlg.rollTableModel.Col.component = Componente +componentanalysisdlg.rollTableModel.Col.rollforc = Coeficiente forçado de rotação +componentanalysisdlg.rollTableModel.Col.rolldamp = Coeficiente de amortecimento de rotação +componentanalysisdlg.rollTableModel.Col.total = Total Cl +componentanalysisdlg.rollTableModel = Rotação dinâmica +componentanalysisdlg.rollTableModel.ttip = Rotação dinâmica +componentanalysisdlg.println.closingmethod = Fechando método chamado: +componentanalysisdlg.println.settingnam = DEFININDO VALORES DE NAN +componentanalysisdlg.lbl.reflenght = Comprimento de referência: +componentanalysisdlg.lbl.refarea = Área de referência: +# componentanalysisdlg.But.close =Close +componentanalysisdlg.TabStability.Col.Component = Componente +componentanalysisdlg.TOTAL = Total +componentanalysisdlg.noWarnings = Nenhuma advertência. +# Custom Material dialog +custmatdlg.title.Custommaterial = Material personalizado +custmatdlg.lbl.Materialname = Nome do material: +custmatdlg.lbl.Materialtype = Tipo de material +custmatdlg.lbl.Materialdensity = Densidade do material: +custmatdlg.checkbox.Addmaterial = Adicionar material à base de dados +# Ring Component Config +ringcompcfg.OuterRadius = Raio externo +ringcompcfg.Automatic = Automático +ringcompcfg.InnerRadius = Raio interno +ringcompcfg.Thickness = Espessura +ringcompcfg.Length = Comprimento +ringcompcfg.Positionrelativeto = Posição em relação a: +ringcompcfg.plus = mais +ringcompcfg.PositionValue = Valor da posição +ringcompcfg.Radialdistance = Distância radial: +ringcompcfg.Distancefrom = Distância a partir da linha de centro do foguete +ringcompcfg.Radialdirection = Direção radial: +ringcompcfg.radialdirectionfrom = Direção radial a partir da linha de centro do foguete +ringcompcfg.but.Reset = Restaurar +ringcompcfg.but.Resetcomponant = Redefinir o componente para a linha de centro do foguete +ringcompcfg.EngineBlock.desc = Um bloco do motor pára o motor de se mover para a frente no tubo de montagem do motor.

Para adicionar um motor, criar um tubo de corpo or tubo interno e marcá-lo como uma montagem do motor no Motor tab. +ringcompcfg.note.desc = Nota: Um tubo interno não irá afetar a aerodinâmica do foguete, mesmo se ele estiver localizado do lado de fora do tubo do corpo. +# Body Tube Config +BodyTubecfg.lbl.Bodytubelength = Comprimento do tubo do corpo: +BodyTubecfg.lbl.Outerdiameter = Diâmetro externo +BodyTubecfg.lbl.Innerdiameter = Diâmetro interno: +BodyTubecfg.lbl.Wallthickness = Espessura de parede: +BodyTubecfg.tab.General = Geral +BodyTubecfg.tab.Generalproperties = Propriedades gerais +BodyTubecfg.tab.Motor = Motor +BodyTubecfg.tab.Motormountconf = Configuração da montagem do motor +BodyTubecfg.checkbox.Automatic = Automático +BodyTubecfg.checkbox.Filled = Atribuído +# FinSetConfig +FinSetConfig.tab.Fintabs = Guias das aletas +FinSetConfig.tab.Through-the-wall = Guias de aletas através do corpo +FinSetConfig.but.Converttofreeform = Converter para forma livre +FinSetConfig.but.Converttofreeform.ttip = Converter este conjunto de aletas em um conjunto de aletas de forma livre +FinSetConfig.Convertfinset = Converter conjunto de aletas +FinSetConfig.but.Splitfins = Aletas divididas: +FinSetConfig.but.Splitfins.ttip = Dividir o conjunto de aletas em aletas separadas +FinSetConfig.but.AutoCalc = Calcular automaticamente +FinSetConfig.lbl.Through-the-wall = Guias de aletas através do corpo: +FinSetConfig.lbl.Tablength = Comprimento da guia: +FinSetConfig.ttip.Tablength = Comprimento da guia da aleta. +FinSetConfig.lbl.Tabheight = Altura da guia: +FinSetConfig.ttip.Tabheight = Alltura do spanwise da guia da aleta. +FinSetConfig.lbl.Tabposition = Posição de guia: +FinSetConfig.ttip.Tabposition = Posição da guia da aleta. +FinSetConfig.lbl.relativeto = em relação ao +# FinMarkingGuide +FinMarkingGuide.lbl.Front = Frente +# MotorDatabaseLoadingDialog +MotorDbLoadDlg.title = Carregando motores +MotorDbLoadDlg.Loadingmotors = Carregando motores... +# RocketConfig +RocketCfg.lbl.Designname = Nome do projeto: +RocketCfg.lbl.Designer = Projetista: +RocketCfg.lbl.Comments = Comentários: +RocketCfg.lbl.Revisionhistory = Histórico de Revisão: +RocketCfg.lbl.Material = Material: +# ShockCordConfig +# ShockCordConfig +ShockCordCfg.lbl.Shockcordlength = Comprimento do cabo de choque: +# RocketComponentConfig +RocketCompCfg.lbl.Componentname = Nome do componente: +RocketCompCfg.ttip.Thecomponentname = Nome do componente. +RocketCompCfg.tab.Override = Modificar +RocketCompCfg.tab.MassandCGoverride = Opções de modificação de massa e CG +RocketCompCfg.tab.Figure = Figura +RocketCompCfg.tab.Figstyleopt = Opções do estilo de figura +RocketCompCfg.tab.Comment = Comentário +RocketCompCfg.tab.Specifyacomment = Especifique um comentário para o componente +RocketCompCfg.lbl.Mass = Massa: +RocketCompCfg.lbl.Componentmass = Massa do componente: +RocketCompCfg.lbl.overriddento = (substituído para +RocketCompCfg.lbl.overriddenby = (substituído pela +RocketCompCfg.lbl.Componentmaterial = Material componente: +RocketCompCfg.lbl.Componentfinish = Acabamento de componentes: +RocketCompCfg.lbl.ttip.componentmaterialaffects = O material do componente afeta o peso do componente. +RocketCompCfg.combo.ttip.componentmaterialaffects = O material do componente afeta o peso do componente. +RocketCompCfg.lbl.longA1 = O acabamento do componente afeta o arrasto aerodinâmico do componente.
+RocketCompCfg.lbl.longA2 = O valor indicado é a altura média da rugosidade da superfície. +RocketCompCfg.but.Setforall = Definir para todos +RocketCompCfg.but.ttip.Setforall = Definir este acabamento para todos os componentes do foguete. +RocketCompCfg.lbl.Overridemassorcenter = Modificar a massa ou o centro de gravidade do +RocketCompCfg.checkbox.Overridemass = Modificar massa: +RocketCompCfg.checkbox.Overridecenterofgrav = Modificar o centro de gravidade: +RocketCompCfg.checkbox.OverridemassandCG = Modificar a massa e o CG de todos os subcomponentes +RocketCompCfg.lbl.longB1 = A massa modificada não inclui motores.
+RocketCompCfg.lbl.longB2 = O centro de gravidade é medido a partir da extremidade dianteira do +RocketCompCfg.lbl.Commentsonthe = Comentários sobre o +RocketCompCfg.lbl.Figurestyle = Estilo da figura: +RocketCompCfg.lbl.Componentcolor = Cor do componente: +RocketCompCfg.lbl.Choosecolor = Escolha a cor +RocketCompCfg.checkbox.Usedefaultcolor = Use a cor padrão +RocketCompCfg.lbl.Complinestyle = Estilo de linha de componentes: +RocketCompCfg.but.Saveasdefstyle = Salvar como estilo padrão +RocketCompCfg.lbl.Diameter = Diâmetro: +RocketCompCfg.lbl.Length = Comprimento: +RocketCompCfg.lbl.Thickness = Espessura: +RocketCompCfg.checkbox.Endcapped = Fim tampado +RocketCompCfg.ttip.Endcapped = Quando a extremidade do ressalto é limitada. +RocketCompCfg.title.Noseconeshoulder = Ressalto da ogiva +RocketCompCfg.title.Aftshoulder = Ressalto traseiro +RocketCompCfg.border.Foreshoulder = Ressalto dianteiro +# RocketCompCfg.lbl.Length = Length: +# BulkheadConfig +BulkheadCfg.tab.Diameter = Diâmetro: +BulkheadCfg.tab.Thickness = Espessura: +BulkheadCfg.tab.General = Geral +BulkheadCfg.tab.Generalproperties = Propriedades gerais +# CenteringRingConfig +CenteringRingCfg.tab.Outerdiam = Diâmetro externo: +CenteringRingCfg.tab.Innerdiam = Diâmetro interno: +CenteringRingCfg.tab.Thickness = Espessura: +CenteringRingCfg.tab.General = Geral +CenteringRingCfg.tab.Generalproperties = Propriedades gerais +# ComponentConfigDialog +ComponentCfgDlg.configuration = configuração +ComponentCfgDlg.configuration1 = +ComponentCfgDlg.Modify = Modificar +# StageConfig +StageConfig.tab.Separation = Separação +StageConfig.tab.Separation.ttip = Opções de separação de estágio +StageConfig.separation.lbl.title = Selecione quando este estágio separa: +StageConfig.separation.lbl.plus = mais +StageConfig.separation.lbl.seconds = segundos +# EllipticalFinSetConfig +EllipticalFinSetCfg.Nbroffins = Número de aletas: +EllipticalFinSetCfg.Rotation = Rotação: +EllipticalFinSetCfg.Fincant = inclinação da aleta: +EllipticalFinSetCfg.Rootchord = Corda da raiz: +EllipticalFinSetCfg.Height = Altura: +EllipticalFinSetCfg.Positionrelativeto = Posição em relação a: +EllipticalFinSetCfg.plus = mais +EllipticalFinSetCfg.FincrossSection = Secção transversal da aleta: +EllipticalFinSetCfg.Thickness = Espessura: +EllipticalFinSetCfg.General = Geral +EllipticalFinSetCfg.Generalproperties = Propriedades gerais +EllipticalFinSetCfg.ttip.Fincant = O ângulo que as aletas estão oblíquas em relação ao corpo do foguete. +# FreeformFinSetConfig +FreeformFinSetCfg.tab.General = Geral +FreeformFinSetCfg.tab.ttip.General = Propriedades gerais +FreeformFinSetCfg.tab.Shape = Forma +FreeformFinSetCfg.tab.ttip.Finshape = Forma das aletas +FreeformFinSetCfg.lbl.Numberoffins = Número de aletas: +FreeformFinSetCfg.lbl.Finrotation = Rotação da aleta: +FreeformFinSetCfg.lbl.Fincant = Inclinação da aleta: +FreeformFinSetCfg.lbl.ttip.Fincant = Ângulo que as aletas são oblíquas em relação ao corpo do foguete. +FreeformFinSetCfg.lbl.Posrelativeto = Posição em relação a: +FreeformFinSetCfg.lbl.plus = mais +FreeformFinSetCfg.lbl.FincrossSection = Seção transversal da aleta: +FreeformFinSetCfg.lbl.Thickness = Seção transversal da aleta: +# doubleClick1 + 2 form the message "Double-click to edit", split approximately at the middle +FreeformFinSetConfig.lbl.doubleClick1 = Duplo clique +FreeformFinSetConfig.lbl.doubleClick2 = editar +FreeformFinSetConfig.lbl.clickDrag = Clique+arraste: Adicionar e mover pontos +FreeformFinSetConfig.lbl.ctrlClick = Ctrl+clique em: Remover ponto +FreeformFinSetConfig.lbl.scaleFin = Escala da aleta +# InnerTubeConfig +InnerTubeCfg.tab.Motor = Motor +InnerTubeCfg.tab.ttip.Motor = Motor montagem configuração +InnerTubeCfg.tab.Cluster = Grupo +InnerTubeCfg.tab.ttip.Cluster = Configuração de conjunto +InnerTubeCfg.tab.Radialpos = Posição radial +InnerTubeCfg.tab.ttip.Radialpos = Posição radial +InnerTubeCfg.lbl.Selectclustercfg = Selecionar a configuração do conjunto: +InnerTubeCfg.lbl.TubeSep = Separação do tubo: +InnerTubeCfg.lbl.ttip.TubeSep = Separação dos tubos, 1,0 = tocam um ao outro +InnerTubeCfg.lbl.Rotation = Rotação +InnerTubeCfg.lbl.ttip.Rotation = Ângulo de rotação da configuração do conjunto +InnerTubeCfg.lbl.Rotangle = Ângulo de rotação da configuração do conjunto +InnerTubeCfg.but.Splitcluster = Divisão do conjunto +InnerTubeCfg.lbl.longA1 = Dividir o grupo em componentes separados.
+InnerTubeCfg.lbl.longA2 = Isso duplica também todos os componentes ligados a este tubo interior. +InnerTubeCfg.but.Resetsettings = Redefinir as configurações +InnerTubeCfg.but.ttip.Resetsettings = Repor a separação e rotação para os valores padrão +# LaunchLugConfig +LaunchLugCfg.lbl.Length = Comprimento: +LaunchLugCfg.lbl.Outerdiam = Diâmetro Externo +LaunchLugCfg.lbl.Innerdiam = Diâmetro interno: +LaunchLugCfg.lbl.Thickness = Espessura: +LaunchLugCfg.lbl.Radialpos = Posição radial: +LaunchLugCfg.lbl.Posrelativeto = Posição em relação a: +LaunchLugCfg.lbl.plus = mais +LaunchLugCfg.tab.General = Geral +LaunchLugCfg.tab.Generalprop = Propriedades gerais +# MassComponentConfig +MassComponentCfg.lbl.Mass = Massa. +MassComponentCfg.lbl.Density = Densidade aproximada: +MassComponentCfg.lbl.Length = Comprimento: +MassComponentCfg.lbl.Diameter = Diâmetro +MassComponentCfg.lbl.PosRelativeto = Posição em relação a: +MassComponentCfg.lbl.plus = mais +MassComponentCfg.tab.General = Geral +MassComponentCfg.tab.ttip.General = Propriedades gerais +MassComponentCfg.tab.Radialpos = Posição radial +MassComponentCfg.tab.ttip.Radialpos = Configuração posição radial +MassComponentCfg.lbl.Radialdistance = Distância radial: +MassComponentCfg.lbl.Radialdirection = Direção radial: +MassComponentCfg.but.Reset = Restaurar +# MotorConfig +MotorCfg.checkbox.compmotormount = Este componente é uma montagem do motor +MotorCfg.lbl.Motorcfg = Configuração do motor: +MotorCfg.but.New = Novo +MotorCfg.lbl.Currentmotor = Motor atual: +MotorCfg.lbl.Motoroverhang = Extensão do motor: +MotorCfg.lbl.Ignitionat = Ignição em: +MotorCfg.lbl.plus = mais +MotorCfg.lbl.seconds = segundos +MotorCfg.lbl.longA1 = O projeto atual tem apenas uma fase. +MotorCfg.lbl.longA2 = Estágios podem ser adicionados clicando em \"Novo estágio\". +MotorCfg.lbl.longB1 = O projeto atual tem +MotorCfg.lbl.longB2 = estágios. +MotorCfg.but.Selectmotor = Selecionar do motor +MotorCfg.but.Removemotor = Remova o motor +MotorCfg.lbl.motorLabel = Nenhum +# NoseConeConfig +NoseConeCfg.lbl.Noseconeshape = Forma da ogiva: +NoseConeCfg.lbl.Shapeparam = Parâmetro de forma: +NoseConeCfg.lbl.Noseconelength = Comprimento da ogiva: +NoseConeCfg.lbl.Basediam = Diâmetro da base: +NoseConeCfg.checkbox.Automatic = Automático +NoseConeCfg.lbl.Wallthickness = Espessura de parede: +NoseConeCfg.checkbox.Filled = Atribuído +NoseConeCfg.tab.General = Geral +NoseConeCfg.tab.ttip.General = Propriedades gerais +NoseConeCfg.tab.Shoulder = Ressalto +NoseConeCfg.tab.ttip.Shoulder = Propriedades do ressalto +# ParachuteConfig +ParachuteCfg.lbl.Canopy = Canopy: +ParachuteCfg.lbl.Diameter = Diâmetro +ParachuteCfg.lbl.Material = Material: +ParachuteCfg.combo.MaterialModel = O material componente afeta o peso da componente. +ParachuteCfg.lbl.longA1 = coeficiente de arrasto CD: +ParachuteCfg.lbl.longB1 = Coeficiente de arrasto em relação à área total do pára-quedas.
+ParachuteCfg.lbl.longB2 = Um maior coeficiente de arrasto gera uma taxa de descida lenta. +ParachuteCfg.lbl.longB3 = Um valor típico para o pára-quedas é 0,8. +ParachuteCfg.but.Reset = Restaurar +ParachuteCfg.lbl.Shroudlines = Linhas de suspensão: +ParachuteCfg.lbl.Numberoflines = Número de linhas +ParachuteCfg.lbl.Linelength = Comprimento da linha: +ParachuteCfg.lbl.Posrelativeto = Posição em relação a: +ParachuteCfg.lbl.plus = mais +ParachuteCfg.lbl.Packedlength = Comprimento embalado: +ParachuteCfg.lbl.Packeddiam = Diâmetro embalado: +ParachuteCfg.lbl.Deploysat = Implanta em: +ParachuteCfg.lbl.seconds = segundo +ParachuteCfg.lbl.Altitude = Altitude: +ParachuteCfg.tab.General = Geral +ParachuteCfg.tab.ttip.General = Propriedades gerais +ParachuteCfg.tab.Radialpos = Posição radial +ParachuteCfg.tab.ttip.Radialpos = Configuração posição radial +ParachuteCfg.lbl.Radialdistance = Distância radial: +ParachuteCfg.lbl.Radialdirection = Direção radial: +ParachuteCfg.lbl.plusdelay = mais +ShockCordCfg.lbl.Shockcordmaterial = Material de cabo de choque: +ShockCordCfg.lbl.Posrelativeto = Posição em relação a: +ShockCordCfg.lbl.plus = mais +ShockCordCfg.lbl.Packedlength = Comprimento embalado: +ShockCordCfg.lbl.Packeddiam = Diâmetro embalado: +ShockCordCfg.tab.General = Geral +ShockCordCfg.tab.ttip.General = Propriedades gerais +# SleeveConfig +SleeveCfg.tab.Outerdiam = Diâmetro Externo +SleeveCfg.tab.Innerdiam = Diâmetro interno: +SleeveCfg.tab.Wallthickness = Espessura de parede: +SleeveCfg.tab.Length = Comprimento: +SleeveCfg.tab.General = Geral +SleeveCfg.tab.Generalproperties = Propriedades gerais +# StreamerConfig +StreamerCfg.lbl.Striplength = Tira comprimento: +StreamerCfg.lbl.Stripwidth = Largura da tira: +StreamerCfg.lbl.Striparea = Área da tira: +StreamerCfg.lbl.Aspectratio = Aspecto: +StreamerCfg.lbl.Material = Material: +StreamerCfg.combo.ttip.MaterialModel = O material do componente afeta o peso do componente. +StreamerCfg.lbl.longA1 = Coeficiente de arrasto CD : +StreamerCfg.lbl.longB1 = O coeficiente de arrasto em relação à área total da fita.
+StreamerCfg.lbl.longB2 = Um maior coeficiente de arrasto gera uma taxa de descida lenta. +StreamerCfg.lbl.Automatic = Automático +StreamerCfg.lbl.longC1 = O coeficiente de arrasto é relativa à área da fita +StreamerCfg.lbl.Posrelativeto = Posição em relação a: +StreamerCfg.lbl.plus = mais +StreamerCfg.lbl.Packedlength = Comprimento embalado: +StreamerCfg.lbl.Packeddiam = Diâmetro embalado: +StreamerCfg.lbl.Deploysat = Implanta em: +StreamerCfg.lbl.seconds = segundo +StreamerCfg.lbl.Altitude = Altitude: +StreamerCfg.tab.General = Geral +StreamerCfg.tab.ttip.General = Propriedades gerais +StreamerCfg.tab.Radialpos = Posição radial +StreamerCfg.tab.ttip.Radialpos = Configuração posição radial +StreamerCfg.lbl.Radialdistance = Distância radial: +StreamerCfg.lbl.Radialdirection = Direção radial: +StreamerCfg.but.Reset = Restaurar +StreamerCfg.lbl.plusdelay = mais +# ThicknessRingComponentConfig +ThicknessRingCompCfg.tab.Outerdiam = Diâmetro Externo +ThicknessRingCompCfg.tab.Innerdiam = Diâmetro interno: +ThicknessRingCompCfg.tab.Wallthickness = Espessura de parede: +ThicknessRingCompCfg.tab.Length = Comprimento: +ThicknessRingCompCfg.tab.General = Geral +ThicknessRingCompCfg.tab.Generalprop = Propriedades gerais +# TransitionConfig +TransitionCfg.lbl.Transitionshape = Forma de transição: +TransitionCfg.checkbox.Clipped = Cortado +TransitionCfg.lbl.Shapeparam = Parâmetro de forma: +TransitionCfg.lbl.Transitionlength = Comprimento de transição: +TransitionCfg.lbl.Forediam = Diâmetro dianteiro: +TransitionCfg.checkbox.Automatic = Automático +TransitionCfg.lbl.Aftdiam = Diâmetro da popa: +TransitionCfg.lbl.Wallthickness = Espessura de parede: +TransitionCfg.checkbox.Filled = Atribuído +TransitionCfg.tab.General = Geral +TransitionCfg.tab.Generalproperties = Propriedades gerais +TransitionCfg.tab.Shoulder = ressalto +TransitionCfg.tab.Shoulderproperties = Propriedades do ressalto +# TrapezoidFinSetConfig +TrapezoidFinSetCfg.lbl.Nbroffins = Número de aletas: +TrapezoidFinSetCfg.lbl.ttip.Nbroffins = Número de alhetas no conjunto de aletas. +TrapezoidFinSetCfg.lbl.Finrotation = Rotação da aleta: +TrapezoidFinSetCfg.lbl.ttip.Finrotation = Ângulo da primeira aleta no conjunto de aletas. +TrapezoidFinSetCfg.lbl.Fincant = inclinação da aleta: +TrapezoidFinSetCfg.lbl.ttip.Fincant = O ângulo que as aletas são oblíquos em relação ao corpo do foguete. +TrapezoidFinSetCfg.lbl.Rootchord = Corda da raiz: +TrapezoidFinSetCfg.lbl.Tipchord = Dica da corda: +TrapezoidFinSetCfg.lbl.Height = Altura: +TrapezoidFinSetCfg.lbl.Sweeplength = Comprimento do contorno: +TrapezoidFinSetCfg.lbl.Sweepangle = Ângulo do contorno: +TrapezoidFinSetCfg.lbl.FincrossSection = Secção transversal da aleta: +TrapezoidFinSetCfg.lbl.Thickness = Espessura: +TrapezoidFinSetCfg.lbl.Posrelativeto = Posição em relação a: +TrapezoidFinSetCfg.lbl.plus = mais +TrapezoidFinSetCfg.tab.General = Geral +TrapezoidFinSetCfg.tab.Generalproperties = Propriedades gerais +# MotorConfigurationModel +MotorCfgModel.Editcfg = Editar configurações +# StorageOptionChooser +StorageOptChooser.lbl.Simdatatostore = Dados simulados para armazenar: +StorageOptChooser.rdbut.Allsimdata = Todos os dados simulados +StorageOptChooser.lbl.longA1 = Armazenar todos os dados simulados.
+StorageOptChooser.lbl.longA2 = Isto pode resultar em ficheiros muito grandes! +StorageOptChooser.rdbut.Every = Todo +StorageOptChooser.lbl.longB1 = Armazene os valores aproximados para plotagem.
+StorageOptChooser.lbl.longB2 = Valores maiores resultar em arquivos menores. +StorageOptChooser.lbl.seconds = segundos +StorageOptChooser.rdbut.Onlyprimfig = Somente figuras primárias +StorageOptChooser.lbl.longC1 = Armazenar apenas os valores mostrados na tabela de resumo.
+StorageOptChooser.lbl.longC2 = Isto resulta em arquivos menores. +StorageOptChooser.checkbox.Compfile = Compactar arquivos +StorageOptChooser.lbl.UsingComp = Usando compressão reduz o tamanho do arquivo significativamente. +StorageOptChooser.lbl.longD1 = Uma estimativa sobre o tamanho do arquivo resultante seria com as opções de presentes. +StorageOptChooser.ttip.Saveopt = Salvar as Opções +StorageOptChooser.lbl.Estfilesize = Tamanho estimado: +StorageOptChooser.lbl.Saveopt = Salvar as Opções +# ThrustCurveMotorSelectionPanel +TCMotorSelPan.lbl.Selrocketmotor = Selecione motor do foguete: +TCMotorSelPan.checkbox.hideSimilar = Esconder as curvas de impulso muito semelhantes +TCMotorSelPan.SHOW_DESCRIPTIONS.desc1 = Mostrar todos os motores +TCMotorSelPan.SHOW_DESCRIPTIONS.desc2 = Mostrar motores com um diâmetro menor do que a montagem do motor +TCMotorSelPan.SHOW_DESCRIPTIONS.desc3 = Mostrar motores com um diâmetro igual ao da montagem do motor +TCMotorSelPan.lbl.Motormountdia = Diâmetro da montagem do motor: +TCMotorSelPan.lbl.Search = Pesquisar: +TCMotorSelPan.lbl.Selectthrustcurve = Selecione curva de empuxo: +TCMotorSelPan.lbl.Ejectionchargedelay = Atraso da carga de ejeção: +TCMotorSelPan.equalsIgnoreCase.None = Nenhum +TCMotorSelPan.lbl.NumberofsecondsorNone = (Número de segundos ou \"Nenhum\") +TCMotorSelPan.lbl.Totalimpulse = Impulso total: +TCMotorSelPan.lbl.Avgthrust = Empuxo médio: +TCMotorSelPan.lbl.Maxthrust = Empuxo máximo: +TCMotorSelPan.lbl.Burntime = Tempo de queima: +TCMotorSelPan.lbl.Launchmass = Massa do lançamento: +TCMotorSelPan.lbl.Emptymass = Massa em vazio: +TCMotorSelPan.lbl.Datapoints = Pontos de dados: +TCMotorSelPan.lbl.Digest = Sumário: +TCMotorSelPan.title.Thrustcurve = Curva de empuxo: +TCMotorSelPan.title.Thrust = Impulso +TCMotorSelPan.delayBox.None = Nenhum +TCMotorSelPan.noDescription = Nenhuma descrição disponível. +# PlotDialog +PlotDialog.title.Flightdataplot = Plotagem dos dados de voo +PlotDialog.Chart.Simulatedflight = Caminho plotado +PlotDialog.CheckBox.Showdatapoints = Mostram os pontos de dados +PlotDialog.lbl.Chart = Clique e arraste para baixo+direita para ampliar, para cima+para a esquerda para diminuir o zoom +# "main" prefix is used for the main application dialog +# FIXME: Rename the description keys +main.menu.file = Arquivo +main.menu.file.desc = Tarefas relacionadas com manipulação de arquivos +main.menu.file.new = Novo +main.menu.file.new.desc = Criar um projeto novo foguete +main.menu.file.open = Abrir... +BasicFrame.item.Openrocketdesign = Abrir um projeto de foguete +main.menu.file.openRecent = Abrir recente... +BasicFrame.item.Openrecentrocketdesign = Abrir um projeto recente de foguete +main.menu.file.openExample = Abrir exemplo... +BasicFrame.item.Openexamplerocketdesign = Abrir um projeto exemplo de foguete +main.menu.file.save = Gravar +BasicFrame.item.SavecurRocketdesign = Salvar o projeto do foguete atual +main.menu.file.saveAs = Salvar como +BasicFrame.item.SavecurRocketdesnewfile = Salvar o projeto do foguete atual para um novo arquivo +main.menu.file.print = Imprimir / Exportar PDF... +main.menu.file.print.desc = Imprimir ou salvar como PDF a lista de peças e modelos de aleta +main.menu.file.close = Fechar +BasicFrame.item.Closedesign = Fechar o projeto do foguete atual +main.menu.file.quit = Sair +BasicFrame.item.Quitprogram = Sair do programa +main.menu.edit = Editar +BasicFrame.menu.Rocketedt = Edição do foguete +main.menu.edit.undo = Desfazer +main.menu.edit.undo.desc = Desfazer a operação anterior +main.menu.edit.redo = Refazer +main.menu.edit.redo.desc = Refazer a operação previamente desfeita +main.menu.edit.cut = Cortar +main.menu.edit.copy = Copiar +main.menu.edit.paste = Colar +main.menu.edit.delete = Excluir +main.menu.edit.resize = Escala... +main.menu.edit.resize.desc = Escalar partes do projeto do foguete +main.menu.edit.editpreset = Editar Arquivo Componente Predefinição +main.menu.edit.preferences = Preferências +main.menu.edit.preferences.desc = Configurar as preferências do aplicativo +main.menu.analyze = Analisar +main.menu.analyze.desc = Análise do foguete +main.menu.analyze.componentAnalysis = Análise dos componentes +main.menu.analyze.componentAnalysis.desc = Analisar os componentes dos foguetes separadamente +main.menu.analyze.optimization = Otimização do foguete +main.menu.analyze.optimization.desc = Otimização do projeto geral do foguete +main.menu.analyze.customExpressions = Expressões personalizadas +main.menu.analyze.customExpressions.desc = Definir novos tipos de dados de voo por escrito personalizados expressões matemáticas +main.menu.help = Ajuda +main.menu.help.desc = Informações sobre OpenRocket +main.menu.help.tours = Visitas guiadas +main.menu.help.tours.desc = Tome visitas guiadas em OpenRocket +main.menu.help.license = Licença +main.menu.help.license.desc = Informações de licença OpenRocket +main.menu.help.bugReport = Relatório de erro +main.menu.help.bugReport.desc = Informações sobre como relatar erros do OpenRocket +main.menu.help.debugLog = Relatório de Depuração +main.menu.help.debugLog.desc = Ver o relatório de depuração do OpenRocket +main.menu.help.about = Sobre +main.menu.help.about.desc = Detalhes de direitos autorais sobre OpenRocket +main.menu.debug = Depuração +main.menu.debug.whatisthismenu = O que é esse menu? +main.menu.debug.createtestrocket = Criar foguete de teste +# database +# Translate here all material database +Material.CUSTOM = Personalizado +# Material database +Databases.materials.types.Bulk = Em massa +Databases.materials.types.Line = Linha +Databases.materials.types.Surface = Superfície +# BULK_MATERIAL +material.acrylic = Acrílico +material.aluminum = Alumínio +material.balsa = Balsa +material.basswood = Tília Americana +material.birch = Bétula +material.brass = Bronze +material.cardboard = Papel +material.carbon_fiber = Fibra de carbono +material.cork = Cortiça +material.depron_xps = Depron (XPS) +material.fiberglass = Fibra de vidro +material.kraft_phenolic = Fenólico Kraft +material.maple = Bordo +material.paper_office = Papel (escritório) +material.pine = Pinho +material.plywood_birch = Madeira compensada (bétula) +material.polycarbonate_lexan = Policarbonato (Lexan) +material.polystyrene = Poliestireno +material.pvc = PVC +material.spruce = Spruce +material.steel = Aço +material.styrofoam_generic_eps = Isopor (EPS genéricos) +material.styrofoam_blue_foam_xps = Isopor \"espuma azul\" (XPS) +material.titanium = Titânio +material.quantum_tubing = Tubulação Quantum +material.blue_tube = Tubo azul +# SURFACE_MATERIAL +material.ripstop_nylon = Nylon \"Ripstop\" +material.mylar = Mylar +material.polyethylene_thin = Polietileno (fino) +material.polyethylene_heavy = Polietileno (pesado) +material.silk = Seda +material.cellophane = Celofane +material.crepe_paper = Papel crepom +# LINE_MATERIAL +material.thread_heavy_duty = Linha (pesada) +material.elastic_cord_round_2_mm_1_16_in = Elástico (cerca de 2mm, 1/16 pol) +material.elastic_cord_flat_6_mm_1_4_in = Elástico (plano 6 mm, 1/4 pol) +material.elastic_cord_flat_12_mm_1_2_in = Elástico (plano 12 mm, 1/2 pol) +material.elastic_cord_flat_19_mm_3_4_in = Elástico (plano 19 mm, 3/4 pol) +material.elastic_cord_flat_25_mm_1_in = Elástico (plano 25 mm, 1 pol) +material.braided_nylon_2_mm_1_16_in = Nylon entrançado (2 mm, 1/16 pol) +material.braided_nylon_3_mm_1_8_in = Nylon trançado (3 mm, 1/8 pol) +material.tubular_nylon_11_mm_7_16_in = Nylon tubular (11 mm, 7/16 pol) +material.tubular_nylon_14_mm_9_16_in = Nylon tubular (14 mm, 9/16 pol) +material.tubular_nylon_25_mm_1_in = Nylon tubular (25 mm, 1 pol) +# ExternalComponent +ExternalComponent.Rough = Áspero +ExternalComponent.Unfinished = Inacabado +ExternalComponent.Regularpaint = Pintura regular +ExternalComponent.Smoothpaint = Pintura lisa +ExternalComponent.Polished = Polido +# LineStyle +LineStyle.Solid = Básico +LineStyle.Dashed = Tracejada +LineStyle.Dotted = Pontilhado +LineStyle.Dash-dotted = Traço-pontilhada +LineStyle.Defaultstyle = Estilo padrão +# Shape +Shape.Conical = Cônico +Shape.Conical.desc1 = Uma ogiva cônica tem um perfil de um triângulo. +Shape.Conical.desc2 = Uma transição cônica tem lados retos. +Shape.Ogive = Ogiva +Shape.Ogive.desc1 = Uma ogiva cônica tem um perfil que é um segmento de um círculo. O valor do parâmetro de forma 1 produz uma ogiva tangente, que tem uma transição suave para o tubo do foguete, valores menores do que 1 produzemogivas secantes. +Shape.Ogive.desc2 = Uma ogiva de transição tem um perfil que é um segmento de um círculo. O valor do parâmetro de forma 1 produz uma ogiva tangente, que tem uma transição suave para o tubo do foguete, valores menores do que 1 produzem ogivas secantes. +Shape.Ellipsoid = Elipsóide +Shape.Ellipsoid.desc1 = Uma ogiva elipsoidal tem o perfil de mia-elipse com o comprimento do eixo maior 2×Comprimento and Diâmetro. +Shape.Ellipsoid.desc2 = Uma elipsoide de transição tem um perfil que é uma meia elipse com o eixo maior de comprimento 2×Comprimento eDiâmetro. Se a transição não for grampeado, o perfil da transição é estendido ao centro do raio corrspondente. +Shape.Powerseries = Série de potência +Shape.Powerseries.desc1 = Uma ogiva tipo série de potência tem o perfil de Raio × (x / Comprimento)k ondek é o parâmetro de forma. Para k=0.5 é uma ½-power ou ogiva parabólica, para k=0.75 a ¾-power, e parak=1 uma ogiva cônica. +Shape.Powerseries.desc2 = Ums transição série de potências tem o perfil de Raio × (x / Comprimento)k onde k é o parâmetro de forma. Para k=0.5 a transição é ½-power ou parabólica, para k=0.75 a ¾-power, e para k=1 cônica. +Shape.Parabolicseries = Série parabólica +Shape.Parabolicseries.desc1 = Uma ogiva tipo parabólica tem o perfil de uma parábola. O parâmetro de forma define o segmento da parábola a ser utilizado. O parâmetro de forma 1.0 produz uma parábola inteira tangenciando o corpo do foguete, 0.75 produz 3/4 de parábola, 0.5 produz 1/2 parábola e 0 produz uma ogica cônica. +Shape.Parabolicseries.desc2 = Uma transição tipo parabólica tem o perfil de uma parábola. O parâmetro de forma define o segmento da parábola a ser utilizado. O parâmetro de forma 1.0 produz uma parábola inteira tangenciando o corpo do foguete, 0.75 produz 3/4 de parábola, 0.5 produz 1/2 parábola e 0 produz uma ogica cônica. +Shape.Haackseries = Série de Haack +Shape.Haackseries.desc1 = Uma ogiva série de Haack é projetada para diminuir o arrasto. O parâmetro de forma 0.0 produz uma ogiva LD-Haack ou Von Karman, que minimiza o arrasto para comprimento e diâmetro fixos, enquanto um valor de 0.333 produz uma ogiva LV-Haack, que minimiza o arrasto para comprimento e volume fixos. +Shape.Haackseries.desc2 = As ogivas série de Haack são projetadas para diminuir o arrasto. Essas formas de transição são equivalentes, mas não necessáriamente produzem uma melhoria no arrasto. O parâmetro de forma 0.0 produz uma ogiva LD-Haack ou Von Karman, enquanto um valor de 0.333 produz uma ogiva LV-Haack. +# RocketComponent +RocketComponent.Position.TOP = Parte superior do componente pai +RocketComponent.Position.MIDDLE = Meio do componente pai +RocketComponent.Position.BOTTOM = Parte do componente pai +RocketComponent.Position.AFTER = Depois do componente pai +RocketComponent.Position.ABSOLUTE = Dica da ogiva +# LaunchLug +LaunchLug.Launchlug = Guia de lançamento +# NoseCone +NoseCone.NoseCone = Ogiva +# Transition +Transition.Transition = Transição +# Stage +Stage.Stage = Etapa +Stage.SeparationEvent.UPPER_IGNITION = Ignição do motor do estágio superior +Stage.SeparationEvent.IGNITION = Ignição do motor do estágio atual +Stage.SeparationEvent.BURNOUT = Queima do motor do estágio atual +Stage.SeparationEvent.EJECTION = Carga de ejeção do estágio atual +Stage.SeparationEvent.LAUNCH = Lançamento +Stage.SeparationEvent.NEVER = Nunca +# BodyTube +BodyTube.BodyTube = Tubo do corpo +# TubeCoupler +TubeCoupler.TubeCoupler = Acoplador de tubo +# InnerTube +InnerTube.InnerTube = Tubo interno +# TrapezoidFinSet +TrapezoidFinSet.TrapezoidFinSet = Conjunto de aletas trapezoidais +# FreeformFinSet +FreeformFinSet.FreeformFinSet = Conjunto de aletas de formato livre +# MassComponent +MassComponent.MassComponent = Componente de massa +# Parachute +Parachute.Parachute = Pára-quedas +# ShockCord +ShockCord.ShockCord = Cabo de choque +# Bulkhead +Bulkhead.Bulkhead = Anteparo +# CenteringRing +CenteringRing.CenteringRing = Anel centralizador +# EngineBlock +EngineBlock.EngineBlock = Bloco de motor +# Streamer +Streamer.Streamer = Fita +# Sleeve +Sleeve.Sleeve = Manga +# Rocket +Rocket.motorCount.Nomotor = Sem motores +Rocket.compname.Rocket = Foguete +# MotorMount +MotorMount.IgnitionEvent.AUTOMATIC = Automático (lançamento ou carga de ejeção) +MotorMount.IgnitionEvent.LAUNCH = Lançamento +MotorMount.IgnitionEvent.EJECTION_CHARGE = Primeira carga de ejeção do estágio anterior +MotorMount.IgnitionEvent.BURNOUT = Primeira queima do estágio anterior +MotorMount.IgnitionEvent.NEVER = Nunca +# ComponentIcons +ComponentIcons.Nosecone = Ogiva +ComponentIcons.Bodytube = Tubo do corpo +ComponentIcons.Transition = Transição +ComponentIcons.Trapezoidalfinset = Conjunto de aletas trapezoidais +ComponentIcons.Ellipticalfinset = Conjunto de aletas elípticas +ComponentIcons.Freeformfinset = Conjunto de aletas de formato livre +ComponentIcons.Launchlug = Guia de lançamento +ComponentIcons.Innertube = Tubo interno +ComponentIcons.Tubecoupler = Acoplador de tubo +ComponentIcons.Centeringring = Centrar anel +ComponentIcons.Bulkhead = Cabeça massa +ComponentIcons.Engineblock = Bloco de motor +ComponentIcons.Parachute = Pára-quedas +ComponentIcons.Streamer = Fita +ComponentIcons.Shockcord = Cabo de choque +ComponentIcons.Masscomponent = Componente de massa +ComponentIcons.disabled = (desabilitado) +# StageAction +StageAction.Stage = Estágio +# RecoveryDevice +RecoveryDevice.DeployEvent.LAUNCH = Lançamento (mais NN segundos) +RecoveryDevice.DeployEvent.EJECTION = Primeira carga de ejeção deste estágio +RecoveryDevice.DeployEvent.APOGEE = Apogeu +RecoveryDevice.DeployEvent.ALTITUDE = Altitude específica durante a descida +RecoveryDevice.DeployEvent.CURRENT_STAGE_SEPARATION = Separação do estágio atual +RecoveryDevice.DeployEvent.LOWER_STAGE_SEPARATION = Separação do estágio inferior +RecoveryDevice.DeployEvent.NEVER = Nunca +# FlightEvent +FlightEvent.Type.LAUNCH = Lançamento +FlightEvent.Type.IGNITION = Ignição do motor +FlightEvent.Type.LIFTOFF = Decolagem +FlightEvent.Type.LAUNCHROD = Folga da haste de lançamento +FlightEvent.Type.BURNOUT = Queima do motor +FlightEvent.Type.EJECTION_CHARGE = Ejeção da carga +FlightEvent.Type.STAGE_SEPARATION = Separação do estágio +FlightEvent.Type.APOGEE = Apogeu +FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT = Implantação de dispositivos de recuperação +FlightEvent.Type.GROUND_HIT = Toque no solo +FlightEvent.Type.SIMULATION_END = Final de simulação +FlightEvent.Type.ALTITUDE = Mudança de altitude +# ThrustCurveMotorColumns +TCurveMotorCol.MANUFACTURER = Fabricante +TCurveMotorCol.DESIGNATION = Designação +TCurveMotorCol.TYPE = Tipo +TCurveMotorCol.DIAMETER = Diâmetro +TCurveMotorCol.LENGTH = Comprimento +TCurveMotor.ttip.diameter = Diâmetro +TCurveMotor.ttip.length = Comprimento: +TCurveMotor.ttip.maxThrust = Máximo de impulso: +TCurveMotor.ttip.avgThrust = Esforço médio: +TCurveMotor.ttip.burnTime = Tempo de queima: +TCurveMotor.ttip.totalImpulse = Impulso total: +TCurveMotor.ttip.launchMass = Massa no lançamento: +TCurveMotor.ttip.emptyMass = Massa em vazio: +# RocketInfo +RocketInfo.lengthLine.Length = Tamanho +RocketInfo.lengthLine.maxdiameter = , diâmetro máx. +RocketInfo.massText1 = Massa com motores +RocketInfo.massText2 = Massa sem motores +RocketInfo.at = em M= +RocketInfo.cgText = CG: +RocketInfo.cpText = CP: +RocketInfo.stabText = Estabilidade: +RocketInfo.Warning = Alerta: +RocketInfo.Calculating = Calculando... +RocketInfo.Apogee = Apogeu: +RocketInfo.Maxvelocity = Velocidade máx.: +RocketInfo.Maxacceleration = Aceleração máx.: +RocketInfo.apogeeValue = N/D +RocketInfo.Mach = (Mach +RocketInfo.velocityValue = N/D +RocketInfo.accelerationValue = N/D +# FinSet +FinSet.CrossSection.SQUARE = Quadrado +FinSet.CrossSection.ROUNDED = Arredondado +FinSet.CrossSection.AIRFOIL = Aerofólio +FinSet.TabRelativePosition.FRONT = Corda da raiz da borda dianteira +FinSet.TabRelativePosition.CENTER = Corda do ponto médio da raiz +FinSet.TabRelativePosition.END = Corda da raiz da borda traseira +# FlightDataType +FlightDataType.TYPE_TIME = Tempo +FlightDataType.TYPE_ALTITUDE = Altitude +FlightDataType.TYPE_VELOCITY_Z = Velocidade vertical +FlightDataType.TYPE_ACCELERATION_Z = Aceleração vertical +FlightDataType.TYPE_VELOCITY_TOTAL = Velocidade total +FlightDataType.TYPE_ACCELERATION_TOTAL = Aceleração total +FlightDataType.TYPE_POSITION_X = Position upwind +FlightDataType.TYPE_POSITION_Y = Posição paralela ao vento +FlightDataType.TYPE_POSITION_XY = Distância lateral +FlightDataType.TYPE_POSITION_DIRECTION = Direção lateral +FlightDataType.TYPE_VELOCITY_XY = Velocidade lateral +FlightDataType.TYPE_ACCELERATION_XY = Aceleração lateral +FlightDataType.TYPE_AOA = Ângulo de ataque +FlightDataType.TYPE_ROLL_RATE = Taxa de rotação +FlightDataType.TYPE_PITCH_RATE = Taxa de arremesso +FlightDataType.TYPE_YAW_RATE = Taxa de guinada +FlightDataType.TYPE_MASS = Massa. +FlightDataType.TYPE_PROPELLANT_MASS = Massa do propelente +FlightDataType.TYPE_LONGITUDINAL_INERTIA = +FlightDataType.TYPE_ROTATIONAL_INERTIA = Momento de inércia de rotação +FlightDataType.TYPE_CP_LOCATION = Localização do CP +FlightDataType.TYPE_CG_LOCATION = Localização do CG +FlightDataType.TYPE_STABILITY = Calibres da margem de estabilidade +FlightDataType.TYPE_MACH_NUMBER = Número de Mach +FlightDataType.TYPE_REYNOLDS_NUMBER = Número de Reynolds +FlightDataType.TYPE_THRUST_FORCE = Impulso +FlightDataType.TYPE_DRAG_FORCE = Força de arrasto +FlightDataType.TYPE_DRAG_COEFF = Coeficiente de arrasto +FlightDataType.TYPE_AXIAL_DRAG_COEFF = Coeficiente de arrasto axial +FlightDataType.TYPE_FRICTION_DRAG_COEFF = Coeficiente de arrasto de atrito +FlightDataType.TYPE_PRESSURE_DRAG_COEFF = Coeficiente de arrasto de pressão +FlightDataType.TYPE_BASE_DRAG_COEFF = Coeficiente de arrasto de base +FlightDataType.TYPE_NORMAL_FORCE_COEFF = Coeficiente de força normal +FlightDataType.TYPE_PITCH_MOMENT_COEFF = Coeficiente de momento de Pitch +FlightDataType.TYPE_YAW_MOMENT_COEFF = Guinada coeficiente de momento +FlightDataType.TYPE_SIDE_FORCE_COEFF = Coeficiente de força lateral +FlightDataType.TYPE_ROLL_MOMENT_COEFF = Coeficiente do momento de rotação +FlightDataType.TYPE_ROLL_FORCING_COEFF = Coeficiente forçado de rotação +FlightDataType.TYPE_ROLL_DAMPING_COEFF = Coeficiente de amortecimento de rotação +FlightDataType.TYPE_PITCH_DAMPING_MOMENT_COEFF = Coeficiente de arremesso de amortecimento +FlightDataType.TYPE_YAW_DAMPING_MOMENT_COEFF = Guinada coeficiente de amortecimento +FlightDataType.TYPE_REFERENCE_LENGTH = Comprimento de referência +FlightDataType.TYPE_REFERENCE_AREA = Área de referência +FlightDataType.TYPE_ORIENTATION_THETA = Orientação vertical (zênite) +FlightDataType.TYPE_ORIENTATION_PHI = Orientação lateral (azimute) +FlightDataType.TYPE_WIND_VELOCITY = Velocidade do vento +FlightDataType.TYPE_AIR_TEMPERATURE = Temperatura do ar +FlightDataType.TYPE_AIR_PRESSURE = Pressão do ar +FlightDataType.TYPE_SPEED_OF_SOUND = Velocidade do som +FlightDataType.TYPE_TIME_STEP = Passo de tempo de simulação +FlightDataType.TYPE_COMPUTATION_TIME = Tempo de computação +FlightDataType.TYPE_LATITUDE = Latitude +FlightDataType.TYPE_LONGITUDE = Longitude +FlightDataType.TYPE_CORIOLIS_ACCELERATION = Aceleração de Coriolis +FlightDataType.TYPE_GRAVITY = Aceleração gravitacional +# PlotConfiguration +PlotConfiguration.Verticalmotion = Movimento vertical em função do tempo +PlotConfiguration.Totalmotion = Movimento total em função do tempo +PlotConfiguration.Flightside = Perfil lateral de voo +PlotConfiguration.Stability = Estabilidade em função do tempo +PlotConfiguration.Dragcoef = Arraste coeficientes vs número de Mach +PlotConfiguration.Rollcharacteristics = Características da rotação +PlotConfiguration.Angleofattack = Ângulo de ataque e de orientação em função do tempo +PlotConfiguration.Simulationtime = Passo da simulação de tempo e tempo de computação +# Warning +Warning.LargeAOA.str1 = Grande ângulo de ataque encontrado. +Warning.LargeAOA.str2 = Grande ângulo de ataque encontrou ( +Warning.DISCONTINUITY = Descontinuidade no diâmetro do corpo do foguete. +Warning.THICK_FIN = Aletas finas não pode ser modelado com precisão. +Warning.JAGGED_EDGED_FIN = Previsões com aletas de bordo irregular podem ser imprecisos. +Warning.LISTENERS_AFFECTED = Observador modificou a simulação de voo +Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING = Dispositivo de recuperação aberto enquanto o motor ainda está queimando. +Warning.FILE_INVALID_PARAMETER = Parâmetro inválido encontrado, ignorando. +Warning.PARALLEL_FINS = Muitas aletas paralelas +Warning.SUPERSONIC = Cálculos do corpo pode não ser totalmente precisos em velocidades supersônicas. +Warning.RECOVERY_LAUNCH_ROD = Dispositivo de recuperação implantado, enquanto na guia de lançamento. +Warning.RECOVERY_HIGH_SPEED = Implantação recuperação do dispositivo a alta velocidade +# Scale dialog +ScaleDialog.lbl.scaleRocket = Foguete inteiro +ScaleDialog.lbl.scaleSubselection = Seleção e todos os subcomponentes +ScaleDialog.lbl.scaleSelection = Único componente selecionado +ScaleDialog.title = Escala do projeto +ScaleDialog.lbl.scale = Escala: +ScaleDialog.lbl.scale.ttip = Selecione se a escala aplica-se ao projeto inteiro ou apenas o componente selecionado +ScaleDialog.lbl.scaling = Escala a aplicar: +ScaleDialog.lbl.scaling.ttip = Resultante de tamanho, os valores acima de 100% aumentam e os valores abaixo de 100% encolhem o desenho. +# The scaleFrom/scaleTo pair creates a phrase "Scale from [...] to [...]" +ScaleDialog.lbl.scaleFrom = Escala de +ScaleDialog.lbl.scaleTo = a +ScaleDialog.lbl.scaleFromTo.ttip = Definem o escalonamento baseado num comprimento original e resultante. +ScaleDialog.checkbox.scaleMass = Atualizar os valores de massa explícitas +ScaleDialog.checkbox.scaleMass.ttip = Escalar a massa do componente e modificar os valores de massa pelo cubo do fator de escala +ScaleDialog.button.scale = Escala\n +ScaleDialog.undo.scaleRocket = Escala do foguete +ScaleDialog.undo.scaleComponent = Escala do componente +ScaleDialog.undo.scaleComponents = Escala dos componentes +# icons +Icons.Undo = Desfazer +Icons.Redo = Refazer +OpenRocketPrintable.Partsdetail = Detalhe das peças +OpenRocketPrintable.Fintemplates = Modelos de aleta +OpenRocketPrintable.Transitiontemplates = Modelos de transição +OpenRocketPrintable.Noseconetemplates = Modelos de ogiva cone +OpenRocketPrintable.Finmarkingguide = Marcação da guia da aleta +OpenRocketPrintable.DesignReport = Relatório do Projeto +OpenRocketPrintable.Centeringringtemplates = Modelos de anéis centralizadores +OpenRocketDocument.Redo = Refazer +OpenRocketDocument.Undo = Desfazer +# EllipticalFinSet +EllipticalFinSet.Ellipticalfinset = Conjunto de aletas elípticas +# Optimization +# Modifiers +optimization.modifier.nosecone.length = Tamanho +optimization.modifier.nosecone.length.desc = Otimizar o comprimento da ogiva. +optimization.modifier.nosecone.diameter = Diâmetro +optimization.modifier.nosecone.diameter.desc = Otimizar o diâmetro da base da ogiva. +optimization.modifier.nosecone.thickness = Espessura +optimization.modifier.nosecone.thickness.desc = Otimizar a espessura da parede da ogiva. +optimization.modifier.nosecone.shapeparameter = Parâmetro de forma +optimization.modifier.nosecone.shapeparameter.desc = Otimizar o parâmetro de forma da ogiva. +optimization.modifier.transition.length = Comprimento +optimization.modifier.transition.length.desc = Otimizar a duração da transição. +optimization.modifier.transition.forediameter = Diâmetro dianteiro +optimization.modifier.transition.forediameter.desc = Otimizar a transição do diâmetro dianteiro. +optimization.modifier.transition.aftdiameter = Diâmetro traseiro +optimization.modifier.transition.aftdiameter.desc = Otimizar a transição do diâmetro traseiro. +optimization.modifier.transition.thickness = Espessura +optimization.modifier.transition.thickness.desc = Otimizar a espessura da parede de transição. +optimization.modifier.transition.shapeparameter = Parâmetro de forma +optimization.modifier.transition.shapeparameter.desc = Otimizar o parâmetro de forma de transição. +optimization.modifier.bodytube.length = Comprimento +optimization.modifier.bodytube.length.desc = Otimizar o comprimento do tubo corpo. +optimization.modifier.bodytube.outerDiameter = Diâmetro Externo +optimization.modifier.bodytube.outerDiameter.desc = Otimizar o diâmetro do tubo exterior do corpo, mantendo a espessura da parede. +optimization.modifier.bodytube.thickness = Espessura +optimization.modifier.bodytube.thickness.desc = Otimizar o corpo espessura da parede do tubo. +optimization.modifier.trapezoidfinset.rootChord = Corda da base +optimization.modifier.trapezoidfinset.rootChord.desc = Otimizar o comprimento da corda da base do conjunto de aletas (comprimento da aleta no corpo do foguete). +optimization.modifier.trapezoidfinset.tipChord = Corda do topo +optimization.modifier.trapezoidfinset.tipChord.desc = Otimizar o comprimento do conjunto das aletas (comprimento da aleta na borda externa). +optimization.modifier.trapezoidfinset.sweep = Contorno +optimization.modifier.trapezoidfinset.sweep.desc = Otimize o contorno do conjunto de aletas (distância que a borda dianteira contorna para trás) +optimization.modifier.trapezoidfinset.height = Altura +optimization.modifier.trapezoidfinset.height.desc = Otimizar a altura (semi-período) do conjunto de aletas. +optimization.modifier.ellipticalfinset.length = Corda da base +optimization.modifier.ellipticalfinset.length.desc = Otimize o comprimento da corda raiz do conjunto de aletas. +optimization.modifier.ellipticalfinset.height = Altura +optimization.modifier.ellipticalfinset.height.desc = Resultado de otimização +optimization.modifier.finset.cant = Ângulo Cant +optimization.modifier.finset.cant.desc = Otimizar o ângulo oblíquo do conjunto de aletas. +optimization.modifier.finset.position = Posição +optimization.modifier.finset.position.desc = Otimizar a posição conjunto de aletas ao longo do corpo do foguete. +optimization.modifier.launchlug.length = Comprimento +optimization.modifier.launchlug.length.desc = Otimize o comprimento da guia de lançamento. +optimization.modifier.launchlug.outerDiameter = Diâmetro Externo +optimization.modifier.launchlug.outerDiameter.desc = Otimize o diâmetro externo da guia de lançamento. +optimization.modifier.launchlug.thickness = Espessura +optimization.modifier.launchlug.thickness.desc = Otimizar a espessura da guia de lançamento, mantendo o diâmetro externo constante. +optimization.modifier.launchlug.position = Posição +optimization.modifier.launchlug.position.desc = Otimizar a posição da guia de lançamento ao longo do corpo do foguete. +optimization.modifier.internalcomponent.position = Posição +optimization.modifier.internalcomponent.position.desc = Otimizar a posição do componente em relação à componente principal. +optimization.modifier.masscomponent.mass = Massa. +optimization.modifier.masscomponent.mass.desc = Otimizar a massa do componente de massa. +optimization.modifier.parachute.diameter = Diâmetro +optimization.modifier.parachute.diameter.desc = Otimizar o diâmetro da copa pára-quedas. +optimization.modifier.parachute.coefficient = Coeficiente de arrasto +optimization.modifier.parachute.coefficient.desc = Otimizar o coeficiente de arrasto do pára-quedas. Pára-quedas típicos têm um coeficiente de arrasto de cerca de 0,8. +optimization.modifier.streamer.length = Comprimento +optimization.modifier.streamer.length.desc = Otimizar o comprimento da fita. +optimization.modifier.streamer.width = Largura +optimization.modifier.streamer.width.desc = Otimizar a largura da fita. +optimization.modifier.streamer.aspectRatio = Relação de aspecto +optimization.modifier.streamer.aspectRatio.desc = Otimizar a relação de aspecto da fita (comprimento / largura). Você NÃO deve selecionar comprimento da fita ou largura ao mesmo tempo. +optimization.modifier.streamer.coefficient = Arraste coeficiente +optimization.modifier.streamer.coefficient.desc = Otimizar o coeficiente de arrasto da fita. +optimization.modifier.recoverydevice.deployDelay = Implantação atraso +optimization.modifier.recoverydevice.deployDelay.desc = Otimizar o atraso de implantação do dispositivo de recuperação. +optimization.modifier.recoverydevice.deployAltitude = Altitude implantação +optimization.modifier.recoverydevice.deployAltitude.desc = Otimizar a altitude de implantação do dispositivo de recuperação. +optimization.modifier.rocketcomponent.overrideMass = Modificar massa +optimization.modifier.rocketcomponent.overrideMass.desc = Otimizar a massa do componente modificado. +optimization.modifier.rocketcomponent.overrideCG = Modificar o CG +optimization.modifier.rocketcomponent.overrideCG.desc = Otimizar o centro de gravidade modificado do componente. +optimization.modifier.motormount.overhang = Extensão do motor +optimization.modifier.motormount.overhang.desc = Otimizar a extensão do motor. +optimization.modifier.motormount.delay = Atraso na ignição do motor +optimization.modifier.motormount.delay.desc = Otimizar o atraso de ignição do motor. +# General rocket design optimization dialog +GeneralOptimizationDialog.title = Otimização do foguete +GeneralOptimizationDialog.goal.maximize = Maximizar o valor +GeneralOptimizationDialog.goal.minimize = Minimizar valor +GeneralOptimizationDialog.goal.seek = Procure valor de +GeneralOptimizationDialog.btn.start = Iniciar otimização +GeneralOptimizationDialog.btn.stop = Parar otimização +GeneralOptimizationDialog.lbl.paramsToOptimize = Parâmetros para otimizar: +GeneralOptimizationDialog.btn.add = Adicionar +GeneralOptimizationDialog.btn.add.ttip = Adicione o parâmetro selecionado para a otimização +GeneralOptimizationDialog.btn.remove = Remover +GeneralOptimizationDialog.btn.remove.ttip = Remova o parâmetro selecionado da otimização +GeneralOptimizationDialog.btn.removeAll = Remover todos +GeneralOptimizationDialog.btn.removeAll.ttip = Remova todos os parâmetros da otimização +GeneralOptimizationDialog.lbl.availableParams = Parâmetros disponíveis: +GeneralOptimizationDialog.lbl.optimizationOpts = Opções de otimização +GeneralOptimizationDialog.lbl.optimizeSim = Otimizar simulação: +GeneralOptimizationDialog.lbl.optimizeSim.ttip = Selecione qual simulação para otimizar +GeneralOptimizationDialog.lbl.optimizeValue = Valor otimizado: +GeneralOptimizationDialog.lbl.optimizeValue.ttip = Selecione o valor que deve ser otimizado +GeneralOptimizationDialog.lbl.optimizeGoal = Objetivo de otimização: +GeneralOptimizationDialog.lbl.optimizeGoal.ttip = Selecione o objetivo da otimização +GeneralOptimizationDialog.lbl.optimizeGoalValue.ttip = Valor personalizado para buscar +GeneralOptimizationDialog.lbl.requireStability = Estabilidade requerida +GeneralOptimizationDialog.lbl.requireMinStability = Estabilidade mínima: +GeneralOptimizationDialog.lbl.requireMinStability.ttip = Exigir uma margem de estabilidade mínima estática para o projeto +GeneralOptimizationDialog.lbl.requireMaxStability = Estabilidade máxima: +GeneralOptimizationDialog.lbl.requireMaxStability.ttip = Exigir uma margem de estabilidade estática máxima para o projeto +GeneralOptimizationDialog.status.bestValue = Melhor valor: +GeneralOptimizationDialog.status.bestValue.ttip = Valor melhor otimização encontrado até agora. +GeneralOptimizationDialog.status.stepCount = Contador de passos: +GeneralOptimizationDialog.status.stepCount.ttip = Número de passos de otimização que foram realizadas. +GeneralOptimizationDialog.status.evalCount = Avaliações: +GeneralOptimizationDialog.status.evalCount.ttip = Número total de avaliações da função (simulações) que tenham sido realizadas. +GeneralOptimizationDialog.status.stepSize = Tamanho do passo: +GeneralOptimizationDialog.status.stepSize.ttip = Tamanho do passo atual da otimização (em relação à extensão dos parâmetros de otimização) +GeneralOptimizationDialog.btn.plotPath = Traçar caminho +GeneralOptimizationDialog.btn.plotPath.ttip = Plotar o caminho de otimização (apenas para otimização de uma ou duas dimensões) +GeneralOptimizationDialog.btn.save = Salvar destino +GeneralOptimizationDialog.btn.save.ttip = Salvar os resultados das avaliações de função (simulações) como um arquivo CSV. +GeneralOptimizationDialog.btn.apply = Aplicar a otimização +GeneralOptimizationDialog.btn.apply.ttip = Aplicar os resultados de otimização para o projeto do foguete +GeneralOptimizationDialog.btn.reset = Restaurar +GeneralOptimizationDialog.btn.reset.ttip = Redefinir o projeto do foguete para o projeto do foguete atual +GeneralOptimizationDialog.btn.close = Fechar +GeneralOptimizationDialog.btn.close.ttip = Feche a caixa de diálogo sem modificar o projeto do foguete +GeneralOptimizationDialog.error.selectParams.text = Primeiro, selecione alguns parâmetros para otimizar a partir dos parâmetros disponíveis. +GeneralOptimizationDialog.error.selectParams.title = Selecione os parâmetros de otimização +GeneralOptimizationDialog.error.optimizationFailure.text = Falha na otimização ao executar: +GeneralOptimizationDialog.error.optimizationFailure.title = Otimização falhou +GeneralOptimizationDialog.undoText = Aplicar a otimização +GeneralOptimizationDialog.basicSimulationName = Simulação básica +GeneralOptimizationDialog.noSimulationName = Nenhuma simulação +GeneralOptimizationDialog.table.col.parameter = Parâmetros +GeneralOptimizationDialog.table.col.current = Atual +GeneralOptimizationDialog.table.col.min = Comprimento Mínimo +GeneralOptimizationDialog.table.col.max = Máximo +GeneralOptimizationDialog.export.header = Incluir linha de cabeçalho +GeneralOptimizationDialog.export.header.ttip = Incluir uma linha de cabeçalho como a primeira linha contendo as descrições de campo. +GeneralOptimizationDialog.export.stability = Estabilidade +# Dialog for plotting optimization results +OptimizationPlotDialog.title = Resultados da otimização +OptimizationPlotDialog.lbl.zoomInstructions = Clique e arraste para down+direita para ampliar, up+esquerda para diminuir o zoom +OptimizationPlotDialog.plot1d.title = Resultado de otimização +OptimizationPlotDialog.plot1d.series = Resultado de otimização +OptimizationPlotDialog.plot2d.title = Caminho de otimização +OptimizationPlotDialog.plot2d.path = Caminho de otimização +OptimizationPlotDialog.plot2d.evals = Avaliações +OptimizationPlotDialog.plot.ttip.stability = Estabilidade: +OptimizationPlotDialog.plot.label.optimum = Ótimo +# Optimization parameters +MaximumAltitudeParameter.name = Altitude do apogeu +MaximumVelocityParameter.name = Velocidade máxima +MaximumAccelerationParameter.name = Aceleração máxima +StabilityParameter.name = Estabilidade +GroundHitVelocityParameter.name = Velocidade ao atingir o solo +LandingDistanceParameter.name = Distância da aterrisagem +TotalFlightTimeParameter.name = Tempo total de voo +DeploymentVelocityParameter.name = Velocidade no acionamento do pára-quedas +# Compass directions drawn on a compass rose. +CompassRose.lbl.north = N +CompassRose.lbl.east = E +CompassRose.lbl.south = S +CompassRose.lbl.west = O +# Compass directions with subdirections. These might not be localized even if the directions on the compass rose are. +CompassSelectionButton.lbl.N = N +CompassSelectionButton.lbl.NE = NE +CompassSelectionButton.lbl.E = E +CompassSelectionButton.lbl.SE = SE +CompassSelectionButton.lbl.S = S +CompassSelectionButton.lbl.SW = SO +CompassSelectionButton.lbl.W = O +CompassSelectionButton.lbl.NW = NO +SlideShowDialog.btn.next = Próximo +SlideShowDialog.btn.prev = Anterior +SlideShowLinkListener.error.title = Visita guiada não encontrada +SlideShowLinkListener.error.msg = Desculpe, a visita selecionado ainda não foi escrita. +GuidedTourSelectionDialog.title = Visitas guiadas +GuidedTourSelectionDialog.lbl.selectTour = Selecione visita guiada: +GuidedTourSelectionDialog.lbl.description = Descrição da visita: +GuidedTourSelectionDialog.lbl.length = Número de slides: +GuidedTourSelectionDialog.btn.start = Iniciar a visita! +# Custom Fin BMP Importer +CustomFinImport.button.label = Importar de imagem +CustomFinImport.badFinImage = Imagem de aleta inválido. Certifique-se a aleta é um sólido de cor negra ou escura e tocar o fundo da imagem. +CustomFinImport.errorLoadingFile = Erro ao carregar arquivo: +CustomFinImport.errorParsingFile = Erro na análise da imagem de aleta: +CustomFinImport.undo = Importação de conjunto de aletas de forma livre +CustomFinImport.error.title = Erro ao carregar o perfil de aleta +CustomFinImport.error.badimage = Não foi possível deduzir forma da aleta na imagem. +CustomFinImport.description = A imagem será convertida internamente para imagem em preto e branco (preto para a aleta), por isso certifique-se de usar uma cor sólida escuro para a aleta, e branco ou uma cor clara para o fundo. A aleta deve tocar o fundo da imagem, que é a base da aleta. +PresetModel.lbl.select = Selecione ajustes pré-definidos +PresetModel.lbl.database = À partir do banco de dados... +# Component Preset Chooser Dialog +ComponentPresetChooserDialog.title = Escolha componentes pré-definidos +ComponentPresetChooserDialog.filter.label = Filtrar por texto: +ComponentPresetChooserDialog.checkbox.filterAftDiameter = Ajuste do diâmetro traseiro +ComponentPresetChooserDialog.checkbox.filterForeDiameter = Ajustar diâmetro dianteiro +ComponentPresetChooserDialog.menu.sortAsc = Classificação Crescente +ComponentPresetChooserDialog.menu.sortDesc = Classificação Decrescente +ComponentPresetChooserDialog.menu.units = Unidades +ComponentPresetChooserDialog.checkbox.showAllCompatible = Mostrar todos compatíveis +ComponentPresetChooserDialog.lbl.favorites = Selecione para adicionar predefinido para menu drop-down +table.column.Favorite = Favorito +table.column.Manufacturer = Fabricante +table.column.PartNo = Número da Peça +table.column.Description = Descrição +table.column.Type = Tip +table.column.Length = Comprimento +table.column.Width = Largura +table.column.InnerDiameter = Diâmetro interno +table.column.OuterDiameter = Diâmetro Externo +table.column.AftOuterDiameter = Aft Outer Diameter +table.column.AftShoulderLength = Comprimento posterior da saliência +table.column.AftShoulderDiameter = Diâmetro posterior da saliência +table.column.ForeShoulderLength = Comprimento anterior da saliência +table.column.ForeShoulderDiameter = Diâmetro anterior da saliência +table.column.ForeOuterDiameter = Diâmetro interno anterior +table.column.Shape = Forma +table.column.Material = Material +table.column.Finish = Terminar +table.column.Thickness = Espessura +table.column.Filled = Atribuído +table.column.Mass = Massa. +table.column.Diameter = Diâmetro +table.column.Sides = Laterais +table.column.LineCount = Contador de linhas +table.column.LineLength = Comprimento da Linha +table.column.LineMaterial = Material da Linha diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties index 419f679a6..8132f44d4 100644 --- a/core/resources/l10n/messages_ru.properties +++ b/core/resources/l10n/messages_ru.properties @@ -173,14 +173,14 @@ debuglogdlg.lbl.Stacktrace = \u0421\u0442\u0435\u043a \u0432\u044b\u0437\u043e\u MotorChooserDialog.title = \u0412\u044b\u0431\u043e\u0440 \u0440\u0430\u043a\u0435\u0442\u043d\u043e\u0433\u043e \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u044f ! Edit Motor configuration dialog -edtmotorconfdlg.but.removemotor = \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u044c -edtmotorconfdlg.but.Selectmotor = \u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u044c +MotorConfigurationPanel.btn.removeMotor = \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u044c +MotorConfigurationPanel.btn.selectMotor = \u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u044c edtmotorconfdlg.but.Removeconfiguration = \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e edtmotorconfdlg.but.Newconfiguration = \u041d\u043e\u0432\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f -edtmotorconfdlg.lbl.Motormounts = \u041a\u0440\u0435\u043f\u043b\u0435\u043d\u0438\u044f \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u0435\u0439: +MotorConfigurationPanel.lbl.motorMounts = \u041a\u0440\u0435\u043f\u043b\u0435\u043d\u0438\u044f \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u0435\u0439: edtmotorconfdlg.title.Editmotorconf = \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0439 \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u0435\u0439 edtmotorconfdlg.selectcomp = \u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u044f\u0432\u043b\u044f\u044e\u0449\u0438\u0435\u0441\u044f \u043a\u0440\u0435\u043f\u043b\u0435\u043d\u0438\u044f\u043c\u0438 \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u0435\u0439: -edtmotorconfdlg.lbl.Motorconfig = \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u0435\u0439: +MotorConfigurationPanel.lbl.motorConfiguration = \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u0435\u0439: edtmotorconfdlg.lbl.Configname = \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438: edtmotorconfdlg.lbl.Leavenamedefault = \u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0443\u0441\u0442\u044b\u043c \u0434\u043b\u044f \u0438\u043c\u0435\u043d\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e. @@ -1274,7 +1274,7 @@ Streamer.Streamer = \u0422\u043e\u0440\u043c\u043e\u0437\u043d\u0430\u044f \u043 Sleeve.Sleeve = \u0412\u0442\u0443\u043b\u043a\u0430 !Rocket -Rocket.motorCount.Nomotor = [\u0411\u0435\u0437 \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u0435\u0439] +Rocket.motorCount.Nomotor = \u0411\u0435\u0437 \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u0435\u0439 Rocket.compname.Rocket = \u0420\u0430\u043a\u0435\u0442\u0430 !MotorMount diff --git a/core/resources/pix/icons/zoom-reset.png b/core/resources/pix/icons/zoom-reset.png new file mode 100644 index 000000000..9d2c8ede5 Binary files /dev/null and b/core/resources/pix/icons/zoom-reset.png differ diff --git a/core/resources/pix/splashscreen.png b/core/resources/pix/splashscreen.png index c2767aa53..ab90ebda7 100644 Binary files a/core/resources/pix/splashscreen.png and b/core/resources/pix/splashscreen.png differ diff --git a/core/src/net/sf/openrocket/aerodynamics/FlightConditions.java b/core/src/net/sf/openrocket/aerodynamics/FlightConditions.java index 0b079e682..7d6a3e470 100644 --- a/core/src/net/sf/openrocket/aerodynamics/FlightConditions.java +++ b/core/src/net/sf/openrocket/aerodynamics/FlightConditions.java @@ -26,14 +26,14 @@ public class FlightConditions implements Cloneable, ChangeSource, Monitorable { private List listenerList = new ArrayList(); private EventObject event = new EventObject(this); - + /** Reference length used in calculations. */ private double refLength = 1.0; /** Reference area used in calculations. */ private double refArea = Math.PI * 0.25; - + /** Angle of attack. */ private double aoa = 0; @@ -59,7 +59,7 @@ public class FlightConditions implements Cloneable, ChangeSource, Monitorable { */ private double beta = MathUtil.safeSqrt(1 - mach * mach); - + /** Current roll rate. */ private double rollRate = 0; @@ -68,10 +68,10 @@ public class FlightConditions implements Cloneable, ChangeSource, Monitorable { private Coordinate pitchCenter = Coordinate.NUL; - + private AtmosphericConditions atmosphericConditions = new AtmosphericConditions(); - + private int modID; private int modIDadd = 0; @@ -326,8 +326,8 @@ public class FlightConditions implements Cloneable, ChangeSource, Monitorable { } - - + + /** * @return the pitchCenter */ @@ -445,12 +445,12 @@ public class FlightConditions implements Cloneable, ChangeSource, Monitorable { @Override - public void addChangeListener(EventListener listener) { + public void addChangeListener(StateChangeListener listener) { listenerList.add(0, listener); } @Override - public void removeChangeListener(EventListener listener) { + public void removeChangeListener(StateChangeListener listener) { listenerList.remove(listener); } @@ -459,8 +459,8 @@ public class FlightConditions implements Cloneable, ChangeSource, Monitorable { // Copy the list before iterating to prevent concurrent modification exceptions. EventListener[] listeners = listenerList.toArray(new EventListener[0]); for (EventListener l : listeners) { - if ( l instanceof StateChangeListener ) { - ((StateChangeListener)l).stateChanged(event); + if (l instanceof StateChangeListener) { + ((StateChangeListener) l).stateChanged(event); } } } diff --git a/core/src/net/sf/openrocket/appearance/Appearance.java b/core/src/net/sf/openrocket/appearance/Appearance.java new file mode 100644 index 000000000..385256bb6 --- /dev/null +++ b/core/src/net/sf/openrocket/appearance/Appearance.java @@ -0,0 +1,49 @@ +package net.sf.openrocket.appearance; + +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.MathUtil; + +/** + * A component appearance. + * This class is immutable. + * + * @author Bill Kuker + */ +public class Appearance { + public static final Appearance MISSING = new Appearance(new Color(0, 0, 0), 1, null); + + private final Color paint; + private final double shine; + private final Decal texture; + + public Appearance(final Color paint, final double shine, final Decal texture) { + this.paint = paint; + this.shine = MathUtil.clamp(shine, 0, 1); + this.texture = texture; + } + + public Appearance(final Color paint, final double shine) { + this.paint = paint; + this.shine = MathUtil.clamp(shine, 0, 1); + this.texture = null; + } + + public Color getPaint() { + return paint; + } + + public double getShine() { + return shine; + } + + public Decal getTexture() { + return texture; + } + + @Override + public String toString() { + return "Appearance [paint=" + paint + ", shine=" + + shine + ", texture=" + texture + "]"; + } + +} diff --git a/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java b/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java new file mode 100644 index 000000000..e65dfdd84 --- /dev/null +++ b/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java @@ -0,0 +1,235 @@ +package net.sf.openrocket.appearance; + +import net.sf.openrocket.appearance.Decal.EdgeMode; +import net.sf.openrocket.util.AbstractChangeSource; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.Coordinate; + +/** + * Use this class to build an immutable Appearance object in a friendly way. Set + * the various values one at a time with the setter methods and then call + * getAppearance(). Each call to getAppearance will return a new appearance. + * + * You can use this class repeatedly and resetToDefaults() in between, or create + * a new one every time. + * + * @author Bill Kuker + * + */ +public class AppearanceBuilder extends AbstractChangeSource { + + private Color paint; + private double shine; + private double offsetU, offsetV; + private double centerU, centerV; + private double scaleU, scaleV; + private double rotation; + private DecalImage image; + private Decal.EdgeMode edgeMode; + + private boolean batch; + + public AppearanceBuilder() { + resetToDefaults(); + } + + public AppearanceBuilder(Appearance a) { + setAppearance(a); + } + + private void resetToDefaults() { + paint = new Color(0, 0, 0); + shine = 0; + offsetU = offsetV = 0; + centerU = centerV = 0; + scaleU = scaleV = 1; + rotation = 0; + image = null; + edgeMode = EdgeMode.REPEAT; + } + + public void setAppearance(final Appearance a) { + batch(new Runnable() { + @Override + public void run() { + resetToDefaults(); + if (a != null) { + setPaint(a.getPaint()); + setShine(a.getShine()); + Decal d = a.getTexture(); + if (d != null) { + setOffset(d.getOffset().x, d.getOffset().y); + setCenter(d.getCenter().x, d.getCenter().y); + setScaleUV(d.getScale().x, d.getScale().y); + setRotation(d.getRotation()); + setEdgeMode(d.getEdgeMode()); + setImage(d.getImage()); + } + } + } + }); + } + + public Appearance getAppearance() { + + Decal t = null; + + if (image != null) { + t = new Decal( // + new Coordinate(offsetU, offsetV), // + new Coordinate(centerU, centerV), // + new Coordinate(scaleU, scaleV), // + rotation, // + image, // + edgeMode); + } + + return new Appearance(paint, shine, t); + } + + + + public Color getPaint() { + return paint; + } + + public void setPaint(Color paint) { + this.paint = paint; + fireChangeEvent(); + } + + public double getShine() { + return shine; + } + + public void setShine(double shine) { + this.shine = shine; + fireChangeEvent(); + } + + public double getOffsetU() { + return offsetU; + } + + public void setOffsetU(double offsetU) { + this.offsetU = offsetU; + fireChangeEvent(); + } + + public double getOffsetV() { + return offsetV; + } + + public void setOffsetV(double offsetV) { + this.offsetV = offsetV; + fireChangeEvent(); + } + + public void setOffset(double u, double v) { + setOffsetU(u); + setOffsetV(v); + } + + public double getCenterU() { + return centerU; + } + + public void setCenterU(double centerU) { + this.centerU = centerU; + fireChangeEvent(); + } + + public double getCenterV() { + return centerV; + } + + public void setCenterV(double centerV) { + this.centerV = centerV; + fireChangeEvent(); + } + + public void setCenter(double u, double v) { + setCenterU(u); + setCenterV(v); + } + + public double getScaleU() { + return scaleU; + } + + public void setScaleU(double scaleU) { + this.scaleU = scaleU; + fireChangeEvent(); + } + + public double getScaleV() { + return scaleV; + } + + public void setScaleV(double scaleV) { + this.scaleV = scaleV; + fireChangeEvent(); + } + + public void setScaleUV(double u, double v) { + setScaleU(u); + setScaleV(v); + } + + public double getScaleX() { + return 1.0 / getScaleU(); + } + + public void setScaleX(double scaleU) { + setScaleU(1.0 / scaleU); + } + + public double getScaleY() { + return 1.0 / getScaleV(); + } + + public void setScaleY(double scaleV) { + setScaleV(1.0 / scaleV); + } + + public double getRotation() { + return rotation; + } + + public void setRotation(double rotation) { + this.rotation = rotation; + fireChangeEvent(); + } + + public DecalImage getImage() { + return image; + } + + public void setImage(DecalImage image) { + this.image = image; + fireChangeEvent(); + } + + public Decal.EdgeMode getEdgeMode() { + return edgeMode; + } + + public void setEdgeMode(Decal.EdgeMode edgeMode) { + this.edgeMode = edgeMode; + fireChangeEvent(); + } + + @Override + protected void fireChangeEvent() { + if (!batch) + super.fireChangeEvent(); + } + + public void batch(Runnable r) { + batch = true; + r.run(); + batch = false; + fireChangeEvent(); + } + +} diff --git a/core/src/net/sf/openrocket/appearance/Decal.java b/core/src/net/sf/openrocket/appearance/Decal.java new file mode 100644 index 000000000..5e5461188 --- /dev/null +++ b/core/src/net/sf/openrocket/appearance/Decal.java @@ -0,0 +1,72 @@ +package net.sf.openrocket.appearance; + +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.Coordinate; + +/** + * A texture that can be applied by an Appearance. This class is immutable. + * + * @author Bill Kuker + */ +public class Decal { + + public static enum EdgeMode { + REPEAT("TextureWrap.Repeat"), MIRROR("TextureWrap.Mirror"), CLAMP("TextureWrap.Clamp"), STICKER("TextureWrap.Sticker"); + private final String transName; + + EdgeMode(final String name) { + this.transName = name; + } + + @Override + public String toString() { + return Application.getTranslator().get(transName); + } + } + + private final Coordinate offset, center, scale; + private final double rotation; + private final DecalImage image; + private final EdgeMode mode; + + public Decal(final Coordinate offset, final Coordinate center, final Coordinate scale, final double rotation, + final DecalImage image, final EdgeMode mode) { + this.offset = offset; + this.center = center; + this.scale = scale; + this.rotation = rotation; + this.image = image; + this.mode = mode; + } + + public Coordinate getOffset() { + return offset; + } + + public Coordinate getCenter() { + return center; + } + + public Coordinate getScale() { + return scale; + } + + public double getRotation() { + return rotation; + } + + public EdgeMode getEdgeMode() { + return mode; + } + + public DecalImage getImage() { + return image; + } + + @Override + public String toString() { + return "Texture [offset=" + offset + ", center=" + center + ", scale=" + scale + ", rotation=" + rotation + + ", image=" + image + "]"; + } + +} diff --git a/core/src/net/sf/openrocket/appearance/DecalImage.java b/core/src/net/sf/openrocket/appearance/DecalImage.java new file mode 100644 index 000000000..31aa9013a --- /dev/null +++ b/core/src/net/sf/openrocket/appearance/DecalImage.java @@ -0,0 +1,18 @@ +package net.sf.openrocket.appearance; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import net.sf.openrocket.util.ChangeSource; + +public interface DecalImage extends ChangeSource, Comparable { + + public String getName(); + + public InputStream getBytes() throws FileNotFoundException, IOException; + + public void exportImage(File file, boolean watchForChanges) throws IOException; + +} diff --git a/core/src/net/sf/openrocket/appearance/defaults/DefaultAppearance.java b/core/src/net/sf/openrocket/appearance/defaults/DefaultAppearance.java new file mode 100644 index 000000000..acb74b5d9 --- /dev/null +++ b/core/src/net/sf/openrocket/appearance/defaults/DefaultAppearance.java @@ -0,0 +1,111 @@ +package net.sf.openrocket.appearance.defaults; + +import java.util.HashMap; + +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; +import net.sf.openrocket.appearance.Decal.EdgeMode; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.motor.ThrustCurveMotor; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.EngineBlock; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.InnerTube; +import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.MassObject; +import net.sf.openrocket.rocketcomponent.Parachute; +import net.sf.openrocket.rocketcomponent.RadiusRingComponent; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.rocketcomponent.TubeCoupler; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.Coordinate; + +public class DefaultAppearance { + + private static Appearance simple(String resource) { + return new Appearance( + new Color(1, 1, 1), + 0, + new Decal( + new Coordinate(0, 0), + new Coordinate(0, 0), + new Coordinate(1, 1), + 0, + new ResourceDecalImage(resource), EdgeMode.REPEAT)); + }; + + private static Appearance simpleAlpha(Color base, float shine, String resource) { + return new Appearance( + base, + shine, + new Decal( + new Coordinate(0, 0), + new Coordinate(0, 0), + new Coordinate(1, 1), + 0, + new ResourceDecalImage(resource), EdgeMode.REPEAT)); + }; + + private static Appearance BALSA = simple("/datafiles/textures/balsa.png"); + private static Appearance WOOD = simple("/datafiles/textures/wood.png"); + @SuppressWarnings("unused") + private static Appearance CARDBOARD = simple("/datafiles/textures/cardboard.png"); + private static Appearance HARDBOARD = simple("/datafiles/textures/hardboard.png"); + private static Appearance WADDING = simple("/datafiles/textures/wadding.png"); + private static Appearance CHUTE = simple("/datafiles/textures/chute.png"); + + + private static final Appearance ESTES_BT = simpleAlpha(new Color(212, 185, 145), .3f, "/datafiles/textures/spiral-wound-alpha.png"); + private static final Appearance ESTES_IT = simpleAlpha(new Color(168, 146, 116), .1f, "/datafiles/textures/spiral-wound-alpha.png"); + private static final Appearance WHITE_BT = simpleAlpha(new Color(240, 240, 240), .3f, "/datafiles/textures/spiral-wound-alpha.png"); + + private static Appearance ESTES_MOTOR = simple("/datafiles/textures/motors/estes.png"); + private static Appearance AEROTECH_MOTOR = simple("/datafiles/textures/motors/aerotech.png"); + private static Appearance REUSABLE_MOTOR = simpleAlpha(new Color(195, 60, 50), .6f, "/datafiles/textures/motors/reusable.png"); + + private static HashMap plastics = new HashMap(); + + private static Appearance getPlastic(Color c) { + if (!plastics.containsKey(c)) { + plastics.put(c, new Appearance(c, .3)); + } + return plastics.get(c); + } + + public static Appearance getDefaultAppearance(RocketComponent c) { + if (c instanceof BodyTube) + return ESTES_BT; + if (c instanceof InnerTube || c instanceof TubeCoupler) + return ESTES_IT; + if (c instanceof FinSet) + return BALSA; + if (c instanceof LaunchLug) + return WHITE_BT; + if (c instanceof Transition) + return getPlastic(new Color(255, 255, 255)); + if (c instanceof RadiusRingComponent) + return WOOD; + if (c instanceof Parachute) + return CHUTE; + if (c instanceof EngineBlock) + return HARDBOARD; + if (c instanceof MassObject) + return WADDING; + + return Appearance.MISSING; + } + + public static Appearance getDefaultAppearance(Motor m) { + if (m instanceof ThrustCurveMotor) { + ThrustCurveMotor tcm = (ThrustCurveMotor) m; + if ("Estes".equals(tcm.getManufacturer().getSimpleName())) { + return ESTES_MOTOR; + } + if ("AeroTech".equals(tcm.getManufacturer().getSimpleName())) { + return AEROTECH_MOTOR; + } + } + return REUSABLE_MOTOR; + } +} diff --git a/core/src/net/sf/openrocket/appearance/defaults/ResourceDecalImage.java b/core/src/net/sf/openrocket/appearance/defaults/ResourceDecalImage.java new file mode 100644 index 000000000..20d2661ac --- /dev/null +++ b/core/src/net/sf/openrocket/appearance/defaults/ResourceDecalImage.java @@ -0,0 +1,55 @@ +package net.sf.openrocket.appearance.defaults; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import net.sf.openrocket.appearance.DecalImage; +import net.sf.openrocket.util.StateChangeListener; + + +class ResourceDecalImage implements DecalImage { + + final String resource; + + ResourceDecalImage(final String resource) { + this.resource = resource; + } + + @Override + public String toString() { + return getName(); + } + + @Override + public String getName() { + return resource; + } + + @Override + public InputStream getBytes() throws FileNotFoundException, IOException { + return this.getClass().getResourceAsStream(resource); + } + + @Override + public void exportImage(File file, boolean watchForChanges) throws IOException { + + } + + @Override + public void addChangeListener(StateChangeListener listener) { + //Unimplemented, this can not change + } + + @Override + public void removeChangeListener(StateChangeListener listener) { + //Unimplemented, this can not change + } + + @Override + public int compareTo(DecalImage o) { + return getName().compareTo(o.getName()); + } + +} diff --git a/core/src/net/sf/openrocket/document/Attachment.java b/core/src/net/sf/openrocket/document/Attachment.java new file mode 100644 index 000000000..6ac927495 --- /dev/null +++ b/core/src/net/sf/openrocket/document/Attachment.java @@ -0,0 +1,41 @@ +package net.sf.openrocket.document; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import net.sf.openrocket.util.AbstractChangeSource; +import net.sf.openrocket.util.ChangeSource; + +public abstract class Attachment extends AbstractChangeSource implements Comparable, ChangeSource { + + private final String name; + + public Attachment(String name) { + super(); + this.name = name; + } + + public String getName() { + return name; + } + + public abstract InputStream getBytes() throws FileNotFoundException, IOException; + + @Override + public int compareTo(Attachment o) { + return this.name.compareTo(o.name); + } + + @Override + public String toString() { + return getName(); + } + + @Override + public void fireChangeEvent() { + super.fireChangeEvent(); + } + + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/document/DecalRegistry.java b/core/src/net/sf/openrocket/document/DecalRegistry.java new file mode 100644 index 000000000..fca62654e --- /dev/null +++ b/core/src/net/sf/openrocket/document/DecalRegistry.java @@ -0,0 +1,299 @@ +package net.sf.openrocket.document; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.sf.openrocket.appearance.DecalImage; +import net.sf.openrocket.document.attachments.FileSystemAttachment; +import net.sf.openrocket.gui.watcher.FileWatcher; +import net.sf.openrocket.gui.watcher.WatchEvent; +import net.sf.openrocket.gui.watcher.WatchService; +import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.BugException; +import net.sf.openrocket.util.ChangeSource; +import net.sf.openrocket.util.FileUtils; +import net.sf.openrocket.util.StateChangeListener; + +public class DecalRegistry { + private static LogHelper log = Application.getLogger(); + + private WatchService watchService = Application.getWatchService(); + + DecalRegistry() { + } + + private Map registeredDecals = new HashMap(); + + public DecalImage makeUniqueImage(DecalImage original) { + + if (!(original instanceof DecalImageImpl)) { + return original; + } + + DecalImageImpl o = (DecalImageImpl) original; + + DecalImageImpl newDecal = o.clone(); + + String newName = makeUniqueName(o.getName()); + + newDecal.name = newName; + + registeredDecals.put(newName, newDecal); + + return newDecal; + + } + + public DecalImage getDecalImage(Attachment attachment) { + String decalName = attachment.getName(); + DecalImageImpl d; + if (attachment instanceof FileSystemAttachment) { + File location = ((FileSystemAttachment) attachment).getLocation(); + d = findDecalForFile(location); + if (d != null) { + return d; + } + + // It's a new file, generate a name for it. + decalName = makeUniqueName(location.getName()); + + d = new DecalImageImpl(decalName, attachment); + d.setFileSystemLocation(location); + + registeredDecals.put(decalName, d); + return d; + + } else { + d = registeredDecals.get(decalName); + if (d != null) { + return d; + } + } + d = new DecalImageImpl(attachment); + registeredDecals.put(decalName, d); + return d; + } + + public Collection getDecalList() { + + Set decals = new TreeSet(); + + decals.addAll(registeredDecals.values()); + + return decals; + } + + public class DecalImageImpl implements DecalImage, Cloneable, Comparable, ChangeSource { + + private final Attachment delegate; + + private String name; + private File fileSystemLocation; + + private DecalImageImpl(String name, Attachment delegate) { + this.name = name; + this.delegate = delegate; + } + + private DecalImageImpl(Attachment delegate) { + this.delegate = delegate; + } + + public String getName() { + return name != null ? name : delegate.getName(); + } + + /** + * This function returns an InputStream backed by a byte[] containing the decal pixels. + * If it reads in the bytes from an actual file, the underlying file is closed. + * + * @return InputStream containing byte[] of the image + * @throws FileNotFoundException + * @throws IOException + */ + public InputStream getBytes() throws FileNotFoundException, IOException { + // First check if the decal is located on the file system + File exportedFile = getFileSystemLocation(); + if (exportedFile != null) { + InputStream rawIs = new FileInputStream(exportedFile); + try { + byte[] bytes = FileUtils.readBytes(rawIs); + return new ByteArrayInputStream(bytes); + } finally { + rawIs.close(); + } + + } + + return delegate.getBytes(); + } + + @Override + public void exportImage(File file, boolean watchForChanges) throws IOException { + try { + InputStream is = getBytes(); + OutputStream os = new BufferedOutputStream(new FileOutputStream(file)); + + FileUtils.copy(is, os); + + is.close(); + os.close(); + + this.fileSystemLocation = file; + + if (watchForChanges) { + watchService.register(new FileWatcher(this.fileSystemLocation) { + + @Override + public void handleEvent(WatchEvent evt) { + DecalImageImpl.this.delegate.fireChangeEvent(); + //System.out.println(this.getFile() + " has changed"); + + } + + }); + } + + } catch (IOException iex) { + throw new BugException(iex); + } + } + + File getFileSystemLocation() { + return fileSystemLocation; + } + + void setFileSystemLocation(File fileSystemLocation) { + this.fileSystemLocation = fileSystemLocation; + } + + @Override + public String toString() { + return getName(); + } + + @Override + public int compareTo(DecalImage o) { + return getName().compareTo(o.getName()); + } + + @Override + protected DecalImageImpl clone() { + DecalImageImpl clone = new DecalImageImpl(this.delegate); + clone.fileSystemLocation = this.fileSystemLocation; + + return clone; + } + + @Override + public void addChangeListener(StateChangeListener listener) { + delegate.addChangeListener(listener); + } + + @Override + public void removeChangeListener(StateChangeListener listener) { + delegate.removeChangeListener(listener); + } + + + } + + private DecalImageImpl findDecalForFile(File file) { + + for (DecalImageImpl d : registeredDecals.values()) { + if (file.equals(d.getFileSystemLocation())) { + return d; + } + } + return null; + } + + /** + * Regular expression for parsing file names with numerical identifiers. + * For examples: + * + * decals/an image (3).png + * + * group(0) = "decals/an image (3).png" + * group(1) = "decals/an image" + * group(2) = " (3)" + * group(3) = "3" + * group(4) = "png" + * + * decals/an image.png + * + * group(0) = "decals/an image.png" + * group(1) = "decals/an image" + * group(2) = "null" + * group(3) = "null" + * group(4) = "png" + */ + private static final Pattern fileNamePattern = Pattern.compile("(.*?)( \\((\\d+)\\)+)?\\.(\\w*)"); + private static final int BASE_NAME_INDEX = 1; + private static final int NUMBER_INDEX = 3; + private static final int EXTENSION_INDEX = 4; + + private String makeUniqueName(String name) { + + String newName = name; + if (!newName.startsWith("decals/")) { + newName = "decals/" + name; + } + String basename = ""; + String extension = ""; + Matcher nameMatcher = fileNamePattern.matcher(newName); + if (nameMatcher.matches()) { + basename = nameMatcher.group(BASE_NAME_INDEX); + extension = nameMatcher.group(EXTENSION_INDEX); + } + + Set counts = new TreeSet(); + + boolean needsRewrite = false; + + for (DecalImageImpl d : registeredDecals.values()) { + Matcher m = fileNamePattern.matcher(d.getName()); + if (m.matches()) { + if (basename.equals(m.group(BASE_NAME_INDEX)) && extension.equals(m.group(EXTENSION_INDEX))) { + String intString = m.group(NUMBER_INDEX); + if (intString != null) { + Integer i = Integer.parseInt(intString); + counts.add(i); + } + needsRewrite = true; + } + } else if (newName.equals(d.getName())) { + needsRewrite = true; + } + } + + if (!needsRewrite) { + return newName; + } + + // find a missing integer; + Integer newIndex = 1; + while (counts.contains(newIndex)) { + newIndex++; + } + + return MessageFormat.format("{0} ({1}).{2}", basename, newIndex, extension); + } + +} diff --git a/core/src/net/sf/openrocket/document/OpenRocketDocument.java b/core/src/net/sf/openrocket/document/OpenRocketDocument.java index 282b6507f..12709d20a 100644 --- a/core/src/net/sf/openrocket/document/OpenRocketDocument.java +++ b/core/src/net/sf/openrocket/document/OpenRocketDocument.java @@ -1,12 +1,17 @@ package net.sf.openrocket.document; import java.io.File; +import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; +import net.sf.openrocket.appearance.DecalImage; import net.sf.openrocket.document.events.DocumentChangeEvent; import net.sf.openrocket.document.events.DocumentChangeListener; import net.sf.openrocket.document.events.SimulationChangeEvent; @@ -16,6 +21,7 @@ import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.simulation.listeners.SimulationListener; @@ -57,19 +63,18 @@ public class OpenRocketDocument implements ComponentChangeListener { private final ArrayList simulations = new ArrayList(); private ArrayList customExpressions = new ArrayList(); - - + /* * The undo/redo variables and mechanism are documented in doc/undo-redo-flow.* */ - + /** * The undo history of the rocket. Whenever a new undo position is created while the * rocket is in "dirty" state, the rocket is copied here. */ private LinkedList undoHistory = new LinkedList(); private LinkedList undoDescription = new LinkedList(); - + /** * The position in the undoHistory we are currently at. If modifications have been * made to the rocket, the rocket is in "dirty" state and this points to the previous @@ -83,7 +88,7 @@ public class OpenRocketDocument implements ComponentChangeListener { private String nextDescription = null; private String storedDescription = null; - + private ArrayList undoRedoListeners = new ArrayList(2); private File file = null; @@ -91,57 +96,54 @@ public class OpenRocketDocument implements ComponentChangeListener { private final StorageOptions storageOptions = new StorageOptions(); - - private final List listeners = - new ArrayList(); + private final DecalRegistry decalRegistry = new DecalRegistry(); - public OpenRocketDocument(Rocket rocket) { - this(rocket.getDefaultConfiguration()); + private final List listeners = new ArrayList(); + + OpenRocketDocument(Rocket rocket) { + this.configuration = rocket.getDefaultConfiguration(); + this.rocket = rocket; + init(); } - - private OpenRocketDocument(Configuration configuration) { - this.configuration = configuration; - this.rocket = configuration.getRocket(); - + private void init() { clearUndo(); rocket.addComponentChangeListener(this); } - - public void addCustomExpression(CustomExpression expression){ - if (customExpressions.contains(expression)){ - log.user("Could not add custom expression "+expression.getName()+" to document as document alerady has a matching expression."); + public void addCustomExpression(CustomExpression expression) { + if (customExpressions.contains(expression)) { + log.user("Could not add custom expression " + expression.getName() + " to document as document alerady has a matching expression."); } else { customExpressions.add(expression); } } - public void removeCustomExpression(CustomExpression expression){ + public void removeCustomExpression(CustomExpression expression) { customExpressions.remove(expression); } - public List getCustomExpressions(){ + public List getCustomExpressions() { return customExpressions; } /* * Returns a set of all the flight data types defined or available in any way in the rocket document */ - public Set getFlightDataTypes(){ + public Set getFlightDataTypes() { Set allTypes = new LinkedHashSet(); // built in Collections.addAll(allTypes, FlightDataType.ALL_TYPES); // custom expressions - for (CustomExpression exp : customExpressions){ + for (CustomExpression exp : customExpressions) { allTypes.add(exp.getType()); } // simulation listeners - for (Simulation sim : simulations){ + for (Simulation sim : simulations) { for (String className : sim.getSimulationListeners()) { SimulationListener l = null; try { @@ -153,7 +155,7 @@ public class OpenRocketDocument implements ComponentChangeListener { } catch (Exception e) { log.error("Could not instantiate listener: " + className); } - } + } } // imported data @@ -163,7 +165,7 @@ public class OpenRocketDocument implements ComponentChangeListener { return allTypes; } - + public Rocket getRocket() { return rocket; } @@ -173,7 +175,6 @@ public class OpenRocketDocument implements ComponentChangeListener { return configuration; } - public File getFile() { return file; } @@ -182,7 +183,6 @@ public class OpenRocketDocument implements ComponentChangeListener { this.file = file; } - public boolean isSaved() { return rocket.getModID() == savedID; } @@ -204,9 +204,41 @@ public class OpenRocketDocument implements ComponentChangeListener { } - - - + public Collection getDecalList() { + + return decalRegistry.getDecalList(); + + } + + public int countDecalUsage(DecalImage img) { + int count = 0; + + Iterator it = rocket.iterator(); + while (it.hasNext()) { + RocketComponent comp = it.next(); + Appearance a = comp.getAppearance(); + if (a == null) { + continue; + } + Decal d = a.getTexture(); + if (d == null) { + continue; + } + if (img.equals(d.getImage())) { + count++; + } + } + return count; + } + + public DecalImage makeUniqueDecal(DecalImage img) { + return decalRegistry.makeUniqueImage(img); + } + + public DecalImage getDecalImage(Attachment a) { + return decalRegistry.getDecalImage(a); + } + public List getSimulations() { return simulations.clone(); } @@ -312,14 +344,14 @@ public class OpenRocketDocument implements ComponentChangeListener { undoDescription.removeLast(); } - + // Add the current state to the undo history undoHistory.add(rocket.copyWithOriginalID()); undoDescription.add(null); nextDescription = description; undoPosition++; - + // Maintain maximum undo size if (undoHistory.size() > UNDO_LEVELS + UNDO_MARGIN && undoPosition > UNDO_MARGIN) { for (int i = 0; i < UNDO_MARGIN; i++) { @@ -376,7 +408,7 @@ public class OpenRocketDocument implements ComponentChangeListener { undoHistory.add(rocket.copyWithOriginalID()); undoDescription.add(null); undoPosition = 0; - + fireUndoRedoChangeEvent(); } @@ -531,18 +563,21 @@ public class OpenRocketDocument implements ComponentChangeListener { } - + /** * Return a copy of this document. The rocket is copied with original ID's, the default * motor configuration ID is maintained and the simulations are copied to the new rocket. * No undo/redo information or file storage information is maintained. * + * This function is used from the Optimization routine to store alternatives of the same rocket. + * For now we can assume that the copy returned does not have any of the attachment factories in place. + * * @return a copy of this document. */ public OpenRocketDocument copy() { Rocket rocketCopy = rocket.copyWithOriginalID(); - OpenRocketDocument documentCopy = new OpenRocketDocument(rocketCopy); - documentCopy.getDefaultConfiguration().setMotorConfigurationID(configuration.getMotorConfigurationID()); + OpenRocketDocument documentCopy = OpenRocketDocumentFactory.createDocumentFromRocket(rocketCopy); + documentCopy.getDefaultConfiguration().setFlightConfigurationID(configuration.getFlightConfigurationID()); for (Simulation s : simulations) { documentCopy.addSimulation(s.duplicateSimulation(rocketCopy)); } @@ -550,14 +585,14 @@ public class OpenRocketDocument implements ComponentChangeListener { } - + /////// Listeners - public void addUndoRedoListener( UndoRedoListener listener ) { + public void addUndoRedoListener(UndoRedoListener listener) { undoRedoListeners.add(listener); } - public void removeUndoRedoListener( UndoRedoListener listener ) { + public void removeUndoRedoListener(UndoRedoListener listener) { undoRedoListeners.remove(listener); } @@ -585,6 +620,6 @@ public class OpenRocketDocument implements ComponentChangeListener { } - - + + } diff --git a/core/src/net/sf/openrocket/document/OpenRocketDocumentFactory.java b/core/src/net/sf/openrocket/document/OpenRocketDocumentFactory.java new file mode 100644 index 000000000..e649a06d0 --- /dev/null +++ b/core/src/net/sf/openrocket/document/OpenRocketDocumentFactory.java @@ -0,0 +1,34 @@ +package net.sf.openrocket.document; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.startup.Application; + +public class OpenRocketDocumentFactory { + + private static final Translator trans = Application.getTranslator(); + + public static OpenRocketDocument createNewRocket() { + Rocket rocket = new Rocket(); + Stage stage = new Stage(); + //// Sustainer + stage.setName(trans.get("BasicFrame.StageName.Sustainer")); + rocket.addChild(stage); + OpenRocketDocument doc = new OpenRocketDocument(rocket); + doc.setSaved(true); + return doc; + } + + public static OpenRocketDocument createDocumentFromRocket(Rocket r) { + OpenRocketDocument doc = new OpenRocketDocument(r); + return doc; + } + + public static OpenRocketDocument createEmptyRocket() { + Rocket rocket = new Rocket(); + OpenRocketDocument doc = new OpenRocketDocument(rocket); + return doc; + } + +} diff --git a/core/src/net/sf/openrocket/document/Simulation.java b/core/src/net/sf/openrocket/document/Simulation.java index f7e601cf7..54d32fa7a 100644 --- a/core/src/net/sf/openrocket/document/Simulation.java +++ b/core/src/net/sf/openrocket/document/Simulation.java @@ -7,6 +7,7 @@ import java.util.List; import net.sf.openrocket.aerodynamics.AerodynamicCalculator; import net.sf.openrocket.aerodynamics.BarrowmanCalculator; import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.formatting.RocketDescriptor; import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.masscalc.MassCalculator; @@ -39,55 +40,58 @@ import net.sf.openrocket.util.StateChangeListener; */ public class Simulation implements ChangeSource, Cloneable { private static final LogHelper log = Application.getLogger(); - + public static enum Status { /** Up-to-date */ UPTODATE, - + /** Loaded from file, status probably up-to-date */ LOADED, - + /** Data outdated */ OUTDATED, - + /** Imported external data */ EXTERNAL, - + /** Not yet simulated */ NOT_SIMULATED } - + + private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); + + private SafetyMutex mutex = SafetyMutex.newInstance(); - + private final Rocket rocket; - + private String name = ""; - + private Status status = Status.NOT_SIMULATED; - + /** The conditions to use */ // TODO: HIGH: Change to use actual conditions class?? private SimulationOptions options; - + private ArrayList simulationListeners = new ArrayList(); - + private final Class simulationEngineClass = BasicEventSimulationEngine.class; private Class simulationStepperClass = RK4SimulationStepper.class; private Class aerodynamicCalculatorClass = BarrowmanCalculator.class; @SuppressWarnings("unused") private Class massCalculatorClass = BasicMassCalculator.class; - + /** Listeners for this object */ private List listeners = new ArrayList(); - - + + /** The conditions actually used in the previous simulation, or null */ private SimulationOptions simulatedConditions = null; - private String simulatedMotors = null; + private String simulatedConfiguration = null; private FlightData simulatedData = null; private int simulatedRocketID = -1; - - + + /** * Create a new simulation for the rocket. Parent document should also be provided. * The initial motor configuration is taken from the default rocket configuration. @@ -97,17 +101,17 @@ public class Simulation implements ChangeSource, Cloneable { public Simulation(Rocket rocket) { this.rocket = rocket; this.status = Status.NOT_SIMULATED; - + options = new SimulationOptions(rocket); options.setMotorConfigurationID( - rocket.getDefaultConfiguration().getMotorConfigurationID()); + rocket.getDefaultConfiguration().getFlightConfigurationID()); options.addChangeListener(new ConditionListener()); } - - + + public Simulation(Rocket rocket, Status status, String name, SimulationOptions options, List listeners, FlightData data) { - + if (rocket == null) throw new IllegalArgumentException("rocket cannot be null"); if (status == null) @@ -116,9 +120,9 @@ public class Simulation implements ChangeSource, Cloneable { throw new IllegalArgumentException("name cannot be null"); if (options == null) throw new IllegalArgumentException("options cannot be null"); - + this.rocket = rocket; - + if (status == Status.UPTODATE) { this.status = Status.LOADED; } else if (data == null) { @@ -126,17 +130,17 @@ public class Simulation implements ChangeSource, Cloneable { } else { this.status = status; } - + this.name = name; - + this.options = options; options.addChangeListener(new ConditionListener()); - + if (listeners != null) { this.simulationListeners.addAll(listeners); } - - + + if (data != null && this.status != Status.NOT_SIMULATED) { simulatedData = data; if (this.status == Status.LOADED) { @@ -144,9 +148,9 @@ public class Simulation implements ChangeSource, Cloneable { simulatedRocketID = rocket.getModID(); } } - + } - + /** * Return the rocket associated with this simulation. * @@ -156,8 +160,8 @@ public class Simulation implements ChangeSource, Cloneable { mutex.verify(); return rocket; } - - + + /** * Return a newly created Configuration for this simulation. The configuration * has the motor ID set and all stages active. @@ -167,11 +171,11 @@ public class Simulation implements ChangeSource, Cloneable { public Configuration getConfiguration() { mutex.verify(); Configuration c = new Configuration(rocket); - c.setMotorConfigurationID(options.getMotorConfigurationID()); + c.setFlightConfigurationID(options.getMotorConfigurationID()); c.setAllStages(); return c; } - + /** * Returns the simulation options attached to this simulation. The options * may be modified freely, and the status of the simulation will change to reflect @@ -183,8 +187,8 @@ public class Simulation implements ChangeSource, Cloneable { mutex.verify(); return options; } - - + + /** * Get the list of simulation listeners. The returned list is the one used by * this object; changes to it will reflect changes in the simulation. @@ -195,8 +199,8 @@ public class Simulation implements ChangeSource, Cloneable { mutex.verify(); return simulationListeners; } - - + + /** * Return the user-defined name of the simulation. * @@ -206,7 +210,7 @@ public class Simulation implements ChangeSource, Cloneable { mutex.verify(); return name; } - + /** * Set the user-defined name of the simulation. Setting the name to * null yields an empty name. @@ -218,19 +222,19 @@ public class Simulation implements ChangeSource, Cloneable { try { if (this.name.equals(name)) return; - + if (name == null) this.name = ""; else this.name = name; - + fireChangeEvent(); } finally { mutex.unlock("setName"); } } - - + + /** * Returns the status of this simulation. This method examines whether the * simulation has been outdated and returns {@link Status#OUTDATED} accordingly. @@ -240,18 +244,18 @@ public class Simulation implements ChangeSource, Cloneable { */ public Status getStatus() { mutex.verify(); - + if (status == Status.UPTODATE || status == Status.LOADED) { if (rocket.getFunctionalModID() != simulatedRocketID || !options.equals(simulatedConditions)) return Status.OUTDATED; } - + return status; } - - - + + + /** * Simulate the flight. * @@ -259,16 +263,16 @@ public class Simulation implements ChangeSource, Cloneable { * @throws SimulationException if a problem occurs during simulation */ public void simulate(SimulationListener... additionalListeners) - throws SimulationException { + throws SimulationException { mutex.lock("simulate"); try { - + if (this.status == Status.EXTERNAL) { throw new SimulationException("Cannot simulate imported simulation."); } - + SimulationEngine simulator; - + try { simulator = simulationEngineClass.newInstance(); } catch (InstantiationException e) { @@ -276,13 +280,13 @@ public class Simulation implements ChangeSource, Cloneable { } catch (IllegalAccessException e) { throw new IllegalStateException("Cannot access simulator instance?! BUG!", e); } - + SimulationConditions simulationConditions = options.toSimulationConditions(); simulationConditions.setSimulation(this); for (SimulationListener l : additionalListeners) { simulationConditions.getSimulationListenerList().add(l); } - + for (String className : simulationListeners) { SimulationListener l = null; try { @@ -294,29 +298,30 @@ public class Simulation implements ChangeSource, Cloneable { } simulationConditions.getSimulationListenerList().add(l); } - + long t1, t2; log.debug("Simulation: calling simulator"); t1 = System.currentTimeMillis(); simulatedData = simulator.simulate(simulationConditions); t2 = System.currentTimeMillis(); log.debug("Simulation: returning from simulator, simulation took " + (t2 - t1) + "ms"); - + // Set simulated info after simulation, will not be set in case of exception simulatedConditions = options.clone(); - final Configuration configuration = getConfiguration(); - simulatedMotors = configuration.getMotorConfigurationDescription(); + final Configuration configuration = getConfiguration(); + + simulatedConfiguration = descriptor.format(configuration.getRocket(), configuration.getFlightConfigurationID()); simulatedRocketID = rocket.getFunctionalModID(); - + status = Status.UPTODATE; fireChangeEvent(); - configuration.release(); + configuration.release(); } finally { mutex.unlock("simulate"); } } - - + + /** * Return the conditions used in the previous simulation, or null * if this simulation has not been run. @@ -327,7 +332,7 @@ public class Simulation implements ChangeSource, Cloneable { mutex.verify(); return simulatedConditions; } - + /** * Return the warnings generated in the previous simulation, or * null if this simulation has not been run. This is the same @@ -342,21 +347,20 @@ public class Simulation implements ChangeSource, Cloneable { return null; return simulatedData.getWarningSet(); } - - + + /** * Return a string describing the motor configuration of the previous simulation, * or null if this simulation has not been run. * * @return a description of the motor configuration of the previous simulation, or * null. - * @see Rocket#getMotorConfigurationNameOrDescription(String) */ - public String getSimulatedMotorDescription() { + public String getSimulatedConfigurationDescription() { mutex.verify(); - return simulatedMotors; + return simulatedConfiguration; } - + /** * Return the flight data of the previous simulation, or null if * this simulation has not been run. @@ -367,9 +371,9 @@ public class Simulation implements ChangeSource, Cloneable { mutex.verify(); return simulatedData; } - - - + + + /** * Returns a copy of this simulation suitable for cut/copy/paste operations. * The rocket refers to the same instance as the original simulation. @@ -380,29 +384,29 @@ public class Simulation implements ChangeSource, Cloneable { public Simulation copy() { mutex.lock("copy"); try { - + Simulation copy = (Simulation) super.clone(); - + copy.mutex = SafetyMutex.newInstance(); copy.status = Status.NOT_SIMULATED; copy.options = this.options.clone(); copy.simulationListeners = this.simulationListeners.clone(); copy.listeners = new ArrayList(); copy.simulatedConditions = null; - copy.simulatedMotors = null; + copy.simulatedConfiguration = null; copy.simulatedData = null; copy.simulatedRocketID = -1; - + return copy; - + } catch (CloneNotSupportedException e) { throw new BugException("Clone not supported, BUG", e); } finally { mutex.unlock("copy"); } } - - + + /** * Create a duplicate of this simulation with the specified rocket. The new * simulation is in non-simulated state. @@ -414,51 +418,52 @@ public class Simulation implements ChangeSource, Cloneable { mutex.lock("duplicateSimulation"); try { Simulation copy = new Simulation(newRocket); - + copy.name = this.name; copy.options.copyFrom(this.options); + copy.simulatedConfiguration = this.simulatedConfiguration; copy.simulationListeners = this.simulationListeners.clone(); copy.simulationStepperClass = this.simulationStepperClass; copy.aerodynamicCalculatorClass = this.aerodynamicCalculatorClass; - + return copy; } finally { mutex.unlock("duplicateSimulation"); } } - - - + + + @Override - public void addChangeListener(EventListener listener) { + public void addChangeListener(StateChangeListener listener) { mutex.verify(); listeners.add(listener); } - + @Override - public void removeChangeListener(EventListener listener) { + public void removeChangeListener(StateChangeListener listener) { mutex.verify(); listeners.remove(listener); } - + protected void fireChangeEvent() { EventObject e = new EventObject(this); // Copy the list before iterating to prevent concurrent modification exceptions. EventListener[] ls = listeners.toArray(new EventListener[0]); for (EventListener l : ls) { - if ( l instanceof StateChangeListener ) { - ((StateChangeListener)l).stateChanged(e); + if (l instanceof StateChangeListener) { + ((StateChangeListener) l).stateChanged(e); } } } - - - - + + + + private class ConditionListener implements StateChangeListener { - + private Status oldStatus = null; - + @Override public void stateChanged(EventObject e) { if (getStatus() != oldStatus) { diff --git a/core/src/net/sf/openrocket/document/StorageOptions.java b/core/src/net/sf/openrocket/document/StorageOptions.java index 7e1186c1e..4618aa5d2 100644 --- a/core/src/net/sf/openrocket/document/StorageOptions.java +++ b/core/src/net/sf/openrocket/document/StorageOptions.java @@ -4,22 +4,26 @@ import net.sf.openrocket.util.BugException; public class StorageOptions implements Cloneable { + public enum FileType { + OPENROCKET, + ROCKSIM + } + public static final double SIMULATION_DATA_NONE = Double.POSITIVE_INFINITY; public static final double SIMULATION_DATA_ALL = 0; - private boolean compressionEnabled = true; + private FileType fileType = FileType.OPENROCKET; private double simulationTimeSkip = SIMULATION_DATA_NONE; private boolean explicitlySet = false; - - public boolean isCompressionEnabled() { - return compressionEnabled; + public FileType getFileType() { + return fileType; } - public void setCompressionEnabled(boolean compression) { - this.compressionEnabled = compression; + public void setFileType(FileType fileType) { + this.fileType = fileType; } public double getSimulationTimeSkip() { @@ -30,8 +34,6 @@ public class StorageOptions implements Cloneable { this.simulationTimeSkip = simulationTimeSkip; } - - public boolean isExplicitlySet() { return explicitlySet; } @@ -40,8 +42,6 @@ public class StorageOptions implements Cloneable { this.explicitlySet = explicitlySet; } - - @Override public StorageOptions clone() { try { diff --git a/core/src/net/sf/openrocket/document/attachments/FileSystemAttachment.java b/core/src/net/sf/openrocket/document/attachments/FileSystemAttachment.java new file mode 100644 index 000000000..28d737e44 --- /dev/null +++ b/core/src/net/sf/openrocket/document/attachments/FileSystemAttachment.java @@ -0,0 +1,29 @@ +package net.sf.openrocket.document.attachments; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import net.sf.openrocket.document.Attachment; + +public class FileSystemAttachment extends Attachment { + + private final File location; + + public FileSystemAttachment(String name, File location) { + super(name); + this.location = location; + } + + public File getLocation() { + return location; + } + + @Override + public InputStream getBytes() throws FileNotFoundException, IOException { + return new FileInputStream(location); + } + +} diff --git a/core/src/net/sf/openrocket/document/attachments/ZipFileAttachment.java b/core/src/net/sf/openrocket/document/attachments/ZipFileAttachment.java new file mode 100644 index 000000000..27d7d6423 --- /dev/null +++ b/core/src/net/sf/openrocket/document/attachments/ZipFileAttachment.java @@ -0,0 +1,45 @@ +package net.sf.openrocket.document.attachments; + +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import net.sf.openrocket.document.Attachment; +import net.sf.openrocket.util.FileUtils; + +public class ZipFileAttachment extends Attachment { + + private final URL zipFileLocation; + + public ZipFileAttachment(String name, URL zipFileLocation) { + super(name); + this.zipFileLocation = zipFileLocation; + } + + @Override + public InputStream getBytes() throws FileNotFoundException, IOException { + String name = getName(); + + ZipInputStream zis = new ZipInputStream(zipFileLocation.openStream()); + + try { + ZipEntry entry = zis.getNextEntry(); + while (entry != null) { + if (entry.getName().equals(name)) { + byte[] bytes = FileUtils.readBytes(zis); + return new ByteArrayInputStream(bytes); + } + entry = zis.getNextEntry(); + } + throw new FileNotFoundException("Unable to locate decal for name " + name); + } finally { + zis.close(); + } + + } + +} diff --git a/core/src/net/sf/openrocket/file/AbstractRocketLoader.java b/core/src/net/sf/openrocket/file/AbstractRocketLoader.java index f0c7464df..0c0a0a222 100644 --- a/core/src/net/sf/openrocket/file/AbstractRocketLoader.java +++ b/core/src/net/sf/openrocket/file/AbstractRocketLoader.java @@ -1,55 +1,24 @@ package net.sf.openrocket.file; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import net.sf.openrocket.aerodynamics.WarningSet; -import net.sf.openrocket.document.OpenRocketDocument; public abstract class AbstractRocketLoader implements RocketLoader { protected final WarningSet warnings = new WarningSet(); - /** - * Loads a rocket from the specified File object. - */ - @Override - public final OpenRocketDocument load(File source, MotorFinder motorFinder) throws RocketLoadException { - warnings.clear(); - InputStream stream = null; - - try { - - stream = new BufferedInputStream(new FileInputStream(source)); - return load(stream, motorFinder); - - } catch (FileNotFoundException e) { - throw new RocketLoadException("File not found: " + source); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - /** * Loads a rocket from the specified InputStream. */ @Override - public final OpenRocketDocument load(InputStream source, MotorFinder motorFinder) throws RocketLoadException { + public final void load(DocumentLoadingContext context, InputStream source) throws RocketLoadException { warnings.clear(); try { - return loadFromStream(source, motorFinder); + loadFromStream(context, source); } catch (RocketLoadException e) { throw e; } catch (IOException e) { @@ -65,8 +34,7 @@ public abstract class AbstractRocketLoader implements RocketLoader { * * @throws RocketLoadException if an error occurs during loading. */ - protected abstract OpenRocketDocument loadFromStream(InputStream source, MotorFinder motorFinder) throws IOException, - RocketLoadException; + protected abstract void loadFromStream(DocumentLoadingContext context, InputStream source) throws IOException, RocketLoadException; diff --git a/core/src/net/sf/openrocket/file/AttachmentFactory.java b/core/src/net/sf/openrocket/file/AttachmentFactory.java new file mode 100644 index 000000000..74377f30f --- /dev/null +++ b/core/src/net/sf/openrocket/file/AttachmentFactory.java @@ -0,0 +1,9 @@ +package net.sf.openrocket.file; + +import net.sf.openrocket.document.Attachment; + +public interface AttachmentFactory { + + public Attachment getAttachment(String name); + +} diff --git a/core/src/net/sf/openrocket/file/AttachmentUtils.java b/core/src/net/sf/openrocket/file/AttachmentUtils.java new file mode 100644 index 000000000..5d76d2036 --- /dev/null +++ b/core/src/net/sf/openrocket/file/AttachmentUtils.java @@ -0,0 +1,25 @@ +package net.sf.openrocket.file; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import net.sf.openrocket.document.Attachment; +import net.sf.openrocket.util.FileUtils; + +public abstract class AttachmentUtils { + + public static void exportAttachment(Attachment a, File outFile) throws IOException { + InputStream is = a.getBytes(); + OutputStream os = new BufferedOutputStream(new FileOutputStream(outFile)); + + FileUtils.copy(is, os); + + is.close(); + os.close(); + + } +} diff --git a/core/src/net/sf/openrocket/file/DocumentLoadingContext.java b/core/src/net/sf/openrocket/file/DocumentLoadingContext.java new file mode 100644 index 000000000..cd1812805 --- /dev/null +++ b/core/src/net/sf/openrocket/file/DocumentLoadingContext.java @@ -0,0 +1,44 @@ +package net.sf.openrocket.file; + +import net.sf.openrocket.document.OpenRocketDocument; + +public class DocumentLoadingContext { + + private int fileVersion; + private MotorFinder motorFinder; + private AttachmentFactory attachmentFactory = new FileSystemAttachmentFactory(); + private OpenRocketDocument document; + + public int getFileVersion() { + return fileVersion; + } + + public void setFileVersion(int fileVersion) { + this.fileVersion = fileVersion; + } + + public MotorFinder getMotorFinder() { + return motorFinder; + } + + public void setMotorFinder(MotorFinder motorFinder) { + this.motorFinder = motorFinder; + } + + public OpenRocketDocument getOpenRocketDocument() { + return document; + } + + public void setOpenRocketDocument(OpenRocketDocument document) { + this.document = document; + } + + public AttachmentFactory getAttachmentFactory() { + return attachmentFactory; + } + + public void setAttachmentFactory(AttachmentFactory attachmentFactory) { + this.attachmentFactory = attachmentFactory; + } + +} diff --git a/core/src/net/sf/openrocket/file/FileSystemAttachmentFactory.java b/core/src/net/sf/openrocket/file/FileSystemAttachmentFactory.java new file mode 100644 index 000000000..27e85868d --- /dev/null +++ b/core/src/net/sf/openrocket/file/FileSystemAttachmentFactory.java @@ -0,0 +1,45 @@ +package net.sf.openrocket.file; + +import java.io.File; + +import net.sf.openrocket.document.Attachment; +import net.sf.openrocket.document.attachments.FileSystemAttachment; + +public class FileSystemAttachmentFactory implements AttachmentFactory { + + private final File baseDirectory; + + public FileSystemAttachmentFactory() { + super(); + this.baseDirectory = null; + } + + public FileSystemAttachmentFactory(File baseDirectory) { + super(); + if (baseDirectory != null && baseDirectory.isDirectory() == false) { + throw new IllegalArgumentException("Base file for FileSystemAttachmentFactory is not a directory"); + } + this.baseDirectory = baseDirectory; + } + + public Attachment getAttachment(File file) { + return new FileSystemAttachment(file.getName(), file); + } + + @Override + public Attachment getAttachment(String name) { + + File file = new File(name); + + if (file.isAbsolute()) { + return new FileSystemAttachment(name, file); + } + + else { + file = new File(baseDirectory, name); + return new FileSystemAttachment(name, file); + } + + } + +} diff --git a/core/src/net/sf/openrocket/file/GeneralRocketLoader.java b/core/src/net/sf/openrocket/file/GeneralRocketLoader.java index da6858d89..c6f3b527a 100644 --- a/core/src/net/sf/openrocket/file/GeneralRocketLoader.java +++ b/core/src/net/sf/openrocket/file/GeneralRocketLoader.java @@ -1,15 +1,20 @@ package net.sf.openrocket.file; import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.Charset; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Arrays; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.document.OpenRocketDocumentFactory; import net.sf.openrocket.file.openrocket.importt.OpenRocketLoader; import net.sf.openrocket.file.rocksim.importt.RocksimLoader; import net.sf.openrocket.util.ArrayUtils; @@ -23,22 +28,94 @@ import net.sf.openrocket.util.TextUtil; * * @author Sampo Niskanen */ -public class GeneralRocketLoader extends AbstractRocketLoader { +public class GeneralRocketLoader { + + protected final WarningSet warnings = new WarningSet(); private static final int READ_BYTES = 300; private static final byte[] GZIP_SIGNATURE = { 31, -117 }; // 0x1f, 0x8b - private static final byte[] ZIP_SIGNATURE = TextUtil.convertStringToBytes("PK",Charset.forName("US-ASCII")); - private static final byte[] OPENROCKET_SIGNATURE = TextUtil.convertStringToBytes(" usedDecals = new TreeSet(); + + // Look for all decals used in the rocket. + for (RocketComponent c : document.getRocket()) { + if (c.getAppearance() == null) { + continue; + } + Appearance ap = c.getAppearance(); + if (ap.getTexture() == null) { + continue; + } + + Decal decal = ap.getTexture(); + + usedDecals.add(decal.getImage()); + } + + saveAllPartsZipFile(output, document, options, usedDecals); + } + + public void saveAllPartsZipFile(OutputStream output, OpenRocketDocument document, StorageOptions options, Set decals) throws IOException { + + // Open a zip stream to write to. + ZipOutputStream zos = new ZipOutputStream(output); + zos.setLevel(9); + // big try block to close the zos. + try { + + + ZipEntry mainFile = new ZipEntry("rocket.ork"); + zos.putNextEntry(mainFile); + saveInternal(zos, document, options); + zos.closeEntry(); + + // Now we write out all the decal images files. + + for (DecalImage image : decals) { + + String name = image.getName(); + ZipEntry decal = new ZipEntry(name); + zos.putNextEntry(decal); + + InputStream is = image.getBytes(); + int bytesRead = 0; + byte[] buffer = new byte[2048]; + while ((bytesRead = is.read(buffer)) > 0) { + zos.write(buffer, 0, bytesRead); + } + zos.closeEntry(); + } + + zos.flush(); + } finally { + zos.close(); + } + + + } + + // package scope for testing. + + private void saveInternal(OutputStream output, OpenRocketDocument document, StorageOptions options) + throws IOException { + + if (options.getFileType() == StorageOptions.FileType.ROCKSIM) { + new RocksimSaver().save(output, document, options); + } else { + new OpenRocketSaver().save(output, document, options); + } + } + + private static class ProgressOutputStream extends FilterOutputStream { + + private long estimatedSize; + private long bytesWritten = 0; + private SavingProgress progressCallback; + + ProgressOutputStream(OutputStream ostream, long estimatedSize, SavingProgress progressCallback) { + super(ostream); + this.estimatedSize = estimatedSize; + this.progressCallback = progressCallback; + } + + @Override + public void write(int b) throws IOException { + super.write(b); + bytesWritten++; + updateProgress(); + } + + @Override + public void write(byte[] b) throws IOException { + super.write(b); + bytesWritten += b.length; + updateProgress(); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + super.write(b, off, len); + bytesWritten += len; + updateProgress(); + } + + private void updateProgress() { + if (progressCallback != null) { + int p = 50; + if (estimatedSize > 0) { + p = (int) Math.floor(bytesWritten * 100.0 / estimatedSize); + p = MathUtil.clamp(p, 0, 100); + } + progressCallback.setProgress(p); + } + } + + } +} diff --git a/core/src/net/sf/openrocket/file/RocketLoader.java b/core/src/net/sf/openrocket/file/RocketLoader.java index 81bcb0ac8..37e73dc51 100644 --- a/core/src/net/sf/openrocket/file/RocketLoader.java +++ b/core/src/net/sf/openrocket/file/RocketLoader.java @@ -1,16 +1,12 @@ package net.sf.openrocket.file; -import java.io.File; import java.io.InputStream; import net.sf.openrocket.aerodynamics.WarningSet; -import net.sf.openrocket.document.OpenRocketDocument; public interface RocketLoader { - public OpenRocketDocument load(File source, MotorFinder motorFinder) throws RocketLoadException; - - public OpenRocketDocument load(InputStream source, MotorFinder motorFinder) throws RocketLoadException; + public void load(DocumentLoadingContext context, InputStream source) throws RocketLoadException; public WarningSet getWarnings(); diff --git a/core/src/net/sf/openrocket/file/RocketSaver.java b/core/src/net/sf/openrocket/file/RocketSaver.java index 5c18bb8de..b731c7f75 100644 --- a/core/src/net/sf/openrocket/file/RocketSaver.java +++ b/core/src/net/sf/openrocket/file/RocketSaver.java @@ -1,8 +1,5 @@ package net.sf.openrocket.file; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -12,37 +9,6 @@ import net.sf.openrocket.document.StorageOptions; public abstract class RocketSaver { - /** - * Save the document to the specified file using the default storage options. - * - * @param dest the destination file. - * @param document the document to save. - * @throws IOException in case of an I/O error. - */ - public final void save(File dest, OpenRocketDocument document) throws IOException { - save(dest, document, document.getDefaultStorageOptions()); - } - - - /** - * Save the document to the specified file using the given storage options. - * - * @param dest the destination file. - * @param document the document to save. - * @param options the storage options. - * @throws IOException in case of an I/O error. - */ - public void save(File dest, OpenRocketDocument document, StorageOptions options) - throws IOException { - OutputStream s = new BufferedOutputStream(new FileOutputStream(dest)); - try { - save(s, document, options); - } finally { - s.close(); - } - } - - /** * Save the document to the specified output stream using the default storage options. * @@ -53,8 +19,7 @@ public abstract class RocketSaver { public final void save(OutputStream dest, OpenRocketDocument doc) throws IOException { save(dest, doc, doc.getDefaultStorageOptions()); } - - + /** * Save the document to the specified output stream using the given storage options. * @@ -63,10 +28,7 @@ public abstract class RocketSaver { * @param options the storage options. * @throws IOException in case of an I/O error. */ - public abstract void save(OutputStream dest, OpenRocketDocument doc, - StorageOptions options) throws IOException; - - + public abstract void save(OutputStream dest, OpenRocketDocument doc, StorageOptions options) throws IOException; /** * Provide an estimate of the file size when saving the document with the @@ -78,25 +40,4 @@ public abstract class RocketSaver { * @return the estimated number of bytes the storage would take. */ public abstract long estimateFileSize(OpenRocketDocument doc, StorageOptions options); - - - - - public static String escapeXML(String s) { - - s = s.replace("&", "&"); - s = s.replace("<", "<"); - s = s.replace(">", ">"); - s = s.replace("\"","""); - s = s.replace("'", "'"); - - for (int i=0; i < s.length(); i++) { - char n = s.charAt(i); - if (((n < 32) && (n != 9) && (n != 10) && (n != 13)) || (n == 127)) { - s = s.substring(0,i) + "&#" + ((int)n) + ";" + s.substring(i+1); - } - } - - return s; - } } diff --git a/core/src/net/sf/openrocket/file/ZipFileAttachmentFactory.java b/core/src/net/sf/openrocket/file/ZipFileAttachmentFactory.java new file mode 100644 index 000000000..f58a331f3 --- /dev/null +++ b/core/src/net/sf/openrocket/file/ZipFileAttachmentFactory.java @@ -0,0 +1,21 @@ +package net.sf.openrocket.file; + +import java.net.URL; + +import net.sf.openrocket.document.Attachment; +import net.sf.openrocket.document.attachments.ZipFileAttachment; + +public class ZipFileAttachmentFactory implements AttachmentFactory { + + private final URL zipFile; + + public ZipFileAttachmentFactory(URL zipFile) { + super(); + this.zipFile = zipFile; + } + + @Override + public Attachment getAttachment(String name) { + return new ZipFileAttachment(name, zipFile); + } +} diff --git a/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java b/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java index 0a5e04790..f066ef31c 100644 --- a/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale; -import java.util.zip.GZIPOutputStream; import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.document.OpenRocketDocument; @@ -17,12 +16,14 @@ import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.StorageOptions; import net.sf.openrocket.file.RocketSaver; import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.FlightConfigurableComponent; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.RecoveryDevice; -import net.sf.openrocket.rocketcomponent.RecoveryDevice.DeployEvent; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Stage; import net.sf.openrocket.rocketcomponent.TubeCoupler; import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.FlightDataBranch; @@ -57,11 +58,8 @@ public class OpenRocketSaver extends RocketSaver { // Estimated storage used by different portions // These have been hand-estimated from saved files - private static final int BYTES_PER_COMPONENT_UNCOMPRESSED = 590; private static final int BYTES_PER_COMPONENT_COMPRESSED = 80; - private static final int BYTES_PER_SIMULATION_UNCOMPRESSED = 1000; private static final int BYTES_PER_SIMULATION_COMPRESSED = 100; - private static final int BYTES_PER_DATAPOINT_UNCOMPRESSED = 350; private static final int BYTES_PER_DATAPOINT_COMPRESSED = 100; @@ -69,16 +67,10 @@ public class OpenRocketSaver extends RocketSaver { private Writer dest; @Override - public void save(OutputStream output, OpenRocketDocument document, StorageOptions options) - throws IOException { + public void save(OutputStream output, OpenRocketDocument document, StorageOptions options) throws IOException { log.info("Saving .ork file"); - if (options.isCompressionEnabled()) { - log.debug("Enabling compression"); - output = new GZIPOutputStream(output); - } - dest = new BufferedWriter(new OutputStreamWriter(output, OPENROCKET_CHARSET)); // Select file version number @@ -122,9 +114,6 @@ public class OpenRocketSaver extends RocketSaver { log.debug("Writing complete, flushing buffers"); dest.flush(); - if (options.isCompressionEnabled()) { - ((GZIPOutputStream) output).finish(); - } } /* @@ -168,6 +157,8 @@ public class OpenRocketSaver extends RocketSaver { long size = 0; + // TODO - estimate decals + // Size per component int componentCount = 0; Rocket rocket = doc.getRocket(); @@ -177,17 +168,11 @@ public class OpenRocketSaver extends RocketSaver { componentCount++; } - if (options.isCompressionEnabled()) - size += componentCount * BYTES_PER_COMPONENT_COMPRESSED; - else - size += componentCount * BYTES_PER_COMPONENT_UNCOMPRESSED; + size += componentCount * BYTES_PER_COMPONENT_COMPRESSED; // Size per simulation - if (options.isCompressionEnabled()) - size += doc.getSimulationCount() * BYTES_PER_SIMULATION_COMPRESSED; - else - size += doc.getSimulationCount() * BYTES_PER_SIMULATION_UNCOMPRESSED; + size += doc.getSimulationCount() * BYTES_PER_SIMULATION_COMPRESSED; // Size per flight data point @@ -204,10 +189,7 @@ public class OpenRocketSaver extends RocketSaver { } } - if (options.isCompressionEnabled()) - size += pointCount * BYTES_PER_DATAPOINT_COMPRESSED; - else - size += pointCount * BYTES_PER_DATAPOINT_UNCOMPRESSED; + size += pointCount * BYTES_PER_DATAPOINT_COMPRESSED; return size; } @@ -224,6 +206,9 @@ public class OpenRocketSaver extends RocketSaver { */ private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) { /* + * File version 1.6 is required for: + * - saving files using appearances and textures, flight configurations. + * * File version 1.5 is requires for: * - saving designs using ComponentPrests * - recovery device deployment on lower stage separation @@ -240,6 +225,33 @@ public class OpenRocketSaver extends RocketSaver { * Otherwise use version 1.0. */ + // Search the rocket for any Appearances or non-motor flight configurations (version 1.6) + for (RocketComponent c : document.getRocket()) { + if (c.getAppearance() != null) { + return FILE_VERSION_DIVISOR + 6; + } + if (c instanceof FlightConfigurableComponent) { + if (c instanceof MotorMount) { + MotorMount mmt = (MotorMount) c; + if (mmt.getIgnitionConfiguration().size() > 0) { + return FILE_VERSION_DIVISOR + 6; + } + } + if (c instanceof RecoveryDevice) { + RecoveryDevice recovery = (RecoveryDevice) c; + if (recovery.getDeploymentConfiguration().size() > 0) { + return FILE_VERSION_DIVISOR + 6; + } + } + if (c instanceof Stage) { + Stage stage = (Stage) c; + if (stage.getStageSeparationConfiguration().size() > 0) { + return FILE_VERSION_DIVISOR + 6; + } + } + } + } + // Search the rocket for any ComponentPresets (version 1.5) for (RocketComponent c : document.getRocket()) { if (c.getPresetComponent() != null) { @@ -250,7 +262,7 @@ public class OpenRocketSaver extends RocketSaver { // Search for recovery device deployment type LOWER_STAGE_SEPARATION (version 1.5) for (RocketComponent c : document.getRocket()) { if (c instanceof RecoveryDevice) { - if (((RecoveryDevice) c).getDeployEvent() == DeployEvent.LOWER_STAGE_SEPARATION) { + if (((RecoveryDevice) c).getDeploymentConfiguration().getDefault().getDeployEvent() == DeployEvent.LOWER_STAGE_SEPARATION) { return FILE_VERSION_DIVISOR + 5; } } @@ -272,7 +284,7 @@ public class OpenRocketSaver extends RocketSaver { continue; MotorMount mount = (MotorMount) c; - for (String id : document.getRocket().getMotorConfigurationIDs()) { + for (String id : document.getRocket().getFlightConfigurationIDs()) { if (mount.getMotor(id) != null) { return FILE_VERSION_DIVISOR + 4; } @@ -364,7 +376,7 @@ public class OpenRocketSaver extends RocketSaver { writeln(""); indent++; - writeln("" + escapeXML(simulation.getName()) + ""); + writeln("" + TextUtil.escapeXML(simulation.getName()) + ""); // TODO: MEDIUM: Other simulators/calculators writeln("RK4Simulator"); @@ -402,7 +414,7 @@ public class OpenRocketSaver extends RocketSaver { for (String s : simulation.getSimulationListeners()) { - writeElement("listener", escapeXML(s)); + writeElement("listener", TextUtil.escapeXML(s)); } // Write basic simulation data @@ -433,7 +445,7 @@ public class OpenRocketSaver extends RocketSaver { indent++; for (Warning w : data.getWarningSet()) { - writeElement("warning", escapeXML(w.toString())); + writeElement("warning", TextUtil.escapeXML(w.toString())); } // Check whether to store data @@ -457,7 +469,6 @@ public class OpenRocketSaver extends RocketSaver { } - private void saveFlightDataBranch(FlightDataBranch branch, double timeSkip) throws IOException { double previousTime = -100000; @@ -481,7 +492,7 @@ public class OpenRocketSaver extends RocketSaver { // Build the tag StringBuilder sb = new StringBuilder(); sb.append(" 0) sb.append(","); - sb.append(escapeXML(types[i].getName())); + sb.append(TextUtil.escapeXML(types[i].getName())); } sb.append("\">"); writeln(sb.toString()); diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/AppearanceHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/AppearanceHandler.java new file mode 100644 index 000000000..11b52f2ba --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/AppearanceHandler.java @@ -0,0 +1,99 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.appearance.AppearanceBuilder; +import net.sf.openrocket.appearance.Decal.EdgeMode; +import net.sf.openrocket.document.Attachment; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Color; + +import org.xml.sax.SAXException; + +class AppearanceHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final RocketComponent component; + + private final AppearanceBuilder builder = new AppearanceBuilder(); + private boolean isInDecal = false; + + public AppearanceHandler(RocketComponent component, DocumentLoadingContext context) { + this.context = context; + this.component = component; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) + throws SAXException { + if ("decal".equals(element)) { + String name = attributes.remove("name"); + Attachment a = context.getAttachmentFactory().getAttachment(name); + builder.setImage(context.getOpenRocketDocument().getDecalImage(a)); + double rotation = Double.parseDouble(attributes.remove("rotation")); + builder.setRotation(rotation); + String edgeModeName = attributes.remove("edgemode"); + EdgeMode edgeMode = EdgeMode.valueOf(edgeModeName); + builder.setEdgeMode(edgeMode); + isInDecal = true; + return this; + } + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { + if ("paint".equals(element)) { + int red = Integer.parseInt(attributes.get("red")); + int green = Integer.parseInt(attributes.get("green")); + int blue = Integer.parseInt(attributes.get("blue")); + builder.setPaint(new Color(red, green, blue)); + return; + } + if ("shine".equals(element)) { + double shine = Double.parseDouble(content); + builder.setShine(shine); + return; + } + if (isInDecal && "center".equals(element)) { + double x = Double.parseDouble(attributes.get("x")); + double y = Double.parseDouble(attributes.get("y")); + builder.setCenter(x, y); + return; + } + if (isInDecal && "offset".equals(element)) { + double x = Double.parseDouble(attributes.get("x")); + double y = Double.parseDouble(attributes.get("y")); + builder.setOffset(x, y); + return; + } + if (isInDecal && "scale".equals(element)) { + double x = Double.parseDouble(attributes.get("x")); + double y = Double.parseDouble(attributes.get("y")); + builder.setScaleUV(x, y); + return; + } + if (isInDecal && "decal".equals(element)) { + isInDecal = false; + return; + } + + super.closeElement(element, attributes, content, warnings); + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + if ("decal".equals(element)) { + isInDecal = false; + return; + } + component.setAppearance(builder.getAppearance()); + super.endHandler(element, attributes, content, warnings); + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/AtmosphereHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/AtmosphereHandler.java new file mode 100644 index 000000000..b109c1c24 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/AtmosphereHandler.java @@ -0,0 +1,76 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.simulation.SimulationOptions; + +import org.xml.sax.SAXException; + +class AtmosphereHandler extends AbstractElementHandler { + @SuppressWarnings("unused") + private final DocumentLoadingContext context; + private final String model; + private double temperature = Double.NaN; + private double pressure = Double.NaN; + + public AtmosphereHandler(String model, DocumentLoadingContext context) { + this.model = model; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + double d = Double.NaN; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException ignore) { + } + + if (element.equals("basetemperature")) { + if (Double.isNaN(d)) { + warnings.add("Illegal base temperature specified, ignoring."); + } + temperature = d; + } else if (element.equals("basepressure")) { + if (Double.isNaN(d)) { + warnings.add("Illegal base pressure specified, ignoring."); + } + pressure = d; + } else { + super.closeElement(element, attributes, content, warnings); + } + } + + + public void storeSettings(SimulationOptions cond, WarningSet warnings) { + if (!Double.isNaN(pressure)) { + cond.setLaunchPressure(pressure); + } + if (!Double.isNaN(temperature)) { + cond.setLaunchTemperature(temperature); + } + + if ("isa".equals(model)) { + cond.setISAAtmosphere(true); + } else if ("extendedisa".equals(model)) { + cond.setISAAtmosphere(false); + } else { + cond.setISAAtmosphere(true); + warnings.add("Unknown atmospheric model, using ISA."); + } + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/BooleanSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/BooleanSetter.java new file mode 100644 index 000000000..b0aef58c0 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/BooleanSetter.java @@ -0,0 +1,31 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +//// BooleanSetter - set a boolean value +class BooleanSetter implements Setter { + private final Reflection.Method setMethod; + + public BooleanSetter(Reflection.Method set) { + setMethod = set; + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + s = s.trim(); + if (s.equalsIgnoreCase("true")) { + setMethod.invoke(c, true); + } else if (s.equalsIgnoreCase("false")) { + setMethod.invoke(c, false); + } else { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/ClusterConfigurationSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/ClusterConfigurationSetter.java new file mode 100644 index 000000000..4fce5c064 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/ClusterConfigurationSetter.java @@ -0,0 +1,36 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.ClusterConfiguration; +import net.sf.openrocket.rocketcomponent.Clusterable; +import net.sf.openrocket.rocketcomponent.RocketComponent; + +class ClusterConfigurationSetter implements Setter { + + @Override + public void set(RocketComponent component, String value, HashMap attributes, + WarningSet warnings) { + + if (!(component instanceof Clusterable)) { + warnings.add("Illegal component defined as cluster."); + return; + } + + ClusterConfiguration config = null; + for (ClusterConfiguration c : ClusterConfiguration.CONFIGURATIONS) { + if (c.getXMLName().equals(value)) { + config = c; + break; + } + } + + if (config == null) { + warnings.add("Illegal cluster configuration specified."); + return; + } + + ((Clusterable) component).setClusterConfiguration(config); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/ColorSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/ColorSetter.java new file mode 100644 index 000000000..f290162e0 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/ColorSetter.java @@ -0,0 +1,54 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.Reflection; + +//// ColorSetter - sets a Color value +class ColorSetter implements Setter { + private final Reflection.Method setMethod; + + public ColorSetter(Reflection.Method set) { + setMethod = set; + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + String red = attributes.get("red"); + String green = attributes.get("green"); + String blue = attributes.get("blue"); + + if (red == null || green == null || blue == null) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + int r, g, b; + try { + r = Integer.parseInt(red); + g = Integer.parseInt(green); + b = Integer.parseInt(blue); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + if (r < 0 || g < 0 || b < 0 || r > 255 || g > 255 || b > 255) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + Color color = new Color(r, g, b); + setMethod.invoke(c, color); + + if (!s.trim().equals("")) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/ComponentHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentHandler.java new file mode 100644 index 000000000..85d782bb0 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentHandler.java @@ -0,0 +1,56 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.BugException; +import net.sf.openrocket.util.Reflection; + +/** + * A handler that creates components from the corresponding elements. The control of the + * contents is passed on to ComponentParameterHandler. + */ +class ComponentHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final RocketComponent parent; + + public ComponentHandler(RocketComponent parent, DocumentLoadingContext context) { + this.parent = parent; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + // Attempt to construct new component + Constructor constructor = DocumentConfig.constructors + .get(element); + if (constructor == null) { + warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); + return null; + } + + RocketComponent c; + try { + c = constructor.newInstance(); + } catch (InstantiationException e) { + throw new BugException("Error constructing component.", e); + } catch (IllegalAccessException e) { + throw new BugException("Error constructing component.", e); + } catch (InvocationTargetException e) { + throw Reflection.handleWrappedException(e); + } + + parent.addChild(c); + + return new ComponentParameterHandler(c, context); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/ComponentParameterHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentParameterHandler.java new file mode 100644 index 000000000..01e2bb3c7 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentParameterHandler.java @@ -0,0 +1,115 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.FreeformFinSet; +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Stage; + +/** + * A handler that populates the parameters of a previously constructed rocket component. + * This uses the setters, or delegates the handling to another handler for specific + * elements. + */ +class ComponentParameterHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final RocketComponent component; + + public ComponentParameterHandler(RocketComponent c, DocumentLoadingContext context) { + this.component = c; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + // Check for specific elements that contain other elements + if (element.equals("subcomponents")) { + return new ComponentHandler(component, context); + } + if ( element.equals("appearance")) { + return new AppearanceHandler(component,context); + } + if (element.equals("motormount")) { + if (!(component instanceof MotorMount)) { + warnings.add(Warning.fromString("Illegal component defined as motor mount.")); + return null; + } + return new MotorMountHandler((MotorMount) component, context); + } + if (element.equals("finpoints")) { + if (!(component instanceof FreeformFinSet)) { + warnings.add(Warning.fromString("Illegal component defined for fin points.")); + return null; + } + return new FinSetPointHandler((FreeformFinSet) component, context); + } + if (element.equals("motorconfiguration")) { + if (!(component instanceof Rocket)) { + warnings.add(Warning.fromString("Illegal component defined for motor configuration.")); + return null; + } + return new MotorConfigurationHandler((Rocket) component, context); + } + if ( element.equals("deploymentconfiguration")) { + if ( !(component instanceof RecoveryDevice) ) { + warnings.add(Warning.fromString("Illegal component defined as recovery device.")); + return null; + } + return new DeploymentConfigurationHandler( (RecoveryDevice) component, context ); + } + if ( element.equals("separationconfiguration")) { + if ( !(component instanceof Stage) ) { + warnings.add(Warning.fromString("Illegal component defined as stage.")); + return null; + } + return new StageSeparationConfigurationHandler( (Stage) component, context ); + } + + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("subcomponents") || element.equals("motormount") || + element.equals("finpoints") || element.equals("motorconfiguration") || + element.equals("appearance") || element.equals("deploymentconfiguration") || + element.equals("separationconfiguration")) { + return; + } + + // Search for the correct setter class + + Class c; + for (c = component.getClass(); c != null; c = c.getSuperclass()) { + String setterKey = c.getSimpleName() + ":" + element; + Setter s = DocumentConfig.setters.get(setterKey); + if (s != null) { + // Setter found + s.set(component, content, attributes, warnings); + break; + } + if (DocumentConfig.setters.containsKey(setterKey)) { + // Key exists but is null -> invalid parameter + c = null; + break; + } + } + if (c == null) { + warnings.add(Warning.fromString("Unknown parameter type '" + element + "' for " + + component.getComponentName() + ", ignoring.")); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/ComponentPresetSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentPresetSetter.java new file mode 100644 index 000000000..dead76d17 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/ComponentPresetSetter.java @@ -0,0 +1,74 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; +import java.util.List; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.preset.ComponentPreset; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.Reflection; + +////ComponentPresetSetter - sets a ComponentPreset value +class ComponentPresetSetter implements Setter { + private final Reflection.Method setMethod; + + public ComponentPresetSetter(Reflection.Method set) { + this.setMethod = set; + } + + @Override + public void set(RocketComponent c, String name, HashMap attributes, + WarningSet warnings) { + String manufacturerName = attributes.get("manufacturer"); + if (manufacturerName == null) { + warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no manufacturer specified. Ignored")); + return; + } + + String productNo = attributes.get("partno"); + if (productNo == null) { + warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no partno specified. Ignored")); + return; + } + + String digest = attributes.get("digest"); + if (digest == null) { + warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no digest specified.")); + } + + String type = attributes.get("type"); + if (type == null) { + warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no type specified.")); + } + + List presets = Application.getComponentPresetDao().find(manufacturerName, productNo); + + ComponentPreset matchingPreset = null; + + for (ComponentPreset preset : presets) { + if (digest != null && preset.getDigest().equals(digest)) { + // Found one with matching digest. Take it. + matchingPreset = preset; + break; + } + if (type != null && preset.getType().name().equals(type) && matchingPreset != null) { + // Found the first one with matching type. + matchingPreset = preset; + } + } + + // Was any found? + if (matchingPreset == null) { + warnings.add(Warning.fromString("No matching ComponentPreset for component " + c.getName() + " found matching " + manufacturerName + " " + productNo)); + return; + } + + if (digest != null && !matchingPreset.getDigest().equals(digest)) { + warnings.add(Warning.fromString("ComponentPreset for component " + c.getName() + " has wrong digest")); + } + + setMethod.invoke(c, matchingPreset); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/CustomExpressionHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/CustomExpressionHandler.java new file mode 100644 index 000000000..187f2ffff --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/CustomExpressionHandler.java @@ -0,0 +1,58 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.simulation.customexpression.CustomExpression; + +import org.xml.sax.SAXException; + +class CustomExpressionHandler extends AbstractElementHandler { + @SuppressWarnings("unused") + private final DocumentLoadingContext context; + private final OpenRocketContentHandler contentHandler; + public CustomExpression currentExpression; + + public CustomExpressionHandler(OpenRocketContentHandler contentHandler, DocumentLoadingContext context) { + this.context = context; + this.contentHandler = contentHandler; + currentExpression = new CustomExpression(contentHandler.getDocument()); + + } + + @Override + public ElementHandler openElement(String element, + HashMap attributes, WarningSet warnings) + throws SAXException { + + return this; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + if (element.equals("type")) { + contentHandler.getDocument().addCustomExpression(currentExpression); + } + + if (element.equals("name")) { + currentExpression.setName(content); + } + + if (element.equals("symbol")) { + currentExpression.setSymbol(content); + } + + if (element.equals("unit") && attributes.get("unittype").equals("auto")) { + currentExpression.setUnit(content); + } + + if (element.equals("expression")) { + currentExpression.setExpression(content); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/DatatypeHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/DatatypeHandler.java new file mode 100644 index 000000000..c3fef5140 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/DatatypeHandler.java @@ -0,0 +1,50 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; + +import org.xml.sax.SAXException; + +class DatatypeHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final OpenRocketContentHandler contentHandler; + private CustomExpressionHandler customExpressionHandler = null; + + public DatatypeHandler(OpenRocketContentHandler contentHandler, DocumentLoadingContext context) { + this.context = context; + this.contentHandler = contentHandler; + } + + @Override + public ElementHandler openElement(String element, + HashMap attributes, WarningSet warnings) + throws SAXException { + + if (element.equals("type") && attributes.get("source").equals("customexpression")) { + customExpressionHandler = new CustomExpressionHandler(contentHandler, context); + return customExpressionHandler; + } + else { + warnings.add(Warning.fromString("Unknown datatype " + element + " defined, ignoring")); + } + + return this; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { + attributes.remove("source"); + super.closeElement(element, attributes, content, warnings); + + if (customExpressionHandler != null) { + contentHandler.getDocument().addCustomExpression(customExpressionHandler.currentExpression); + } + + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/DeploymentConfigurationHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/DeploymentConfigurationHandler.java new file mode 100644 index 000000000..e40992bbb --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/DeploymentConfigurationHandler.java @@ -0,0 +1,80 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; + +import org.xml.sax.SAXException; + +class DeploymentConfigurationHandler extends AbstractElementHandler { + + private final RecoveryDevice recoveryDevice; + + private DeployEvent event = null; + private double delay = Double.NaN; + private double altitude = Double.NaN; + + public DeploymentConfigurationHandler(RecoveryDevice component, DocumentLoadingContext context) { + this.recoveryDevice = component; + } + + public DeploymentConfiguration getConfiguration(DeploymentConfiguration def) { + DeploymentConfiguration config = def.clone(); + if (event != null) { + config.setDeployEvent(event); + } + if (!Double.isNaN(delay)) { + config.setDeployDelay(delay); + } + if (!Double.isNaN(altitude)) { + config.setDeployAltitude(altitude); + } + return config; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) + throws SAXException { + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, + WarningSet warnings) throws SAXException { + + content = content.trim(); + + if ("deployevent".equals(element)) { + event = (DeployEvent) DocumentConfig.findEnum(content, DeployEvent.class); + if (event == null) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + return; + } else if ("deploydelay".equals(element)) { + delay = parseDouble(content, warnings, Warning.FILE_INVALID_PARAMETER); + return; + } else if ("deployaltitude".equals(element)) { + altitude = parseDouble(content, warnings, Warning.FILE_INVALID_PARAMETER); + return; + } + super.closeElement(element, attributes, content, warnings); + + } + + @Override + public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { + String configId = attributes.get("configid"); + DeploymentConfiguration def = recoveryDevice.getDeploymentConfiguration().getDefault(); + recoveryDevice.getDeploymentConfiguration().set(configId, getConfiguration(def)); + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java b/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java new file mode 100644 index 000000000..dc14486d0 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java @@ -0,0 +1,431 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Locale; + +import net.sf.openrocket.material.Material; +import net.sf.openrocket.preset.ComponentPreset; +import net.sf.openrocket.rocketcomponent.BodyComponent; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.Bulkhead; +import net.sf.openrocket.rocketcomponent.CenteringRing; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; +import net.sf.openrocket.rocketcomponent.EllipticalFinSet; +import net.sf.openrocket.rocketcomponent.EngineBlock; +import net.sf.openrocket.rocketcomponent.ExternalComponent; +import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.FreeformFinSet; +import net.sf.openrocket.rocketcomponent.InnerTube; +import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.MassComponent; +import net.sf.openrocket.rocketcomponent.MassObject; +import net.sf.openrocket.rocketcomponent.NoseCone; +import net.sf.openrocket.rocketcomponent.Parachute; +import net.sf.openrocket.rocketcomponent.RadiusRingComponent; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.ReferenceType; +import net.sf.openrocket.rocketcomponent.RingComponent; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.ShockCord; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; +import net.sf.openrocket.rocketcomponent.Streamer; +import net.sf.openrocket.rocketcomponent.StructuralComponent; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; +import net.sf.openrocket.rocketcomponent.ThicknessRingComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; +import net.sf.openrocket.rocketcomponent.TubeCoupler; +import net.sf.openrocket.util.BugException; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.LineStyle; +import net.sf.openrocket.util.Reflection; + +class DocumentConfig { + + /* Remember to update OpenRocketSaver as well! */ + public static final String[] SUPPORTED_VERSIONS = { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6" }; + + /** + * Divisor used in converting an integer version to the point-represented version. + * The integer version divided by this value is the major version and the remainder is + * the minor version. For example 101 corresponds to file version "1.1". + */ + public static final int FILE_VERSION_DIVISOR = 100; + + + //////// Component constructors + static final HashMap> constructors = new HashMap>(); + static { + try { + // External components + constructors.put("bodytube", BodyTube.class.getConstructor(new Class[0])); + constructors.put("transition", Transition.class.getConstructor(new Class[0])); + constructors.put("nosecone", NoseCone.class.getConstructor(new Class[0])); + constructors.put("trapezoidfinset", TrapezoidFinSet.class.getConstructor(new Class[0])); + constructors.put("ellipticalfinset", EllipticalFinSet.class.getConstructor(new Class[0])); + constructors.put("freeformfinset", FreeformFinSet.class.getConstructor(new Class[0])); + constructors.put("launchlug", LaunchLug.class.getConstructor(new Class[0])); + + // Internal components + constructors.put("engineblock", EngineBlock.class.getConstructor(new Class[0])); + constructors.put("innertube", InnerTube.class.getConstructor(new Class[0])); + constructors.put("tubecoupler", TubeCoupler.class.getConstructor(new Class[0])); + constructors.put("bulkhead", Bulkhead.class.getConstructor(new Class[0])); + constructors.put("centeringring", CenteringRing.class.getConstructor(new Class[0])); + + constructors.put("masscomponent", MassComponent.class.getConstructor(new Class[0])); + constructors.put("shockcord", ShockCord.class.getConstructor(new Class[0])); + constructors.put("parachute", Parachute.class.getConstructor(new Class[0])); + constructors.put("streamer", Streamer.class.getConstructor(new Class[0])); + + // Other + constructors.put("stage", Stage.class.getConstructor(new Class[0])); + + } catch (NoSuchMethodException e) { + throw new BugException( + "Error in constructing the 'constructors' HashMap."); + } + } + + + //////// Parameter setters + /* + * The keys are of the form Class:param, where Class is the class name and param + * the element name. Setters are searched for in descending class order. + * A setter of null means setting the parameter is not allowed. + */ + static final HashMap setters = new HashMap(); + static { + // RocketComponent + setters.put("RocketComponent:name", new StringSetter( + Reflection.findMethod(RocketComponent.class, "setName", String.class))); + setters.put("RocketComponent:color", new ColorSetter( + Reflection.findMethod(RocketComponent.class, "setColor", Color.class))); + setters.put("RocketComponent:linestyle", new EnumSetter( + Reflection.findMethod(RocketComponent.class, "setLineStyle", LineStyle.class), + LineStyle.class)); + setters.put("RocketComponent:position", new PositionSetter()); + setters.put("RocketComponent:overridemass", new OverrideSetter( + Reflection.findMethod(RocketComponent.class, "setOverrideMass", double.class), + Reflection.findMethod(RocketComponent.class, "setMassOverridden", boolean.class))); + setters.put("RocketComponent:overridecg", new OverrideSetter( + Reflection.findMethod(RocketComponent.class, "setOverrideCGX", double.class), + Reflection.findMethod(RocketComponent.class, "setCGOverridden", boolean.class))); + setters.put("RocketComponent:overridesubcomponents", new BooleanSetter( + Reflection.findMethod(RocketComponent.class, "setOverrideSubcomponents", boolean.class))); + setters.put("RocketComponent:comment", new StringSetter( + Reflection.findMethod(RocketComponent.class, "setComment", String.class))); + setters.put("RocketComponent:preset", new ComponentPresetSetter( + Reflection.findMethod(RocketComponent.class, "loadPreset", ComponentPreset.class))); + + // ExternalComponent + setters.put("ExternalComponent:finish", new EnumSetter( + Reflection.findMethod(ExternalComponent.class, "setFinish", Finish.class), + Finish.class)); + setters.put("ExternalComponent:material", new MaterialSetter( + Reflection.findMethod(ExternalComponent.class, "setMaterial", Material.class), + Material.Type.BULK)); + + // BodyComponent + setters.put("BodyComponent:length", new DoubleSetter( + Reflection.findMethod(BodyComponent.class, "setLength", double.class))); + + // SymmetricComponent + setters.put("SymmetricComponent:thickness", new DoubleSetter( + Reflection.findMethod(SymmetricComponent.class, "setThickness", double.class), + "filled", + Reflection.findMethod(SymmetricComponent.class, "setFilled", boolean.class))); + + // BodyTube + setters.put("BodyTube:radius", new DoubleSetter( + Reflection.findMethod(BodyTube.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethod(BodyTube.class, "setOuterRadiusAutomatic", boolean.class))); + + // Transition + setters.put("Transition:shape", new EnumSetter( + Reflection.findMethod(Transition.class, "setType", Transition.Shape.class), + Transition.Shape.class)); + setters.put("Transition:shapeclipped", new BooleanSetter( + Reflection.findMethod(Transition.class, "setClipped", boolean.class))); + setters.put("Transition:shapeparameter", new DoubleSetter( + Reflection.findMethod(Transition.class, "setShapeParameter", double.class))); + + setters.put("Transition:foreradius", new DoubleSetter( + Reflection.findMethod(Transition.class, "setForeRadius", double.class), + "auto", + Reflection.findMethod(Transition.class, "setForeRadiusAutomatic", boolean.class))); + setters.put("Transition:aftradius", new DoubleSetter( + Reflection.findMethod(Transition.class, "setAftRadius", double.class), + "auto", + Reflection.findMethod(Transition.class, "setAftRadiusAutomatic", boolean.class))); + + setters.put("Transition:foreshoulderradius", new DoubleSetter( + Reflection.findMethod(Transition.class, "setForeShoulderRadius", double.class))); + setters.put("Transition:foreshoulderlength", new DoubleSetter( + Reflection.findMethod(Transition.class, "setForeShoulderLength", double.class))); + setters.put("Transition:foreshoulderthickness", new DoubleSetter( + Reflection.findMethod(Transition.class, "setForeShoulderThickness", double.class))); + setters.put("Transition:foreshouldercapped", new BooleanSetter( + Reflection.findMethod(Transition.class, "setForeShoulderCapped", boolean.class))); + + setters.put("Transition:aftshoulderradius", new DoubleSetter( + Reflection.findMethod(Transition.class, "setAftShoulderRadius", double.class))); + setters.put("Transition:aftshoulderlength", new DoubleSetter( + Reflection.findMethod(Transition.class, "setAftShoulderLength", double.class))); + setters.put("Transition:aftshoulderthickness", new DoubleSetter( + Reflection.findMethod(Transition.class, "setAftShoulderThickness", double.class))); + setters.put("Transition:aftshouldercapped", new BooleanSetter( + Reflection.findMethod(Transition.class, "setAftShoulderCapped", boolean.class))); + + // NoseCone - disable disallowed elements + setters.put("NoseCone:foreradius", null); + setters.put("NoseCone:foreshoulderradius", null); + setters.put("NoseCone:foreshoulderlength", null); + setters.put("NoseCone:foreshoulderthickness", null); + setters.put("NoseCone:foreshouldercapped", null); + + // FinSet + setters.put("FinSet:fincount", new IntSetter( + Reflection.findMethod(FinSet.class, "setFinCount", int.class))); + setters.put("FinSet:rotation", new DoubleSetter( + Reflection.findMethod(FinSet.class, "setBaseRotation", double.class), Math.PI / 180.0)); + setters.put("FinSet:thickness", new DoubleSetter( + Reflection.findMethod(FinSet.class, "setThickness", double.class))); + setters.put("FinSet:crosssection", new EnumSetter( + Reflection.findMethod(FinSet.class, "setCrossSection", FinSet.CrossSection.class), + FinSet.CrossSection.class)); + setters.put("FinSet:cant", new DoubleSetter( + Reflection.findMethod(FinSet.class, "setCantAngle", double.class), Math.PI / 180.0)); + setters.put("FinSet:tabheight", new DoubleSetter( + Reflection.findMethod(FinSet.class, "setTabHeight", double.class))); + setters.put("FinSet:tablength", new DoubleSetter( + Reflection.findMethod(FinSet.class, "setTabLength", double.class))); + setters.put("FinSet:tabposition", new FinTabPositionSetter()); + + // TrapezoidFinSet + setters.put("TrapezoidFinSet:rootchord", new DoubleSetter( + Reflection.findMethod(TrapezoidFinSet.class, "setRootChord", double.class))); + setters.put("TrapezoidFinSet:tipchord", new DoubleSetter( + Reflection.findMethod(TrapezoidFinSet.class, "setTipChord", double.class))); + setters.put("TrapezoidFinSet:sweeplength", new DoubleSetter( + Reflection.findMethod(TrapezoidFinSet.class, "setSweep", double.class))); + setters.put("TrapezoidFinSet:height", new DoubleSetter( + Reflection.findMethod(TrapezoidFinSet.class, "setHeight", double.class))); + + // EllipticalFinSet + setters.put("EllipticalFinSet:rootchord", new DoubleSetter( + Reflection.findMethod(EllipticalFinSet.class, "setLength", double.class))); + setters.put("EllipticalFinSet:height", new DoubleSetter( + Reflection.findMethod(EllipticalFinSet.class, "setHeight", double.class))); + + // FreeformFinSet points handled as a special handler + + // LaunchLug + setters.put("LaunchLug:radius", new DoubleSetter( + Reflection.findMethod(LaunchLug.class, "setOuterRadius", double.class))); + setters.put("LaunchLug:length", new DoubleSetter( + Reflection.findMethod(LaunchLug.class, "setLength", double.class))); + setters.put("LaunchLug:thickness", new DoubleSetter( + Reflection.findMethod(LaunchLug.class, "setThickness", double.class))); + setters.put("LaunchLug:radialdirection", new DoubleSetter( + Reflection.findMethod(LaunchLug.class, "setRadialDirection", double.class), + Math.PI / 180.0)); + + // InternalComponent - nothing + + // StructuralComponent + setters.put("StructuralComponent:material", new MaterialSetter( + Reflection.findMethod(StructuralComponent.class, "setMaterial", Material.class), + Material.Type.BULK)); + + // RingComponent + setters.put("RingComponent:length", new DoubleSetter( + Reflection.findMethod(RingComponent.class, "setLength", double.class))); + setters.put("RingComponent:radialposition", new DoubleSetter( + Reflection.findMethod(RingComponent.class, "setRadialPosition", double.class))); + setters.put("RingComponent:radialdirection", new DoubleSetter( + Reflection.findMethod(RingComponent.class, "setRadialDirection", double.class), + Math.PI / 180.0)); + + // ThicknessRingComponent - radius on separate components due to differing automatics + setters.put("ThicknessRingComponent:thickness", new DoubleSetter( + Reflection.findMethod(ThicknessRingComponent.class, "setThickness", double.class))); + + // EngineBlock + setters.put("EngineBlock:outerradius", new DoubleSetter( + Reflection.findMethod(EngineBlock.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethod(EngineBlock.class, "setOuterRadiusAutomatic", boolean.class))); + + // TubeCoupler + setters.put("TubeCoupler:outerradius", new DoubleSetter( + Reflection.findMethod(TubeCoupler.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethod(TubeCoupler.class, "setOuterRadiusAutomatic", boolean.class))); + + // InnerTube + setters.put("InnerTube:outerradius", new DoubleSetter( + Reflection.findMethod(InnerTube.class, "setOuterRadius", double.class))); + setters.put("InnerTube:clusterconfiguration", new ClusterConfigurationSetter()); + setters.put("InnerTube:clusterscale", new DoubleSetter( + Reflection.findMethod(InnerTube.class, "setClusterScale", double.class))); + setters.put("InnerTube:clusterrotation", new DoubleSetter( + Reflection.findMethod(InnerTube.class, "setClusterRotation", double.class), + Math.PI / 180.0)); + + // RadiusRingComponent + + // Bulkhead + setters.put("RadiusRingComponent:innerradius", new DoubleSetter( + Reflection.findMethod(RadiusRingComponent.class, "setInnerRadius", double.class))); + setters.put("Bulkhead:outerradius", new DoubleSetter( + Reflection.findMethod(Bulkhead.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethod(Bulkhead.class, "setOuterRadiusAutomatic", boolean.class))); + + // CenteringRing + setters.put("CenteringRing:innerradius", new DoubleSetter( + Reflection.findMethod(CenteringRing.class, "setInnerRadius", double.class), + "auto", + Reflection.findMethod(CenteringRing.class, "setInnerRadiusAutomatic", boolean.class))); + setters.put("CenteringRing:outerradius", new DoubleSetter( + Reflection.findMethod(CenteringRing.class, "setOuterRadius", double.class), + "auto", + Reflection.findMethod(CenteringRing.class, "setOuterRadiusAutomatic", boolean.class))); + + + // MassObject + setters.put("MassObject:packedlength", new DoubleSetter( + Reflection.findMethod(MassObject.class, "setLength", double.class))); + setters.put("MassObject:packedradius", new DoubleSetter( + Reflection.findMethod(MassObject.class, "setRadius", double.class))); + setters.put("MassObject:radialposition", new DoubleSetter( + Reflection.findMethod(MassObject.class, "setRadialPosition", double.class))); + setters.put("MassObject:radialdirection", new DoubleSetter( + Reflection.findMethod(MassObject.class, "setRadialDirection", double.class), + Math.PI / 180.0)); + + // MassComponent + setters.put("MassComponent:mass", new DoubleSetter( + Reflection.findMethod(MassComponent.class, "setComponentMass", double.class))); + + // ShockCord + setters.put("ShockCord:cordlength", new DoubleSetter( + Reflection.findMethod(ShockCord.class, "setCordLength", double.class))); + setters.put("ShockCord:material", new MaterialSetter( + Reflection.findMethod(ShockCord.class, "setMaterial", Material.class), + Material.Type.LINE)); + + // RecoveryDevice + setters.put("RecoveryDevice:cd", new DoubleSetter( + Reflection.findMethod(RecoveryDevice.class, "setCD", double.class), + "auto", + Reflection.findMethod(RecoveryDevice.class, "setCDAutomatic", boolean.class))); + setters.put("RecoveryDevice:deployevent", new EnumSetter( + Reflection.findMethod(RecoveryDevice.class, "getDeploymentConfiguration"), + Reflection.findMethod(DeploymentConfiguration.class, "setDeployEvent", DeployEvent.class), + DeployEvent.class)); + setters.put("RecoveryDevice:deployaltitude", new DoubleSetter( + Reflection.findMethod(RecoveryDevice.class, "getDeploymentConfiguration"), + Reflection.findMethod(DeploymentConfiguration.class, "setDeployAltitude", double.class))); + setters.put("RecoveryDevice:deploydelay", new DoubleSetter( + Reflection.findMethod(RecoveryDevice.class, "getDeploymentConfiguration"), + Reflection.findMethod(DeploymentConfiguration.class, "setDeployDelay", double.class))); + setters.put("RecoveryDevice:material", new MaterialSetter( + Reflection.findMethod(RecoveryDevice.class, "setMaterial", Material.class), + Material.Type.SURFACE)); + + // Parachute + setters.put("Parachute:diameter", new DoubleSetter( + Reflection.findMethod(Parachute.class, "setDiameter", double.class))); + setters.put("Parachute:linecount", new IntSetter( + Reflection.findMethod(Parachute.class, "setLineCount", int.class))); + setters.put("Parachute:linelength", new DoubleSetter( + Reflection.findMethod(Parachute.class, "setLineLength", double.class))); + setters.put("Parachute:linematerial", new MaterialSetter( + Reflection.findMethod(Parachute.class, "setLineMaterial", Material.class), + Material.Type.LINE)); + + // Streamer + setters.put("Streamer:striplength", new DoubleSetter( + Reflection.findMethod(Streamer.class, "setStripLength", double.class))); + setters.put("Streamer:stripwidth", new DoubleSetter( + Reflection.findMethod(Streamer.class, "setStripWidth", double.class))); + + // Rocket + // handled by separate handler + setters.put("Rocket:referencetype", new EnumSetter( + Reflection.findMethod(Rocket.class, "setReferenceType", ReferenceType.class), + ReferenceType.class)); + setters.put("Rocket:customreference", new DoubleSetter( + Reflection.findMethod(Rocket.class, "setCustomReferenceLength", double.class))); + setters.put("Rocket:designer", new StringSetter( + Reflection.findMethod(Rocket.class, "setDesigner", String.class))); + setters.put("Rocket:revision", new StringSetter( + Reflection.findMethod(Rocket.class, "setRevision", String.class))); + + // Stage + setters.put("Stage:separationevent", new EnumSetter( + Reflection.findMethod(Stage.class, "getStageSeparationConfiguration"), + Reflection.findMethod(StageSeparationConfiguration.class, "setSeparationEvent", StageSeparationConfiguration.SeparationEvent.class), + StageSeparationConfiguration.SeparationEvent.class)); + setters.put("Stage:separationdelay", new DoubleSetter( + Reflection.findMethod(Stage.class, "getStageSeparationConfiguration"), + Reflection.findMethod(StageSeparationConfiguration.class, "setSeparationDelay", double.class))); + + } + + + /** + * Search for a enum value that has the corresponding name as an XML value. The current + * conversion from enum name to XML value is to lowercase the name and strip out all + * underscore characters. This method returns a match to these criteria, or null + * if no such enum exists. + * + * @param then enum type. + * @param name the XML value, null ok. + * @param enumClass the class of the enum. + * @return the found enum value, or null. + */ + public static > Enum findEnum(String name, + Class> enumClass) { + + if (name == null) + return null; + name = name.trim(); + for (Enum e : enumClass.getEnumConstants()) { + if (e.name().toLowerCase(Locale.ENGLISH).replace("_", "").equals(name)) { + return e; + } + } + return null; + } + + + /** + * Convert a string to a double including formatting specifications of the OpenRocket + * file format. This accepts all formatting that is valid for + * Double.parseDouble(s) and a few others as well ("Inf", "-Inf"). + * + * @param s the string to parse. + * @return the numerical value. + * @throws NumberFormatException the the string cannot be parsed. + */ + public static double stringToDouble(String s) throws NumberFormatException { + if (s == null) + throw new NumberFormatException("null string"); + if (s.equalsIgnoreCase("NaN")) + return Double.NaN; + if (s.equalsIgnoreCase("Inf")) + return Double.POSITIVE_INFINITY; + if (s.equalsIgnoreCase("-Inf")) + return Double.NEGATIVE_INFINITY; + return Double.parseDouble(s); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/DocumentLoadingContext.java b/core/src/net/sf/openrocket/file/openrocket/importt/DocumentLoadingContext.java deleted file mode 100644 index cd920eae3..000000000 --- a/core/src/net/sf/openrocket/file/openrocket/importt/DocumentLoadingContext.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.sf.openrocket.file.openrocket.importt; - -import net.sf.openrocket.file.MotorFinder; - -public class DocumentLoadingContext { - - private int fileVersion; - private MotorFinder motorFinder; - - - public int getFileVersion() { - return fileVersion; - } - - public void setFileVersion(int fileVersion) { - this.fileVersion = fileVersion; - } - - public MotorFinder getMotorFinder() { - return motorFinder; - } - - public void setMotorFinder(MotorFinder motorFinder) { - this.motorFinder = motorFinder; - } - -} diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/DoubleSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/DoubleSetter.java new file mode 100644 index 000000000..9fdd3e9f3 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/DoubleSetter.java @@ -0,0 +1,105 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.FlightConfiguration; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; +import net.sf.openrocket.util.Reflection.Method; + +//// DoubleSetter - sets a double value or (alternatively) if a specific string is encountered +//// calls a setXXX(boolean) method. +class DoubleSetter implements Setter { + private final Reflection.Method configGetter; + private final Reflection.Method setMethod; + private final String specialString; + private final Reflection.Method specialMethod; + private final double multiplier; + + /** + * Set only the double value. + * @param set set method for the double value. + */ + public DoubleSetter(Reflection.Method set) { + this.setMethod = set; + this.configGetter = null; + this.specialString = null; + this.specialMethod = null; + this.multiplier = 1.0; + } + + /** + * Multiply with the given multiplier and set the double value. + * @param set set method for the double value. + * @param mul multiplier. + */ + public DoubleSetter(Reflection.Method set, double mul) { + this.setMethod = set; + this.configGetter = null; + this.specialString = null; + this.specialMethod = null; + this.multiplier = mul; + } + + /** + * Set the double value, or if the value equals the special string, use the + * special setter and set it to true. + * + * @param set double setter. + * @param special special string + * @param specialMethod boolean setter. + */ + public DoubleSetter(Reflection.Method set, String special, + Reflection.Method specialMethod) { + this.setMethod = set; + this.configGetter = null; + this.specialString = special; + this.specialMethod = specialMethod; + this.multiplier = 1.0; + } + + + /** + * Set a double value of the default configuration of a FlightConfiguration object. + * + * @param configGetter getter method for the FlightConfiguration object + * @param setter setter method for the configuration object + */ + public DoubleSetter(Method configGetter, Method setter) { + this.setMethod = setter; + this.configGetter = configGetter; + this.specialString = null; + this.specialMethod = null; + this.multiplier = 1.0; + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + s = s.trim(); + + // Check for special case + if (specialMethod != null && s.equalsIgnoreCase(specialString)) { + specialMethod.invoke(c, true); + return; + } + + // Normal case + try { + double d = Double.parseDouble(s); + + if (configGetter == null) { + setMethod.invoke(c, d * multiplier); + } else { + FlightConfiguration config = (FlightConfiguration) configGetter.invoke(c); + Object obj = config.getDefault(); + setMethod.invoke(obj, d * multiplier); + } + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/EnumSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/EnumSetter.java new file mode 100644 index 000000000..55ceaac34 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/EnumSetter.java @@ -0,0 +1,46 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.FlightConfiguration; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; +import net.sf.openrocket.util.Reflection.Method; + +//// EnumSetter - sets a generic enum type +class EnumSetter> implements Setter { + private final Reflection.Method configurationGetter; + private final Reflection.Method setter; + private final Class enumClass; + + public EnumSetter(Reflection.Method setter, Class enumClass) { + this(null, setter, enumClass); + } + + public EnumSetter(Method configurationGetter, Method setter, Class enumClass) { + this.configurationGetter = configurationGetter; + this.setter = setter; + this.enumClass = enumClass; + } + + @Override + public void set(RocketComponent c, String name, HashMap attributes, + WarningSet warnings) { + + Enum setEnum = DocumentConfig.findEnum(name, enumClass); + if (setEnum == null) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + if (configurationGetter == null) { + setter.invoke(c, setEnum); + } else { + FlightConfiguration config = (FlightConfiguration) configurationGetter.invoke(c); + Object obj = config.getDefault(); + setter.invoke(obj, setEnum); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/FinSetPointHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/FinSetPointHandler.java new file mode 100644 index 000000000..9d9941270 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/FinSetPointHandler.java @@ -0,0 +1,71 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.ArrayList; +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.FreeformFinSet; +import net.sf.openrocket.rocketcomponent.IllegalFinPointException; +import net.sf.openrocket.util.Coordinate; + +import org.xml.sax.SAXException; + +/** + * A handler that reads the specifications within the freeformfinset's + * elements. + */ +class FinSetPointHandler extends AbstractElementHandler { + @SuppressWarnings("unused") + private final DocumentLoadingContext context; + private final FreeformFinSet finset; + private final ArrayList coordinates = new ArrayList(); + + public FinSetPointHandler(FreeformFinSet finset, DocumentLoadingContext context) { + this.finset = finset; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + String strx = attributes.remove("x"); + String stry = attributes.remove("y"); + if (strx == null || stry == null) { + warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); + return; + } + try { + double x = Double.parseDouble(strx); + double y = Double.parseDouble(stry); + coordinates.add(new Coordinate(x, y)); + } catch (NumberFormatException e) { + warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); + return; + } + + super.closeElement(element, attributes, content, warnings); + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) { + try { + finset.setPoints(coordinates.toArray(new Coordinate[0])); + } catch (IllegalFinPointException e) { + warnings.add(Warning.fromString("Freeform fin set point definitions illegal, ignoring.")); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/FinTabPositionSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/FinTabPositionSetter.java new file mode 100644 index 000000000..f6e046d19 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/FinTabPositionSetter.java @@ -0,0 +1,46 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.FinSet.TabRelativePosition; +import net.sf.openrocket.util.Reflection; + +class FinTabPositionSetter extends DoubleSetter { + + public FinTabPositionSetter() { + super(Reflection.findMethod(FinSet.class, "setTabShift", double.class)); + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + if (!(c instanceof FinSet)) { + throw new IllegalStateException("FinTabPositionSetter called for component " + c); + } + + String relative = attributes.get("relativeto"); + FinSet.TabRelativePosition position = + (TabRelativePosition) DocumentConfig.findEnum(relative, + FinSet.TabRelativePosition.class); + + if (position != null) { + + ((FinSet) c).setTabRelativePosition(position); + + } else { + if (relative == null) { + warnings.add("Required attribute 'relativeto' not found for fin tab position."); + } else { + warnings.add("Illegal attribute value '" + relative + "' encountered."); + } + } + + super.set(c, s, attributes, warnings); + } + + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataBranchHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataBranchHandler.java new file mode 100644 index 000000000..4adc42546 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataBranchHandler.java @@ -0,0 +1,154 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.simulation.FlightDataBranch; +import net.sf.openrocket.simulation.FlightDataType; +import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.simulation.FlightEvent.Type; +import net.sf.openrocket.simulation.customexpression.CustomExpression; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; + +class FlightDataBranchHandler extends AbstractElementHandler { + @SuppressWarnings("unused") + private final DocumentLoadingContext context; + private final FlightDataType[] types; + private final FlightDataBranch branch; + + private static final LogHelper log = Application.getLogger(); + private final SingleSimulationHandler simHandler; + + public FlightDataBranchHandler(String name, String typeList, SingleSimulationHandler simHandler, DocumentLoadingContext context) { + this.simHandler = simHandler; + this.context = context; + String[] split = typeList.split(","); + types = new FlightDataType[split.length]; + for (int i = 0; i < split.length; i++) { + String typeName = split[i]; + FlightDataType matching = findFlightDataType(typeName); + types[i] = matching; + //types[i] = FlightDataType.getType(typeName, matching.getSymbol(), matching.getUnitGroup()); + } + + // TODO: LOW: May throw an IllegalArgumentException + branch = new FlightDataBranch(name, types); + } + + // Find the full flight data type given name only + // Note: this way of doing it requires that custom expressions always come before flight data in the file, + // not the nicest but this is always the case anyway. + private FlightDataType findFlightDataType(String name) { + + // Kevins version with lookup by key. Not using right now + /* + if ( key != null ) { + for (FlightDataType t : FlightDataType.ALL_TYPES){ + if (t.getKey().equals(key) ){ + return t; + } + } + } + */ + + // Look in built in types + for (FlightDataType t : FlightDataType.ALL_TYPES) { + if (t.getName().equals(name)) { + return t; + } + } + + // Look in custom expressions + for (CustomExpression exp : simHandler.getDocument().getCustomExpressions()) { + if (exp.getName().equals(name)) { + return exp.getType(); + } + } + + log.warn("Could not find the flight data type '" + name + "' used in the XML file. Substituted type with unknown symbol and units."); + return FlightDataType.getType(name, "Unknown", UnitGroup.UNITS_NONE); + } + + public FlightDataBranch getBranch() { + branch.immute(); + return branch; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("datapoint")) + return PlainTextHandler.INSTANCE; + if (element.equals("event")) + return PlainTextHandler.INSTANCE; + + warnings.add("Unknown element '" + element + "' encountered, ignoring."); + return null; + } + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("event")) { + double time; + FlightEvent.Type type; + + try { + time = DocumentConfig.stringToDouble(attributes.get("time")); + } catch (NumberFormatException e) { + warnings.add("Illegal event specification, ignoring."); + return; + } + + type = (Type) DocumentConfig.findEnum(attributes.get("type"), FlightEvent.Type.class); + if (type == null) { + warnings.add("Illegal event specification, ignoring."); + return; + } + + branch.addEvent(new FlightEvent(type, time)); + return; + } + + if (!element.equals("datapoint")) { + warnings.add("Unknown element '" + element + "' encountered, ignoring."); + return; + } + + // element == "datapoint" + + + // Check line format + String[] split = content.split(","); + if (split.length != types.length) { + warnings.add("Data point did not contain correct amount of values, ignoring point."); + return; + } + + // Parse the doubles + double[] values = new double[split.length]; + for (int i = 0; i < values.length; i++) { + try { + values[i] = DocumentConfig.stringToDouble(split[i]); + } catch (NumberFormatException e) { + warnings.add("Data point format error, ignoring point."); + return; + } + } + + // Add point to branch + branch.addPoint(); + for (int i = 0; i < types.length; i++) { + branch.setValue(types[i], values[i]); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataHandler.java new file mode 100644 index 000000000..bb2a78976 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/FlightDataHandler.java @@ -0,0 +1,138 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.simulation.FlightData; +import net.sf.openrocket.simulation.FlightDataBranch; + +class FlightDataHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + + private FlightDataBranchHandler dataHandler; + private WarningSet warningSet = new WarningSet(); + private List branches = new ArrayList(); + + private SingleSimulationHandler simHandler; + private FlightData data; + + + public FlightDataHandler(SingleSimulationHandler simHandler, DocumentLoadingContext context) { + this.context = context; + this.simHandler = simHandler; + } + + public FlightData getFlightData() { + return data; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("warning")) { + return PlainTextHandler.INSTANCE; + } + if (element.equals("databranch")) { + if (attributes.get("name") == null || attributes.get("types") == null) { + warnings.add("Illegal flight data definition, ignoring."); + return null; + } + dataHandler = new FlightDataBranchHandler(attributes.get("name"), + attributes.get("types"), + simHandler, context); + return dataHandler; + } + + warnings.add("Unknown element '" + element + "' encountered, ignoring."); + return null; + } + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("databranch")) { + FlightDataBranch branch = dataHandler.getBranch(); + if (branch.getLength() > 0) { + branches.add(branch); + } + } else if (element.equals("warning")) { + warningSet.add(Warning.fromString(content)); + } + } + + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (branches.size() > 0) { + data = new FlightData(branches.toArray(new FlightDataBranch[0])); + } else { + double maxAltitude = Double.NaN; + double maxVelocity = Double.NaN; + double maxAcceleration = Double.NaN; + double maxMach = Double.NaN; + double timeToApogee = Double.NaN; + double flightTime = Double.NaN; + double groundHitVelocity = Double.NaN; + double launchRodVelocity = Double.NaN; + double deploymentVelocity = Double.NaN; + + try { + maxAltitude = DocumentConfig.stringToDouble(attributes.get("maxaltitude")); + } catch (NumberFormatException ignore) { + } + try { + maxVelocity = DocumentConfig.stringToDouble(attributes.get("maxvelocity")); + } catch (NumberFormatException ignore) { + } + try { + maxAcceleration = DocumentConfig.stringToDouble(attributes.get("maxacceleration")); + } catch (NumberFormatException ignore) { + } + try { + maxMach = DocumentConfig.stringToDouble(attributes.get("maxmach")); + } catch (NumberFormatException ignore) { + } + try { + timeToApogee = DocumentConfig.stringToDouble(attributes.get("timetoapogee")); + } catch (NumberFormatException ignore) { + } + try { + flightTime = DocumentConfig.stringToDouble(attributes.get("flighttime")); + } catch (NumberFormatException ignore) { + } + try { + groundHitVelocity = + DocumentConfig.stringToDouble(attributes.get("groundhitvelocity")); + } catch (NumberFormatException ignore) { + } + try { + launchRodVelocity = DocumentConfig.stringToDouble(attributes.get("launchrodvelocity")); + } catch (NumberFormatException ignore) { + } + try { + deploymentVelocity = DocumentConfig.stringToDouble(attributes.get("deploymentvelocity")); + } catch (NumberFormatException ignore) { + } + + data = new FlightData(maxAltitude, maxVelocity, maxAcceleration, maxMach, + timeToApogee, flightTime, groundHitVelocity, launchRodVelocity, deploymentVelocity); + } + + data.getWarningSet().addAll(warningSet); + data.immute(); + } + + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/IgnitionConfigurationHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/IgnitionConfigurationHandler.java new file mode 100644 index 000000000..ee02dbc07 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/IgnitionConfigurationHandler.java @@ -0,0 +1,76 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; +import java.util.Locale; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration.IgnitionEvent; + +import org.xml.sax.SAXException; + +class IgnitionConfigurationHandler extends AbstractElementHandler { + + private Double ignitionDelay = null; + private IgnitionEvent ignitionEvent = null; + + + public IgnitionConfigurationHandler(DocumentLoadingContext context) { + + } + + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + + public IgnitionConfiguration getConfiguration(IgnitionConfiguration def) { + IgnitionConfiguration config = def.clone(); + if (ignitionEvent != null) { + config.setIgnitionEvent(ignitionEvent); + } + if (ignitionDelay != null) { + config.setIgnitionDelay(ignitionDelay); + } + return config; + } + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + content = content.trim(); + + if (element.equals("ignitionevent")) { + + for (IgnitionEvent e : IgnitionEvent.values()) { + if (e.name().toLowerCase(Locale.ENGLISH).replaceAll("_", "").equals(content)) { + ignitionEvent = e; + break; + } + } + if (ignitionEvent == null) { + warnings.add(Warning.fromString("Unknown ignition event type '" + content + "', ignoring.")); + } + + } else if (element.equals("ignitiondelay")) { + try { + ignitionDelay = Double.parseDouble(content); + } catch (NumberFormatException nfe) { + warnings.add(Warning.fromString("Illegal ignition delay specified, ignoring.")); + } + } else { + super.closeElement(element, attributes, content, warnings); + } + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/IntSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/IntSetter.java new file mode 100644 index 000000000..c3987342e --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/IntSetter.java @@ -0,0 +1,28 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +//// IntSetter - set an integer value +class IntSetter implements Setter { + private final Reflection.Method setMethod; + + public IntSetter(Reflection.Method set) { + setMethod = set; + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + try { + int n = Integer.parseInt(s); + setMethod.invoke(c, n); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/MaterialSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/MaterialSetter.java new file mode 100644 index 000000000..3aae93c04 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/MaterialSetter.java @@ -0,0 +1,73 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; +import java.util.Locale; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.database.Databases; +import net.sf.openrocket.material.Material; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +////MaterialSetter - sets a Material value +class MaterialSetter implements Setter { + private final Reflection.Method setMethod; + private final Material.Type type; + + public MaterialSetter(Reflection.Method set, Material.Type type) { + this.setMethod = set; + this.type = type; + } + + @Override + public void set(RocketComponent c, String name, HashMap attributes, + WarningSet warnings) { + + Material mat; + + // Check name != "" + name = name.trim(); + if (name.equals("")) { + warnings.add(Warning.fromString("Illegal material specification, ignoring.")); + return; + } + + // Parse density + double density; + String str; + str = attributes.remove("density"); + if (str == null) { + warnings.add(Warning.fromString("Illegal material specification, ignoring.")); + return; + } + try { + density = Double.parseDouble(str); + } catch (NumberFormatException e) { + warnings.add(Warning.fromString("Illegal material specification, ignoring.")); + return; + } + + // Parse thickness + // double thickness = 0; + // str = attributes.remove("thickness"); + // try { + // if (str != null) + // thickness = Double.parseDouble(str); + // } catch (NumberFormatException e){ + // warnings.add(Warning.fromString("Illegal material specification, ignoring.")); + // return; + // } + + // Check type if specified + str = attributes.remove("type"); + if (str != null && !type.name().toLowerCase(Locale.ENGLISH).equals(str)) { + warnings.add(Warning.fromString("Illegal material type specified, ignoring.")); + return; + } + + mat = Databases.findMaterial(type, name, density); + + setMethod.invoke(c, mat); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/MotorConfigurationHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/MotorConfigurationHandler.java new file mode 100644 index 000000000..43d08626d --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/MotorConfigurationHandler.java @@ -0,0 +1,71 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.Rocket; + +import org.xml.sax.SAXException; + +class MotorConfigurationHandler extends AbstractElementHandler { + @SuppressWarnings("unused") + private final DocumentLoadingContext context; + private final Rocket rocket; + private String name = null; + private boolean inNameElement = false; + + public MotorConfigurationHandler(Rocket rocket, DocumentLoadingContext context) { + this.rocket = rocket; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (inNameElement || !element.equals("name")) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return null; + } + inNameElement = true; + + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + name = content; + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + String configid = attributes.remove("configid"); + if (configid == null || configid.equals("")) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + if (!rocket.addMotorConfigurationID(configid)) { + warnings.add("Duplicate motor configuration ID used."); + return; + } + + if (name != null && name.trim().length() > 0) { + rocket.setFlightConfigurationName(configid, name); + } + + if ("true".equals(attributes.remove("default"))) { + rocket.getDefaultConfiguration().setFlightConfigurationID(configid); + } + + super.closeElement(element, attributes, content, warnings); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/MotorHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/MotorHandler.java new file mode 100644 index 000000000..dccd1fb89 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/MotorHandler.java @@ -0,0 +1,145 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; +import java.util.Locale; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.motor.Motor; + +import org.xml.sax.SAXException; + +class MotorHandler extends AbstractElementHandler { + /** File version where latest digest format was introduced */ + private static final int MOTOR_DIGEST_VERSION = 104; + + private final DocumentLoadingContext context; + private Motor.Type type = null; + private String manufacturer = null; + private String designation = null; + private String digest = null; + private double diameter = Double.NaN; + private double length = Double.NaN; + private double delay = Double.NaN; + + public MotorHandler(DocumentLoadingContext context) { + this.context = context; + } + + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + + /** + * Return the motor to use, or null. + */ + public Motor getMotor(WarningSet warnings) { + return context.getMotorFinder().findMotor(type, manufacturer, designation, diameter, length, digest, warnings); + } + + /** + * Return the delay to use for the motor. + */ + public double getDelay(WarningSet warnings) { + if (Double.isNaN(delay)) { + warnings.add(Warning.fromString("Motor delay not specified, assuming no ejection charge.")); + return Motor.PLUGGED; + } + return delay; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + content = content.trim(); + + if (element.equals("type")) { + + // Motor type + type = null; + for (Motor.Type t : Motor.Type.values()) { + if (t.name().toLowerCase(Locale.ENGLISH).equals(content.trim())) { + type = t; + break; + } + } + if (type == null) { + warnings.add(Warning.fromString("Unknown motor type '" + content + "', ignoring.")); + } + + } else if (element.equals("manufacturer")) { + + // Manufacturer + manufacturer = content.trim(); + + } else if (element.equals("designation")) { + + // Designation + designation = content.trim(); + + } else if (element.equals("digest")) { + + // Digest is used only for file versions saved using the same digest algorithm + if (context.getFileVersion() >= MOTOR_DIGEST_VERSION) { + digest = content.trim(); + } + + } else if (element.equals("diameter")) { + + // Diameter + diameter = Double.NaN; + try { + diameter = Double.parseDouble(content.trim()); + } catch (NumberFormatException e) { + // Ignore + } + if (Double.isNaN(diameter)) { + warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); + } + + } else if (element.equals("length")) { + + // Length + length = Double.NaN; + try { + length = Double.parseDouble(content.trim()); + } catch (NumberFormatException ignore) { + } + + if (Double.isNaN(length)) { + warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); + } + + } else if (element.equals("delay")) { + + // Delay + delay = Double.NaN; + if (content.equals("none")) { + delay = Motor.PLUGGED; + } else { + try { + delay = Double.parseDouble(content.trim()); + } catch (NumberFormatException ignore) { + } + + if (Double.isNaN(delay)) { + warnings.add(Warning.fromString("Illegal motor delay specified, ignoring.")); + } + + } + + } else { + super.closeElement(element, attributes, content, warnings); + } + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/MotorMountHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/MotorMountHandler.java new file mode 100644 index 000000000..e723edab1 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/MotorMountHandler.java @@ -0,0 +1,129 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; +import java.util.Locale; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; +import net.sf.openrocket.rocketcomponent.MotorConfiguration; +import net.sf.openrocket.rocketcomponent.MotorMount; + +import org.xml.sax.SAXException; + +class MotorMountHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final MotorMount mount; + private MotorHandler motorHandler; + private IgnitionConfigurationHandler ignitionConfigHandler; + + public MotorMountHandler(MotorMount mount, DocumentLoadingContext context) { + this.mount = mount; + this.context = context; + mount.setMotorMount(true); + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("motor")) { + motorHandler = new MotorHandler(context); + return motorHandler; + } + + if (element.equals("ignitionconfiguration")) { + ignitionConfigHandler = new IgnitionConfigurationHandler(context); + return ignitionConfigHandler; + } + + if (element.equals("ignitionevent") || + element.equals("ignitiondelay") || + element.equals("overhang")) { + return PlainTextHandler.INSTANCE; + } + + warnings.add(Warning.fromString("Unknown element '" + element + "' encountered, ignoring.")); + return null; + } + + + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + + if (element.equals("motor")) { + String id = attributes.get("configid"); + if (id == null || id.equals("")) { + warnings.add(Warning.fromString("Illegal motor specification, ignoring.")); + return; + } + + MotorConfiguration config = new MotorConfiguration(); + config.setMotor(motorHandler.getMotor(warnings)); + config.setEjectionDelay(motorHandler.getDelay(warnings)); + mount.getMotorConfiguration().set(id, config); + + return; + } + + if (element.equals("ignitionconfiguration")) { + String id = attributes.get("configid"); + if (id == null || id.equals("")) { + warnings.add(Warning.fromString("Illegal motor specification, ignoring.")); + return; + } + + IgnitionConfiguration def = mount.getIgnitionConfiguration().getDefault(); + mount.getIgnitionConfiguration().set(id, ignitionConfigHandler.getConfiguration(def)); + return; + } + + if (element.equals("ignitionevent")) { + IgnitionConfiguration.IgnitionEvent event = null; + for (IgnitionConfiguration.IgnitionEvent e : IgnitionConfiguration.IgnitionEvent.values()) { + if (e.name().toLowerCase(Locale.ENGLISH).replaceAll("_", "").equals(content)) { + event = e; + break; + } + } + if (event == null) { + warnings.add(Warning.fromString("Unknown ignition event type '" + content + "', ignoring.")); + return; + } + mount.getIgnitionConfiguration().getDefault().setIgnitionEvent(event); + return; + } + + if (element.equals("ignitiondelay")) { + double d; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException nfe) { + warnings.add(Warning.fromString("Illegal ignition delay specified, ignoring.")); + return; + } + mount.getIgnitionConfiguration().getDefault().setIgnitionDelay(d); + return; + } + + if (element.equals("overhang")) { + double d; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException nfe) { + warnings.add(Warning.fromString("Illegal overhang specified, ignoring.")); + return; + } + mount.setMotorOverhang(d); + return; + } + + super.closeElement(element, attributes, content, warnings); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketContentHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketContentHandler.java new file mode 100644 index 000000000..13b88bd5a --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketContentHandler.java @@ -0,0 +1,71 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; + +/** + * Handles the content of the tag. + */ +class OpenRocketContentHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + + private boolean rocketDefined = false; + private boolean simulationsDefined = false; + private boolean datatypesDefined = false; + + public OpenRocketContentHandler(DocumentLoadingContext context) { + this.context = context; + } + + public OpenRocketDocument getDocument() { + if (!rocketDefined) + return null; + return context.getOpenRocketDocument(); + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("rocket")) { + if (rocketDefined) { + warnings.add(Warning + .fromString("Multiple rocket designs within one document, " + + "ignoring later ones.")); + return null; + } + rocketDefined = true; + return new ComponentParameterHandler(getDocument().getRocket(), context); + } + + if (element.equals("datatypes")) { + if (datatypesDefined) { + warnings.add(Warning.fromString("Multiple datatype blocks. Ignoring later ones.")); + return null; + } + datatypesDefined = true; + return new DatatypeHandler(this, context); + } + + if (element.equals("simulations")) { + if (simulationsDefined) { + warnings.add(Warning + .fromString("Multiple simulation definitions within one document, " + + "ignoring later ones.")); + return null; + } + simulationsDefined = true; + return new SimulationsHandler(getDocument(), context); + } + + warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); + + return null; + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketHandler.java new file mode 100644 index 000000000..ccc6329c9 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketHandler.java @@ -0,0 +1,105 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; + +import org.xml.sax.SAXException; + +/** + * The starting point of the handlers. Accepts a single element and hands + * the contents to be read by a OpenRocketContentsHandler. + */ +class OpenRocketHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private OpenRocketContentHandler handler = null; + + public OpenRocketHandler(DocumentLoadingContext context) { + this.context = context; + } + + /** + * Return the OpenRocketDocument read from the file, or null if a document + * has not been read yet. + * + * @return the document read, or null. + */ + public OpenRocketDocument getDocument() { + return handler.getDocument(); + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + // Check for unknown elements + if (!element.equals("openrocket")) { + warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); + return null; + } + + // Check for first call + if (handler != null) { + warnings.add(Warning.fromString("Multiple document elements found, ignoring later " + + "ones.")); + return null; + } + + // Check version number + String version = null; + String creator = attributes.remove("creator"); + String docVersion = attributes.remove("version"); + for (String v : DocumentConfig.SUPPORTED_VERSIONS) { + if (v.equals(docVersion)) { + version = v; + break; + } + } + if (version == null) { + String str = "Unsupported document version"; + if (docVersion != null) + str += " " + docVersion; + if (creator != null && !creator.trim().equals("")) + str += " (written using '" + creator.trim() + "')"; + str += ", attempting to read file anyway."; + warnings.add(str); + } + + context.setFileVersion(parseVersion(docVersion)); + + handler = new OpenRocketContentHandler(context); + return handler; + } + + + private int parseVersion(String docVersion) { + if (docVersion == null) + return 0; + + Matcher m = Pattern.compile("^([0-9]+)\\.([0-9]+)$").matcher(docVersion); + if (m.matches()) { + int major = Integer.parseInt(m.group(1)); + int minor = Integer.parseInt(m.group(2)); + return major * DocumentConfig.FILE_VERSION_DIVISOR + minor; + } else { + return 0; + } + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + attributes.remove("version"); + attributes.remove("creator"); + super.closeElement(element, attributes, content, warnings); + } + + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java index 0cc7ff803..b607df8e0 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java @@ -2,86 +2,19 @@ package net.sf.openrocket.file.openrocket.importt; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import net.sf.openrocket.aerodynamics.Warning; -import net.sf.openrocket.aerodynamics.WarningSet; -import net.sf.openrocket.database.Databases; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; -import net.sf.openrocket.document.Simulation.Status; import net.sf.openrocket.document.StorageOptions; import net.sf.openrocket.file.AbstractRocketLoader; -import net.sf.openrocket.file.MotorFinder; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.RocketLoadException; -import net.sf.openrocket.file.simplesax.AbstractElementHandler; -import net.sf.openrocket.file.simplesax.ElementHandler; -import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.file.simplesax.SimpleSAX; import net.sf.openrocket.logging.LogHelper; -import net.sf.openrocket.material.Material; -import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.preset.ComponentPreset; -import net.sf.openrocket.rocketcomponent.BodyComponent; -import net.sf.openrocket.rocketcomponent.BodyTube; -import net.sf.openrocket.rocketcomponent.Bulkhead; -import net.sf.openrocket.rocketcomponent.CenteringRing; -import net.sf.openrocket.rocketcomponent.ClusterConfiguration; -import net.sf.openrocket.rocketcomponent.Clusterable; -import net.sf.openrocket.rocketcomponent.EllipticalFinSet; -import net.sf.openrocket.rocketcomponent.EngineBlock; -import net.sf.openrocket.rocketcomponent.ExternalComponent; -import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; -import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.FinSet.TabRelativePosition; -import net.sf.openrocket.rocketcomponent.FreeformFinSet; -import net.sf.openrocket.rocketcomponent.IllegalFinPointException; -import net.sf.openrocket.rocketcomponent.InnerTube; -import net.sf.openrocket.rocketcomponent.InternalComponent; -import net.sf.openrocket.rocketcomponent.LaunchLug; -import net.sf.openrocket.rocketcomponent.MassComponent; -import net.sf.openrocket.rocketcomponent.MassObject; -import net.sf.openrocket.rocketcomponent.MotorMount; -import net.sf.openrocket.rocketcomponent.NoseCone; -import net.sf.openrocket.rocketcomponent.Parachute; -import net.sf.openrocket.rocketcomponent.RadiusRingComponent; -import net.sf.openrocket.rocketcomponent.RecoveryDevice; -import net.sf.openrocket.rocketcomponent.ReferenceType; -import net.sf.openrocket.rocketcomponent.RingComponent; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.RocketComponent.Position; -import net.sf.openrocket.rocketcomponent.ShockCord; -import net.sf.openrocket.rocketcomponent.Stage; -import net.sf.openrocket.rocketcomponent.Streamer; -import net.sf.openrocket.rocketcomponent.StructuralComponent; -import net.sf.openrocket.rocketcomponent.SymmetricComponent; -import net.sf.openrocket.rocketcomponent.ThicknessRingComponent; -import net.sf.openrocket.rocketcomponent.Transition; -import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; -import net.sf.openrocket.rocketcomponent.TubeCoupler; -import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.simulation.FlightDataType; -import net.sf.openrocket.simulation.FlightEvent; -import net.sf.openrocket.simulation.FlightEvent.Type; -import net.sf.openrocket.simulation.SimulationOptions; -import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.unit.UnitGroup; -import net.sf.openrocket.util.BugException; -import net.sf.openrocket.util.Color; -import net.sf.openrocket.util.Coordinate; -import net.sf.openrocket.util.GeodeticComputationStrategy; -import net.sf.openrocket.util.LineStyle; -import net.sf.openrocket.util.Reflection; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -102,15 +35,14 @@ public class OpenRocketLoader extends AbstractRocketLoader { @Override - public OpenRocketDocument loadFromStream(InputStream source, MotorFinder motorFinder) throws RocketLoadException, + public void loadFromStream(DocumentLoadingContext context, InputStream source) throws RocketLoadException, IOException { log.info("Loading .ork file"); - DocumentLoadingContext context = new DocumentLoadingContext(); - context.setMotorFinder(motorFinder); InputSource xmlSource = new InputSource(source); OpenRocketHandler handler = new OpenRocketHandler(context); + OpenRocketDocument doc = context.getOpenRocketDocument(); try { SimpleSAX.readXML(xmlSource, handler, warnings); @@ -119,8 +51,6 @@ public class OpenRocketLoader extends AbstractRocketLoader { throw new RocketLoadException("Malformed XML in input.", e); } - - OpenRocketDocument doc = handler.getDocument(); doc.getDefaultConfiguration().setAllStages(); // Deduce suitable time skip @@ -151,2164 +81,10 @@ public class OpenRocketLoader extends AbstractRocketLoader { timeSkip = Math.rint(timeSkip * 100) / 100; doc.getDefaultStorageOptions().setSimulationTimeSkip(timeSkip); - doc.getDefaultStorageOptions().setCompressionEnabled(false); // Set by caller if compressed doc.getDefaultStorageOptions().setExplicitlySet(false); doc.clearUndo(); log.info("Loading done"); - return doc; } } - - - -class DocumentConfig { - - /* Remember to update OpenRocketSaver as well! */ - public static final String[] SUPPORTED_VERSIONS = { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5" }; - - /** - * Divisor used in converting an integer version to the point-represented version. - * The integer version divided by this value is the major version and the remainder is - * the minor version. For example 101 corresponds to file version "1.1". - */ - public static final int FILE_VERSION_DIVISOR = 100; - - - //////// Component constructors - static final HashMap> constructors = new HashMap>(); - static { - try { - // External components - constructors.put("bodytube", BodyTube.class.getConstructor(new Class[0])); - constructors.put("transition", Transition.class.getConstructor(new Class[0])); - constructors.put("nosecone", NoseCone.class.getConstructor(new Class[0])); - constructors.put("trapezoidfinset", TrapezoidFinSet.class.getConstructor(new Class[0])); - constructors.put("ellipticalfinset", EllipticalFinSet.class.getConstructor(new Class[0])); - constructors.put("freeformfinset", FreeformFinSet.class.getConstructor(new Class[0])); - constructors.put("launchlug", LaunchLug.class.getConstructor(new Class[0])); - - // Internal components - constructors.put("engineblock", EngineBlock.class.getConstructor(new Class[0])); - constructors.put("innertube", InnerTube.class.getConstructor(new Class[0])); - constructors.put("tubecoupler", TubeCoupler.class.getConstructor(new Class[0])); - constructors.put("bulkhead", Bulkhead.class.getConstructor(new Class[0])); - constructors.put("centeringring", CenteringRing.class.getConstructor(new Class[0])); - - constructors.put("masscomponent", MassComponent.class.getConstructor(new Class[0])); - constructors.put("shockcord", ShockCord.class.getConstructor(new Class[0])); - constructors.put("parachute", Parachute.class.getConstructor(new Class[0])); - constructors.put("streamer", Streamer.class.getConstructor(new Class[0])); - - // Other - constructors.put("stage", Stage.class.getConstructor(new Class[0])); - - } catch (NoSuchMethodException e) { - throw new BugException( - "Error in constructing the 'constructors' HashMap."); - } - } - - - //////// Parameter setters - /* - * The keys are of the form Class:param, where Class is the class name and param - * the element name. Setters are searched for in descending class order. - * A setter of null means setting the parameter is not allowed. - */ - static final HashMap setters = new HashMap(); - static { - // RocketComponent - setters.put("RocketComponent:name", new StringSetter( - Reflection.findMethod(RocketComponent.class, "setName", String.class))); - setters.put("RocketComponent:color", new ColorSetter( - Reflection.findMethod(RocketComponent.class, "setColor", Color.class))); - setters.put("RocketComponent:linestyle", new EnumSetter( - Reflection.findMethod(RocketComponent.class, "setLineStyle", LineStyle.class), - LineStyle.class)); - setters.put("RocketComponent:position", new PositionSetter()); - setters.put("RocketComponent:overridemass", new OverrideSetter( - Reflection.findMethod(RocketComponent.class, "setOverrideMass", double.class), - Reflection.findMethod(RocketComponent.class, "setMassOverridden", boolean.class))); - setters.put("RocketComponent:overridecg", new OverrideSetter( - Reflection.findMethod(RocketComponent.class, "setOverrideCGX", double.class), - Reflection.findMethod(RocketComponent.class, "setCGOverridden", boolean.class))); - setters.put("RocketComponent:overridesubcomponents", new BooleanSetter( - Reflection.findMethod(RocketComponent.class, "setOverrideSubcomponents", boolean.class))); - setters.put("RocketComponent:comment", new StringSetter( - Reflection.findMethod(RocketComponent.class, "setComment", String.class))); - setters.put("RocketComponent:preset", new ComponentPresetSetter( - Reflection.findMethod(RocketComponent.class, "loadPreset", ComponentPreset.class))); - - // ExternalComponent - setters.put("ExternalComponent:finish", new EnumSetter( - Reflection.findMethod(ExternalComponent.class, "setFinish", Finish.class), - Finish.class)); - setters.put("ExternalComponent:material", new MaterialSetter( - Reflection.findMethod(ExternalComponent.class, "setMaterial", Material.class), - Material.Type.BULK)); - - // BodyComponent - setters.put("BodyComponent:length", new DoubleSetter( - Reflection.findMethod(BodyComponent.class, "setLength", double.class))); - - // SymmetricComponent - setters.put("SymmetricComponent:thickness", new DoubleSetter( - Reflection.findMethod(SymmetricComponent.class, "setThickness", double.class), - "filled", - Reflection.findMethod(SymmetricComponent.class, "setFilled", boolean.class))); - - // BodyTube - setters.put("BodyTube:radius", new DoubleSetter( - Reflection.findMethod(BodyTube.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethod(BodyTube.class, "setOuterRadiusAutomatic", boolean.class))); - - // Transition - setters.put("Transition:shape", new EnumSetter( - Reflection.findMethod(Transition.class, "setType", Transition.Shape.class), - Transition.Shape.class)); - setters.put("Transition:shapeclipped", new BooleanSetter( - Reflection.findMethod(Transition.class, "setClipped", boolean.class))); - setters.put("Transition:shapeparameter", new DoubleSetter( - Reflection.findMethod(Transition.class, "setShapeParameter", double.class))); - - setters.put("Transition:foreradius", new DoubleSetter( - Reflection.findMethod(Transition.class, "setForeRadius", double.class), - "auto", - Reflection.findMethod(Transition.class, "setForeRadiusAutomatic", boolean.class))); - setters.put("Transition:aftradius", new DoubleSetter( - Reflection.findMethod(Transition.class, "setAftRadius", double.class), - "auto", - Reflection.findMethod(Transition.class, "setAftRadiusAutomatic", boolean.class))); - - setters.put("Transition:foreshoulderradius", new DoubleSetter( - Reflection.findMethod(Transition.class, "setForeShoulderRadius", double.class))); - setters.put("Transition:foreshoulderlength", new DoubleSetter( - Reflection.findMethod(Transition.class, "setForeShoulderLength", double.class))); - setters.put("Transition:foreshoulderthickness", new DoubleSetter( - Reflection.findMethod(Transition.class, "setForeShoulderThickness", double.class))); - setters.put("Transition:foreshouldercapped", new BooleanSetter( - Reflection.findMethod(Transition.class, "setForeShoulderCapped", boolean.class))); - - setters.put("Transition:aftshoulderradius", new DoubleSetter( - Reflection.findMethod(Transition.class, "setAftShoulderRadius", double.class))); - setters.put("Transition:aftshoulderlength", new DoubleSetter( - Reflection.findMethod(Transition.class, "setAftShoulderLength", double.class))); - setters.put("Transition:aftshoulderthickness", new DoubleSetter( - Reflection.findMethod(Transition.class, "setAftShoulderThickness", double.class))); - setters.put("Transition:aftshouldercapped", new BooleanSetter( - Reflection.findMethod(Transition.class, "setAftShoulderCapped", boolean.class))); - - // NoseCone - disable disallowed elements - setters.put("NoseCone:foreradius", null); - setters.put("NoseCone:foreshoulderradius", null); - setters.put("NoseCone:foreshoulderlength", null); - setters.put("NoseCone:foreshoulderthickness", null); - setters.put("NoseCone:foreshouldercapped", null); - - // FinSet - setters.put("FinSet:fincount", new IntSetter( - Reflection.findMethod(FinSet.class, "setFinCount", int.class))); - setters.put("FinSet:rotation", new DoubleSetter( - Reflection.findMethod(FinSet.class, "setBaseRotation", double.class), Math.PI / 180.0)); - setters.put("FinSet:thickness", new DoubleSetter( - Reflection.findMethod(FinSet.class, "setThickness", double.class))); - setters.put("FinSet:crosssection", new EnumSetter( - Reflection.findMethod(FinSet.class, "setCrossSection", FinSet.CrossSection.class), - FinSet.CrossSection.class)); - setters.put("FinSet:cant", new DoubleSetter( - Reflection.findMethod(FinSet.class, "setCantAngle", double.class), Math.PI / 180.0)); - setters.put("FinSet:tabheight", new DoubleSetter( - Reflection.findMethod(FinSet.class, "setTabHeight", double.class))); - setters.put("FinSet:tablength", new DoubleSetter( - Reflection.findMethod(FinSet.class, "setTabLength", double.class))); - setters.put("FinSet:tabposition", new FinTabPositionSetter()); - - // TrapezoidFinSet - setters.put("TrapezoidFinSet:rootchord", new DoubleSetter( - Reflection.findMethod(TrapezoidFinSet.class, "setRootChord", double.class))); - setters.put("TrapezoidFinSet:tipchord", new DoubleSetter( - Reflection.findMethod(TrapezoidFinSet.class, "setTipChord", double.class))); - setters.put("TrapezoidFinSet:sweeplength", new DoubleSetter( - Reflection.findMethod(TrapezoidFinSet.class, "setSweep", double.class))); - setters.put("TrapezoidFinSet:height", new DoubleSetter( - Reflection.findMethod(TrapezoidFinSet.class, "setHeight", double.class))); - - // EllipticalFinSet - setters.put("EllipticalFinSet:rootchord", new DoubleSetter( - Reflection.findMethod(EllipticalFinSet.class, "setLength", double.class))); - setters.put("EllipticalFinSet:height", new DoubleSetter( - Reflection.findMethod(EllipticalFinSet.class, "setHeight", double.class))); - - // FreeformFinSet points handled as a special handler - - // LaunchLug - setters.put("LaunchLug:radius", new DoubleSetter( - Reflection.findMethod(LaunchLug.class, "setOuterRadius", double.class))); - setters.put("LaunchLug:length", new DoubleSetter( - Reflection.findMethod(LaunchLug.class, "setLength", double.class))); - setters.put("LaunchLug:thickness", new DoubleSetter( - Reflection.findMethod(LaunchLug.class, "setThickness", double.class))); - setters.put("LaunchLug:radialdirection", new DoubleSetter( - Reflection.findMethod(LaunchLug.class, "setRadialDirection", double.class), - Math.PI / 180.0)); - - // InternalComponent - nothing - - // StructuralComponent - setters.put("StructuralComponent:material", new MaterialSetter( - Reflection.findMethod(StructuralComponent.class, "setMaterial", Material.class), - Material.Type.BULK)); - - // RingComponent - setters.put("RingComponent:length", new DoubleSetter( - Reflection.findMethod(RingComponent.class, "setLength", double.class))); - setters.put("RingComponent:radialposition", new DoubleSetter( - Reflection.findMethod(RingComponent.class, "setRadialPosition", double.class))); - setters.put("RingComponent:radialdirection", new DoubleSetter( - Reflection.findMethod(RingComponent.class, "setRadialDirection", double.class), - Math.PI / 180.0)); - - // ThicknessRingComponent - radius on separate components due to differing automatics - setters.put("ThicknessRingComponent:thickness", new DoubleSetter( - Reflection.findMethod(ThicknessRingComponent.class, "setThickness", double.class))); - - // EngineBlock - setters.put("EngineBlock:outerradius", new DoubleSetter( - Reflection.findMethod(EngineBlock.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethod(EngineBlock.class, "setOuterRadiusAutomatic", boolean.class))); - - // TubeCoupler - setters.put("TubeCoupler:outerradius", new DoubleSetter( - Reflection.findMethod(TubeCoupler.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethod(TubeCoupler.class, "setOuterRadiusAutomatic", boolean.class))); - - // InnerTube - setters.put("InnerTube:outerradius", new DoubleSetter( - Reflection.findMethod(InnerTube.class, "setOuterRadius", double.class))); - setters.put("InnerTube:clusterconfiguration", new ClusterConfigurationSetter()); - setters.put("InnerTube:clusterscale", new DoubleSetter( - Reflection.findMethod(InnerTube.class, "setClusterScale", double.class))); - setters.put("InnerTube:clusterrotation", new DoubleSetter( - Reflection.findMethod(InnerTube.class, "setClusterRotation", double.class), - Math.PI / 180.0)); - - // RadiusRingComponent - - // Bulkhead - setters.put("RadiusRingComponent:innerradius", new DoubleSetter( - Reflection.findMethod(RadiusRingComponent.class, "setInnerRadius", double.class))); - setters.put("Bulkhead:outerradius", new DoubleSetter( - Reflection.findMethod(Bulkhead.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethod(Bulkhead.class, "setOuterRadiusAutomatic", boolean.class))); - - // CenteringRing - setters.put("CenteringRing:innerradius", new DoubleSetter( - Reflection.findMethod(CenteringRing.class, "setInnerRadius", double.class), - "auto", - Reflection.findMethod(CenteringRing.class, "setInnerRadiusAutomatic", boolean.class))); - setters.put("CenteringRing:outerradius", new DoubleSetter( - Reflection.findMethod(CenteringRing.class, "setOuterRadius", double.class), - "auto", - Reflection.findMethod(CenteringRing.class, "setOuterRadiusAutomatic", boolean.class))); - - - // MassObject - setters.put("MassObject:packedlength", new DoubleSetter( - Reflection.findMethod(MassObject.class, "setLength", double.class))); - setters.put("MassObject:packedradius", new DoubleSetter( - Reflection.findMethod(MassObject.class, "setRadius", double.class))); - setters.put("MassObject:radialposition", new DoubleSetter( - Reflection.findMethod(MassObject.class, "setRadialPosition", double.class))); - setters.put("MassObject:radialdirection", new DoubleSetter( - Reflection.findMethod(MassObject.class, "setRadialDirection", double.class), - Math.PI / 180.0)); - - // MassComponent - setters.put("MassComponent:mass", new DoubleSetter( - Reflection.findMethod(MassComponent.class, "setComponentMass", double.class))); - - // ShockCord - setters.put("ShockCord:cordlength", new DoubleSetter( - Reflection.findMethod(ShockCord.class, "setCordLength", double.class))); - setters.put("ShockCord:material", new MaterialSetter( - Reflection.findMethod(ShockCord.class, "setMaterial", Material.class), - Material.Type.LINE)); - - // RecoveryDevice - setters.put("RecoveryDevice:cd", new DoubleSetter( - Reflection.findMethod(RecoveryDevice.class, "setCD", double.class), - "auto", - Reflection.findMethod(RecoveryDevice.class, "setCDAutomatic", boolean.class))); - setters.put("RecoveryDevice:deployevent", new EnumSetter( - Reflection.findMethod(RecoveryDevice.class, "setDeployEvent", RecoveryDevice.DeployEvent.class), - RecoveryDevice.DeployEvent.class)); - setters.put("RecoveryDevice:deployaltitude", new DoubleSetter( - Reflection.findMethod(RecoveryDevice.class, "setDeployAltitude", double.class))); - setters.put("RecoveryDevice:deploydelay", new DoubleSetter( - Reflection.findMethod(RecoveryDevice.class, "setDeployDelay", double.class))); - setters.put("RecoveryDevice:material", new MaterialSetter( - Reflection.findMethod(RecoveryDevice.class, "setMaterial", Material.class), - Material.Type.SURFACE)); - - // Parachute - setters.put("Parachute:diameter", new DoubleSetter( - Reflection.findMethod(Parachute.class, "setDiameter", double.class))); - setters.put("Parachute:linecount", new IntSetter( - Reflection.findMethod(Parachute.class, "setLineCount", int.class))); - setters.put("Parachute:linelength", new DoubleSetter( - Reflection.findMethod(Parachute.class, "setLineLength", double.class))); - setters.put("Parachute:linematerial", new MaterialSetter( - Reflection.findMethod(Parachute.class, "setLineMaterial", Material.class), - Material.Type.LINE)); - - // Streamer - setters.put("Streamer:striplength", new DoubleSetter( - Reflection.findMethod(Streamer.class, "setStripLength", double.class))); - setters.put("Streamer:stripwidth", new DoubleSetter( - Reflection.findMethod(Streamer.class, "setStripWidth", double.class))); - - // Rocket - // handled by separate handler - setters.put("Rocket:referencetype", new EnumSetter( - Reflection.findMethod(Rocket.class, "setReferenceType", ReferenceType.class), - ReferenceType.class)); - setters.put("Rocket:customreference", new DoubleSetter( - Reflection.findMethod(Rocket.class, "setCustomReferenceLength", double.class))); - setters.put("Rocket:designer", new StringSetter( - Reflection.findMethod(Rocket.class, "setDesigner", String.class))); - setters.put("Rocket:revision", new StringSetter( - Reflection.findMethod(Rocket.class, "setRevision", String.class))); - - // Stage - setters.put("Stage:separationevent", new EnumSetter( - Reflection.findMethod(Stage.class, "setSeparationEvent", Stage.SeparationEvent.class), - Stage.SeparationEvent.class)); - setters.put("Stage:separationdelay", new DoubleSetter( - Reflection.findMethod(Stage.class, "setSeparationDelay", double.class))); - - } - - - /** - * Search for a enum value that has the corresponding name as an XML value. The current - * conversion from enum name to XML value is to lowercase the name and strip out all - * underscore characters. This method returns a match to these criteria, or null - * if no such enum exists. - * - * @param then enum type. - * @param name the XML value, null ok. - * @param enumClass the class of the enum. - * @return the found enum value, or null. - */ - public static > Enum findEnum(String name, - Class> enumClass) { - - if (name == null) - return null; - name = name.trim(); - for (Enum e : enumClass.getEnumConstants()) { - if (e.name().toLowerCase(Locale.ENGLISH).replace("_", "").equals(name)) { - return e; - } - } - return null; - } - - - /** - * Convert a string to a double including formatting specifications of the OpenRocket - * file format. This accepts all formatting that is valid for - * Double.parseDouble(s) and a few others as well ("Inf", "-Inf"). - * - * @param s the string to parse. - * @return the numerical value. - * @throws NumberFormatException the the string cannot be parsed. - */ - public static double stringToDouble(String s) throws NumberFormatException { - if (s == null) - throw new NumberFormatException("null string"); - if (s.equalsIgnoreCase("NaN")) - return Double.NaN; - if (s.equalsIgnoreCase("Inf")) - return Double.POSITIVE_INFINITY; - if (s.equalsIgnoreCase("-Inf")) - return Double.NEGATIVE_INFINITY; - return Double.parseDouble(s); - } -} - - - - - -/** - * The starting point of the handlers. Accepts a single element and hands - * the contents to be read by a OpenRocketContentsHandler. - */ -class OpenRocketHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private OpenRocketContentHandler handler = null; - - public OpenRocketHandler(DocumentLoadingContext context) { - this.context = context; - } - - /** - * Return the OpenRocketDocument read from the file, or null if a document - * has not been read yet. - * - * @return the document read, or null. - */ - public OpenRocketDocument getDocument() { - return handler.getDocument(); - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - // Check for unknown elements - if (!element.equals("openrocket")) { - warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); - return null; - } - - // Check for first call - if (handler != null) { - warnings.add(Warning.fromString("Multiple document elements found, ignoring later " - + "ones.")); - return null; - } - - // Check version number - String version = null; - String creator = attributes.remove("creator"); - String docVersion = attributes.remove("version"); - for (String v : DocumentConfig.SUPPORTED_VERSIONS) { - if (v.equals(docVersion)) { - version = v; - break; - } - } - if (version == null) { - String str = "Unsupported document version"; - if (docVersion != null) - str += " " + docVersion; - if (creator != null && !creator.trim().equals("")) - str += " (written using '" + creator.trim() + "')"; - str += ", attempting to read file anyway."; - warnings.add(str); - } - - context.setFileVersion(parseVersion(docVersion)); - - handler = new OpenRocketContentHandler(context); - return handler; - } - - - private int parseVersion(String docVersion) { - if (docVersion == null) - return 0; - - Matcher m = Pattern.compile("^([0-9]+)\\.([0-9]+)$").matcher(docVersion); - if (m.matches()) { - int major = Integer.parseInt(m.group(1)); - int minor = Integer.parseInt(m.group(2)); - return major * DocumentConfig.FILE_VERSION_DIVISOR + minor; - } else { - return 0; - } - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - attributes.remove("version"); - attributes.remove("creator"); - super.closeElement(element, attributes, content, warnings); - } - - -} - - -/** - * Handles the content of the tag. - */ -class OpenRocketContentHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final OpenRocketDocument doc; - private final Rocket rocket; - - private boolean rocketDefined = false; - private boolean simulationsDefined = false; - private boolean datatypesDefined = false; - - public OpenRocketContentHandler(DocumentLoadingContext context) { - this.context = context; - this.rocket = new Rocket(); - this.doc = new OpenRocketDocument(rocket); - } - - public OpenRocketDocument getDocument() { - if (!rocketDefined) - return null; - return doc; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("rocket")) { - if (rocketDefined) { - warnings.add(Warning - .fromString("Multiple rocket designs within one document, " - + "ignoring later ones.")); - return null; - } - rocketDefined = true; - return new ComponentParameterHandler(rocket, context); - } - - if (element.equals("datatypes")) { - if (datatypesDefined) { - warnings.add(Warning.fromString("Multiple datatype blocks. Ignoring later ones.")); - return null; - } - datatypesDefined = true; - return new DatatypeHandler(this, context); - } - - if (element.equals("simulations")) { - if (simulationsDefined) { - warnings.add(Warning - .fromString("Multiple simulation definitions within one document, " - + "ignoring later ones.")); - return null; - } - simulationsDefined = true; - return new SimulationsHandler(doc, context); - } - - warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); - - return null; - } -} - -class DatatypeHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final OpenRocketContentHandler contentHandler; - private CustomExpressionHandler customExpressionHandler = null; - - public DatatypeHandler(OpenRocketContentHandler contentHandler, DocumentLoadingContext context) { - this.context = context; - this.contentHandler = contentHandler; - } - - @Override - public ElementHandler openElement(String element, - HashMap attributes, WarningSet warnings) - throws SAXException { - - if (element.equals("type") && attributes.get("source").equals("customexpression")) { - customExpressionHandler = new CustomExpressionHandler(contentHandler, context); - return customExpressionHandler; - } - else { - warnings.add(Warning.fromString("Unknown datatype " + element + " defined, ignoring")); - } - - return this; - } - - @Override - public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { - attributes.remove("source"); - super.closeElement(element, attributes, content, warnings); - - if (customExpressionHandler != null) { - contentHandler.getDocument().addCustomExpression(customExpressionHandler.currentExpression); - } - - } - -} - -class CustomExpressionHandler extends AbstractElementHandler { - @SuppressWarnings("unused") - private final DocumentLoadingContext context; - private final OpenRocketContentHandler contentHandler; - public CustomExpression currentExpression; - - public CustomExpressionHandler(OpenRocketContentHandler contentHandler, DocumentLoadingContext context) { - this.context = context; - this.contentHandler = contentHandler; - currentExpression = new CustomExpression(contentHandler.getDocument()); - - } - - @Override - public ElementHandler openElement(String element, - HashMap attributes, WarningSet warnings) - throws SAXException { - - return this; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - if (element.equals("type")) { - contentHandler.getDocument().addCustomExpression(currentExpression); - } - - if (element.equals("name")) { - currentExpression.setName(content); - } - - if (element.equals("symbol")) { - currentExpression.setSymbol(content); - } - - if (element.equals("unit") && attributes.get("unittype").equals("auto")) { - currentExpression.setUnit(content); - } - - if (element.equals("expression")) { - currentExpression.setExpression(content); - } - } -} - -/** - * A handler that creates components from the corresponding elements. The control of the - * contents is passed on to ComponentParameterHandler. - */ -class ComponentHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final RocketComponent parent; - - public ComponentHandler(RocketComponent parent, DocumentLoadingContext context) { - this.parent = parent; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - // Attempt to construct new component - Constructor constructor = DocumentConfig.constructors - .get(element); - if (constructor == null) { - warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); - return null; - } - - RocketComponent c; - try { - c = constructor.newInstance(); - } catch (InstantiationException e) { - throw new BugException("Error constructing component.", e); - } catch (IllegalAccessException e) { - throw new BugException("Error constructing component.", e); - } catch (InvocationTargetException e) { - throw Reflection.handleWrappedException(e); - } - - parent.addChild(c); - - return new ComponentParameterHandler(c, context); - } -} - - -/** - * A handler that populates the parameters of a previously constructed rocket component. - * This uses the setters, or delegates the handling to another handler for specific - * elements. - */ -class ComponentParameterHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final RocketComponent component; - - public ComponentParameterHandler(RocketComponent c, DocumentLoadingContext context) { - this.component = c; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - // Check for specific elements that contain other elements - if (element.equals("subcomponents")) { - return new ComponentHandler(component, context); - } - if (element.equals("motormount")) { - if (!(component instanceof MotorMount)) { - warnings.add(Warning.fromString("Illegal component defined as motor mount.")); - return null; - } - return new MotorMountHandler((MotorMount) component, context); - } - if (element.equals("finpoints")) { - if (!(component instanceof FreeformFinSet)) { - warnings.add(Warning.fromString("Illegal component defined for fin points.")); - return null; - } - return new FinSetPointHandler((FreeformFinSet) component, context); - } - if (element.equals("motorconfiguration")) { - if (!(component instanceof Rocket)) { - warnings.add(Warning.fromString("Illegal component defined for motor configuration.")); - return null; - } - return new MotorConfigurationHandler((Rocket) component, context); - } - - - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("subcomponents") || element.equals("motormount") || - element.equals("finpoints") || element.equals("motorconfiguration")) { - return; - } - - // Search for the correct setter class - - Class c; - for (c = component.getClass(); c != null; c = c.getSuperclass()) { - String setterKey = c.getSimpleName() + ":" + element; - Setter s = DocumentConfig.setters.get(setterKey); - if (s != null) { - // Setter found - s.set(component, content, attributes, warnings); - break; - } - if (DocumentConfig.setters.containsKey(setterKey)) { - // Key exists but is null -> invalid parameter - c = null; - break; - } - } - if (c == null) { - warnings.add(Warning.fromString("Unknown parameter type '" + element + "' for " - + component.getComponentName() + ", ignoring.")); - } - } -} - - -/** - * A handler that reads the specifications within the freeformfinset's - * elements. - */ -class FinSetPointHandler extends AbstractElementHandler { - @SuppressWarnings("unused") - private final DocumentLoadingContext context; - private final FreeformFinSet finset; - private final ArrayList coordinates = new ArrayList(); - - public FinSetPointHandler(FreeformFinSet finset, DocumentLoadingContext context) { - this.finset = finset; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - String strx = attributes.remove("x"); - String stry = attributes.remove("y"); - if (strx == null || stry == null) { - warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); - return; - } - try { - double x = Double.parseDouble(strx); - double y = Double.parseDouble(stry); - coordinates.add(new Coordinate(x, y)); - } catch (NumberFormatException e) { - warnings.add(Warning.fromString("Illegal fin points specification, ignoring.")); - return; - } - - super.closeElement(element, attributes, content, warnings); - } - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) { - try { - finset.setPoints(coordinates.toArray(new Coordinate[0])); - } catch (IllegalFinPointException e) { - warnings.add(Warning.fromString("Freeform fin set point definitions illegal, ignoring.")); - } - } -} - - -class MotorMountHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final MotorMount mount; - private MotorHandler motorHandler; - - public MotorMountHandler(MotorMount mount, DocumentLoadingContext context) { - this.mount = mount; - this.context = context; - mount.setMotorMount(true); - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("motor")) { - motorHandler = new MotorHandler(context); - return motorHandler; - } - - if (element.equals("ignitionevent") || - element.equals("ignitiondelay") || - element.equals("overhang")) { - return PlainTextHandler.INSTANCE; - } - - warnings.add(Warning.fromString("Unknown element '" + element + "' encountered, ignoring.")); - return null; - } - - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - if (element.equals("motor")) { - String id = attributes.get("configid"); - if (id == null || id.equals("")) { - warnings.add(Warning.fromString("Illegal motor specification, ignoring.")); - return; - } - - Motor motor = motorHandler.getMotor(warnings); - mount.setMotor(id, motor); - mount.setMotorDelay(id, motorHandler.getDelay(warnings)); - return; - } - - if (element.equals("ignitionevent")) { - MotorMount.IgnitionEvent event = null; - for (MotorMount.IgnitionEvent e : MotorMount.IgnitionEvent.values()) { - if (e.name().toLowerCase(Locale.ENGLISH).replaceAll("_", "").equals(content)) { - event = e; - break; - } - } - if (event == null) { - warnings.add(Warning.fromString("Unknown ignition event type '" + content + "', ignoring.")); - return; - } - mount.setIgnitionEvent(event); - return; - } - - if (element.equals("ignitiondelay")) { - double d; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException nfe) { - warnings.add(Warning.fromString("Illegal ignition delay specified, ignoring.")); - return; - } - mount.setIgnitionDelay(d); - return; - } - - if (element.equals("overhang")) { - double d; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException nfe) { - warnings.add(Warning.fromString("Illegal overhang specified, ignoring.")); - return; - } - mount.setMotorOverhang(d); - return; - } - - super.closeElement(element, attributes, content, warnings); - } -} - - - - -class MotorConfigurationHandler extends AbstractElementHandler { - @SuppressWarnings("unused") - private final DocumentLoadingContext context; - private final Rocket rocket; - private String name = null; - private boolean inNameElement = false; - - public MotorConfigurationHandler(Rocket rocket, DocumentLoadingContext context) { - this.rocket = rocket; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (inNameElement || !element.equals("name")) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return null; - } - inNameElement = true; - - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - name = content; - } - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - String configid = attributes.remove("configid"); - if (configid == null || configid.equals("")) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - if (!rocket.addMotorConfigurationID(configid)) { - warnings.add("Duplicate motor configuration ID used."); - return; - } - - if (name != null && name.trim().length() > 0) { - rocket.setMotorConfigurationName(configid, name); - } - - if ("true".equals(attributes.remove("default"))) { - rocket.getDefaultConfiguration().setMotorConfigurationID(configid); - } - - super.closeElement(element, attributes, content, warnings); - } -} - - -class MotorHandler extends AbstractElementHandler { - /** File version where latest digest format was introduced */ - private static final int MOTOR_DIGEST_VERSION = 104; - - private final DocumentLoadingContext context; - private Motor.Type type = null; - private String manufacturer = null; - private String designation = null; - private String digest = null; - private double diameter = Double.NaN; - private double length = Double.NaN; - private double delay = Double.NaN; - - public MotorHandler(DocumentLoadingContext context) { - this.context = context; - } - - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - - /** - * Return the motor to use, or null. - */ - public Motor getMotor(WarningSet warnings) { - return context.getMotorFinder().findMotor(type, manufacturer, designation, diameter, length, digest, warnings); - } - - /** - * Return the delay to use for the motor. - */ - public double getDelay(WarningSet warnings) { - if (Double.isNaN(delay)) { - warnings.add(Warning.fromString("Motor delay not specified, assuming no ejection charge.")); - return Motor.PLUGGED; - } - return delay; - } - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - content = content.trim(); - - if (element.equals("type")) { - - // Motor type - type = null; - for (Motor.Type t : Motor.Type.values()) { - if (t.name().toLowerCase(Locale.ENGLISH).equals(content.trim())) { - type = t; - break; - } - } - if (type == null) { - warnings.add(Warning.fromString("Unknown motor type '" + content + "', ignoring.")); - } - - } else if (element.equals("manufacturer")) { - - // Manufacturer - manufacturer = content.trim(); - - } else if (element.equals("designation")) { - - // Designation - designation = content.trim(); - - } else if (element.equals("digest")) { - - // Digest is used only for file versions saved using the same digest algorithm - if (context.getFileVersion() >= MOTOR_DIGEST_VERSION) { - digest = content.trim(); - } - - } else if (element.equals("diameter")) { - - // Diameter - diameter = Double.NaN; - try { - diameter = Double.parseDouble(content.trim()); - } catch (NumberFormatException e) { - // Ignore - } - if (Double.isNaN(diameter)) { - warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); - } - - } else if (element.equals("length")) { - - // Length - length = Double.NaN; - try { - length = Double.parseDouble(content.trim()); - } catch (NumberFormatException ignore) { - } - - if (Double.isNaN(length)) { - warnings.add(Warning.fromString("Illegal motor diameter specified, ignoring.")); - } - - } else if (element.equals("delay")) { - - // Delay - delay = Double.NaN; - if (content.equals("none")) { - delay = Motor.PLUGGED; - } else { - try { - delay = Double.parseDouble(content.trim()); - } catch (NumberFormatException ignore) { - } - - if (Double.isNaN(delay)) { - warnings.add(Warning.fromString("Illegal motor delay specified, ignoring.")); - } - - } - - } else { - super.closeElement(element, attributes, content, warnings); - } - } - -} - - - -class SimulationsHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private final OpenRocketDocument doc; - private SingleSimulationHandler handler; - - public SimulationsHandler(OpenRocketDocument doc, DocumentLoadingContext context) { - this.doc = doc; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (!element.equals("simulation")) { - warnings.add("Unknown element '" + element + "', ignoring."); - return null; - } - - handler = new SingleSimulationHandler(doc, context); - return handler; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - attributes.remove("status"); - - //Finished loading. Rebuilding custom expressions in case something has changed such as listener variable come available. - for (CustomExpression exp : doc.getCustomExpressions()){ - exp.setExpression(exp.getExpressionString()); - } - - super.closeElement(element, attributes, content, warnings); - } -} - -class SingleSimulationHandler extends AbstractElementHandler { - - private final DocumentLoadingContext context; - - private final OpenRocketDocument doc; - - private String name; - - private SimulationConditionsHandler conditionHandler; - private FlightDataHandler dataHandler; - - private final List listeners = new ArrayList(); - - public SingleSimulationHandler(OpenRocketDocument doc, DocumentLoadingContext context) { - this.doc = doc; - this.context = context; - } - - public OpenRocketDocument getDocument() { - return doc; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("name") || element.equals("simulator") || - element.equals("calculator") || element.equals("listener")) { - return PlainTextHandler.INSTANCE; - } else if (element.equals("conditions")) { - conditionHandler = new SimulationConditionsHandler(doc.getRocket(), context); - return conditionHandler; - } else if (element.equals("flightdata")) { - dataHandler = new FlightDataHandler(this, context); - return dataHandler; - } else { - warnings.add("Unknown element '" + element + "', ignoring."); - return null; - } - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("name")) { - name = content; - } else if (element.equals("simulator")) { - if (!content.trim().equals("RK4Simulator")) { - warnings.add("Unknown simulator '" + content.trim() + "' specified, ignoring."); - } - } else if (element.equals("calculator")) { - if (!content.trim().equals("BarrowmanCalculator")) { - warnings.add("Unknown calculator '" + content.trim() + "' specified, ignoring."); - } - } else if (element.equals("listener") && content.trim().length() > 0) { - listeners.add(content.trim()); - } - - } - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) { - - String s = attributes.get("status"); - Simulation.Status status = (Status) DocumentConfig.findEnum(s, Simulation.Status.class); - if (status == null) { - warnings.add("Simulation status unknown, assuming outdated."); - status = Simulation.Status.OUTDATED; - } - - SimulationOptions conditions; - if (conditionHandler != null) { - conditions = conditionHandler.getConditions(); - } else { - warnings.add("Simulation conditions not defined, using defaults."); - conditions = new SimulationOptions(doc.getRocket()); - } - - if (name == null) - name = "Simulation"; - - FlightData data; - if (dataHandler == null) - data = null; - else - data = dataHandler.getFlightData(); - - Simulation simulation = new Simulation(doc.getRocket(), status, name, - conditions, listeners, data); - - doc.addSimulation(simulation); - } -} - -class SimulationConditionsHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - private SimulationOptions conditions; - private AtmosphereHandler atmosphereHandler; - - public SimulationConditionsHandler(Rocket rocket, DocumentLoadingContext context) { - this.context = context; - conditions = new SimulationOptions(rocket); - // Set up default loading settings (which may differ from the new defaults) - conditions.setGeodeticComputation(GeodeticComputationStrategy.FLAT); - } - - public SimulationOptions getConditions() { - return conditions; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - if (element.equals("atmosphere")) { - atmosphereHandler = new AtmosphereHandler(attributes.get("model"), context); - return atmosphereHandler; - } - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - double d = Double.NaN; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException ignore) { - } - - - if (element.equals("configid")) { - if (content.equals("")) { - conditions.setMotorConfigurationID(null); - } else { - conditions.setMotorConfigurationID(content); - } - } else if (element.equals("launchrodlength")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch rod length defined, ignoring."); - } else { - conditions.setLaunchRodLength(d); - } - } else if (element.equals("launchrodangle")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch rod angle defined, ignoring."); - } else { - conditions.setLaunchRodAngle(d * Math.PI / 180); - } - } else if (element.equals("launchroddirection")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch rod direction defined, ignoring."); - } else { - conditions.setLaunchRodDirection(d * Math.PI / 180); - } - } else if (element.equals("windaverage")) { - if (Double.isNaN(d)) { - warnings.add("Illegal average windspeed defined, ignoring."); - } else { - conditions.setWindSpeedAverage(d); - } - } else if (element.equals("windturbulence")) { - if (Double.isNaN(d)) { - warnings.add("Illegal wind turbulence intensity defined, ignoring."); - } else { - conditions.setWindTurbulenceIntensity(d); - } - } else if (element.equals("launchaltitude")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch altitude defined, ignoring."); - } else { - conditions.setLaunchAltitude(d); - } - } else if (element.equals("launchlatitude")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch latitude defined, ignoring."); - } else { - conditions.setLaunchLatitude(d); - } - } else if (element.equals("launchlongitude")) { - if (Double.isNaN(d)) { - warnings.add("Illegal launch longitude."); - } else { - conditions.setLaunchLongitude(d); - } - } else if (element.equals("geodeticmethod")) { - GeodeticComputationStrategy gcs = - (GeodeticComputationStrategy) DocumentConfig.findEnum(content, GeodeticComputationStrategy.class); - if (gcs != null) { - conditions.setGeodeticComputation(gcs); - } else { - warnings.add("Unknown geodetic computation method '" + content + "'"); - } - } else if (element.equals("atmosphere")) { - atmosphereHandler.storeSettings(conditions, warnings); - } else if (element.equals("timestep")) { - if (Double.isNaN(d) || d <= 0 ) { - warnings.add("Illegal time step defined, ignoring."); - } else { - conditions.setTimeStep(d); - } - } - } -} - - -class AtmosphereHandler extends AbstractElementHandler { - @SuppressWarnings("unused") - private final DocumentLoadingContext context; - private final String model; - private double temperature = Double.NaN; - private double pressure = Double.NaN; - - public AtmosphereHandler(String model, DocumentLoadingContext context) { - this.model = model; - this.context = context; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - - double d = Double.NaN; - try { - d = Double.parseDouble(content); - } catch (NumberFormatException ignore) { - } - - if (element.equals("basetemperature")) { - if (Double.isNaN(d)) { - warnings.add("Illegal base temperature specified, ignoring."); - } - temperature = d; - } else if (element.equals("basepressure")) { - if (Double.isNaN(d)) { - warnings.add("Illegal base pressure specified, ignoring."); - } - pressure = d; - } else { - super.closeElement(element, attributes, content, warnings); - } - } - - - public void storeSettings(SimulationOptions cond, WarningSet warnings) { - if (!Double.isNaN(pressure)) { - cond.setLaunchPressure(pressure); - } - if (!Double.isNaN(temperature)) { - cond.setLaunchTemperature(temperature); - } - - if ("isa".equals(model)) { - cond.setISAAtmosphere(true); - } else if ("extendedisa".equals(model)) { - cond.setISAAtmosphere(false); - } else { - cond.setISAAtmosphere(true); - warnings.add("Unknown atmospheric model, using ISA."); - } - } - -} - - -class FlightDataHandler extends AbstractElementHandler { - private final DocumentLoadingContext context; - - private FlightDataBranchHandler dataHandler; - private WarningSet warningSet = new WarningSet(); - private List branches = new ArrayList(); - - private SingleSimulationHandler simHandler; - private FlightData data; - - - public FlightDataHandler(SingleSimulationHandler simHandler, DocumentLoadingContext context) { - this.context = context; - this.simHandler = simHandler; - } - - public FlightData getFlightData() { - return data; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("warning")) { - return PlainTextHandler.INSTANCE; - } - if (element.equals("databranch")) { - if (attributes.get("name") == null || attributes.get("types") == null) { - warnings.add("Illegal flight data definition, ignoring."); - return null; - } - dataHandler = new FlightDataBranchHandler(attributes.get("name"), - attributes.get("types"), - simHandler, context); - return dataHandler; - } - - warnings.add("Unknown element '" + element + "' encountered, ignoring."); - return null; - } - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("databranch")) { - FlightDataBranch branch = dataHandler.getBranch(); - if (branch.getLength() > 0) { - branches.add(branch); - } - } else if (element.equals("warning")) { - warningSet.add(Warning.fromString(content)); - } - } - - - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (branches.size() > 0) { - data = new FlightData(branches.toArray(new FlightDataBranch[0])); - } else { - double maxAltitude = Double.NaN; - double maxVelocity = Double.NaN; - double maxAcceleration = Double.NaN; - double maxMach = Double.NaN; - double timeToApogee = Double.NaN; - double flightTime = Double.NaN; - double groundHitVelocity = Double.NaN; - double launchRodVelocity = Double.NaN; - double deploymentVelocity = Double.NaN; - - try { - maxAltitude = DocumentConfig.stringToDouble(attributes.get("maxaltitude")); - } catch (NumberFormatException ignore) { - } - try { - maxVelocity = DocumentConfig.stringToDouble(attributes.get("maxvelocity")); - } catch (NumberFormatException ignore) { - } - try { - maxAcceleration = DocumentConfig.stringToDouble(attributes.get("maxacceleration")); - } catch (NumberFormatException ignore) { - } - try { - maxMach = DocumentConfig.stringToDouble(attributes.get("maxmach")); - } catch (NumberFormatException ignore) { - } - try { - timeToApogee = DocumentConfig.stringToDouble(attributes.get("timetoapogee")); - } catch (NumberFormatException ignore) { - } - try { - flightTime = DocumentConfig.stringToDouble(attributes.get("flighttime")); - } catch (NumberFormatException ignore) { - } - try { - groundHitVelocity = - DocumentConfig.stringToDouble(attributes.get("groundhitvelocity")); - } catch (NumberFormatException ignore) { - } - try { - launchRodVelocity = DocumentConfig.stringToDouble(attributes.get("launchrodvelocity")); - } catch (NumberFormatException ignore) { - } - try { - deploymentVelocity = DocumentConfig.stringToDouble(attributes.get("deploymentvelocity")); - } catch (NumberFormatException ignore) { - } - - data = new FlightData(maxAltitude, maxVelocity, maxAcceleration, maxMach, - timeToApogee, flightTime, groundHitVelocity, launchRodVelocity, deploymentVelocity); - } - - data.getWarningSet().addAll(warningSet); - data.immute(); - } - - -} - - -class FlightDataBranchHandler extends AbstractElementHandler { - @SuppressWarnings("unused") - private final DocumentLoadingContext context; - private final FlightDataType[] types; - private final FlightDataBranch branch; - - private static final LogHelper log = Application.getLogger(); - private final SingleSimulationHandler simHandler; - - public FlightDataBranchHandler(String name, String typeList, SingleSimulationHandler simHandler, DocumentLoadingContext context) { - this.simHandler = simHandler; - this.context = context; - String[] split = typeList.split(","); - types = new FlightDataType[split.length]; - for (int i = 0; i < split.length; i++) { - String typeName = split[i]; - FlightDataType matching = findFlightDataType(typeName); - types[i] = matching; - //types[i] = FlightDataType.getType(typeName, matching.getSymbol(), matching.getUnitGroup()); - } - - // TODO: LOW: May throw an IllegalArgumentException - branch = new FlightDataBranch(name, types); - } - - // Find the full flight data type given name only - // Note: this way of doing it requires that custom expressions always come before flight data in the file, - // not the nicest but this is always the case anyway. - private FlightDataType findFlightDataType(String name) { - - // Kevins version with lookup by key. Not using right now - /* - if ( key != null ) { - for (FlightDataType t : FlightDataType.ALL_TYPES){ - if (t.getKey().equals(key) ){ - return t; - } - } - } - */ - - // Look in built in types - for (FlightDataType t : FlightDataType.ALL_TYPES) { - if (t.getName().equals(name)) { - return t; - } - } - - // Look in custom expressions - for (CustomExpression exp : simHandler.getDocument().getCustomExpressions()) { - if (exp.getName().equals(name)) { - return exp.getType(); - } - } - - log.warn("Could not find the flight data type '" + name + "' used in the XML file. Substituted type with unknown symbol and units."); - return FlightDataType.getType(name, "Unknown", UnitGroup.UNITS_NONE); - } - - public FlightDataBranch getBranch() { - branch.immute(); - return branch; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - if (element.equals("datapoint")) - return PlainTextHandler.INSTANCE; - if (element.equals("event")) - return PlainTextHandler.INSTANCE; - - warnings.add("Unknown element '" + element + "' encountered, ignoring."); - return null; - } - - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) { - - if (element.equals("event")) { - double time; - FlightEvent.Type type; - - try { - time = DocumentConfig.stringToDouble(attributes.get("time")); - } catch (NumberFormatException e) { - warnings.add("Illegal event specification, ignoring."); - return; - } - - type = (Type) DocumentConfig.findEnum(attributes.get("type"), FlightEvent.Type.class); - if (type == null) { - warnings.add("Illegal event specification, ignoring."); - return; - } - - branch.addEvent(new FlightEvent(type, time)); - return; - } - - if (!element.equals("datapoint")) { - warnings.add("Unknown element '" + element + "' encountered, ignoring."); - return; - } - - // element == "datapoint" - - - // Check line format - String[] split = content.split(","); - if (split.length != types.length) { - warnings.add("Data point did not contain correct amount of values, ignoring point."); - return; - } - - // Parse the doubles - double[] values = new double[split.length]; - for (int i = 0; i < values.length; i++) { - try { - values[i] = DocumentConfig.stringToDouble(split[i]); - } catch (NumberFormatException e) { - warnings.add("Data point format error, ignoring point."); - return; - } - } - - // Add point to branch - branch.addPoint(); - for (int i = 0; i < types.length; i++) { - branch.setValue(types[i], values[i]); - } - } -} - - - - - -///////////////// Setters implementation - - -//// Interface -interface Setter { - /** - * Set the specified value to the given component. - * - * @param component the component to which to set. - * @param value the value within the element. - * @param attributes attributes for the element. - * @param warnings the warning set to use. - */ - public void set(RocketComponent component, String value, - HashMap attributes, WarningSet warnings); -} - - -//// StringSetter - sets the value to the contained String -class StringSetter implements Setter { - private final Reflection.Method setMethod; - - public StringSetter(Reflection.Method set) { - setMethod = set; - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - setMethod.invoke(c, s); - } -} - -//// IntSetter - set an integer value -class IntSetter implements Setter { - private final Reflection.Method setMethod; - - public IntSetter(Reflection.Method set) { - setMethod = set; - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - try { - int n = Integer.parseInt(s); - setMethod.invoke(c, n); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - - -//// BooleanSetter - set a boolean value -class BooleanSetter implements Setter { - private final Reflection.Method setMethod; - - public BooleanSetter(Reflection.Method set) { - setMethod = set; - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - s = s.trim(); - if (s.equalsIgnoreCase("true")) { - setMethod.invoke(c, true); - } else if (s.equalsIgnoreCase("false")) { - setMethod.invoke(c, false); - } else { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - - - -//// DoubleSetter - sets a double value or (alternatively) if a specific string is encountered -//// calls a setXXX(boolean) method. -class DoubleSetter implements Setter { - private final Reflection.Method setMethod; - private final String specialString; - private final Reflection.Method specialMethod; - private final double multiplier; - - /** - * Set only the double value. - * @param set set method for the double value. - */ - public DoubleSetter(Reflection.Method set) { - this.setMethod = set; - this.specialString = null; - this.specialMethod = null; - this.multiplier = 1.0; - } - - /** - * Multiply with the given multiplier and set the double value. - * @param set set method for the double value. - * @param mul multiplier. - */ - public DoubleSetter(Reflection.Method set, double mul) { - this.setMethod = set; - this.specialString = null; - this.specialMethod = null; - this.multiplier = mul; - } - - /** - * Set the double value, or if the value equals the special string, use the - * special setter and set it to true. - * - * @param set double setter. - * @param special special string - * @param specialMethod boolean setter. - */ - public DoubleSetter(Reflection.Method set, String special, - Reflection.Method specialMethod) { - this.setMethod = set; - this.specialString = special; - this.specialMethod = specialMethod; - this.multiplier = 1.0; - } - - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - s = s.trim(); - - // Check for special case - if (specialMethod != null && s.equalsIgnoreCase(specialString)) { - specialMethod.invoke(c, true); - return; - } - - // Normal case - try { - double d = Double.parseDouble(s); - setMethod.invoke(c, d * multiplier); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - - -class OverrideSetter implements Setter { - private final Reflection.Method setMethod; - private final Reflection.Method enabledMethod; - - public OverrideSetter(Reflection.Method set, Reflection.Method enabledMethod) { - this.setMethod = set; - this.enabledMethod = enabledMethod; - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - try { - double d = Double.parseDouble(s); - setMethod.invoke(c, d); - enabledMethod.invoke(c, true); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - -//// EnumSetter - sets a generic enum type -class EnumSetter> implements Setter { - private final Reflection.Method setter; - private final Class enumClass; - - public EnumSetter(Reflection.Method set, Class enumClass) { - this.setter = set; - this.enumClass = enumClass; - } - - @Override - public void set(RocketComponent c, String name, HashMap attributes, - WarningSet warnings) { - - Enum setEnum = DocumentConfig.findEnum(name, enumClass); - if (setEnum == null) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - setter.invoke(c, setEnum); - } -} - - -//// ColorSetter - sets a Color value -class ColorSetter implements Setter { - private final Reflection.Method setMethod; - - public ColorSetter(Reflection.Method set) { - setMethod = set; - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - String red = attributes.get("red"); - String green = attributes.get("green"); - String blue = attributes.get("blue"); - - if (red == null || green == null || blue == null) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - int r, g, b; - try { - r = Integer.parseInt(red); - g = Integer.parseInt(green); - b = Integer.parseInt(blue); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - if (r < 0 || g < 0 || b < 0 || r > 255 || g > 255 || b > 255) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - Color color = new Color(r, g, b); - setMethod.invoke(c, color); - - if (!s.trim().equals("")) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - } -} - -////ComponentPresetSetter - sets a ComponentPreset value -class ComponentPresetSetter implements Setter { - private final Reflection.Method setMethod; - - public ComponentPresetSetter(Reflection.Method set) { - this.setMethod = set; - } - - @Override - public void set(RocketComponent c, String name, HashMap attributes, - WarningSet warnings) { - String manufacturerName = attributes.get("manufacturer"); - if (manufacturerName == null) { - warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no manufacturer specified. Ignored")); - return; - } - - String productNo = attributes.get("partno"); - if (productNo == null) { - warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no partno specified. Ignored")); - return; - } - - String digest = attributes.get("digest"); - if (digest == null) { - warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no digest specified.")); - } - - String type = attributes.get("type"); - if (type == null) { - warnings.add(Warning.fromString("Invalid ComponentPreset for component " + c.getName() + ", no type specified.")); - } - - List presets = Application.getComponentPresetDao().find(manufacturerName, productNo); - - ComponentPreset matchingPreset = null; - - for (ComponentPreset preset : presets) { - if (digest != null && preset.getDigest().equals(digest)) { - // Found one with matching digest. Take it. - matchingPreset = preset; - break; - } - if (type != null && preset.getType().name().equals(type) && matchingPreset != null) { - // Found the first one with matching type. - matchingPreset = preset; - } - } - - // Was any found? - if (matchingPreset == null) { - warnings.add(Warning.fromString("No matching ComponentPreset for component " + c.getName() + " found matching " + manufacturerName + " " + productNo)); - return; - } - - if (digest != null && !matchingPreset.getDigest().equals(digest)) { - warnings.add(Warning.fromString("ComponentPreset for component " + c.getName() + " has wrong digest")); - } - - setMethod.invoke(c, matchingPreset); - } -} - - -////MaterialSetter - sets a Material value -class MaterialSetter implements Setter { - private final Reflection.Method setMethod; - private final Material.Type type; - - public MaterialSetter(Reflection.Method set, Material.Type type) { - this.setMethod = set; - this.type = type; - } - - @Override - public void set(RocketComponent c, String name, HashMap attributes, - WarningSet warnings) { - - Material mat; - - // Check name != "" - name = name.trim(); - if (name.equals("")) { - warnings.add(Warning.fromString("Illegal material specification, ignoring.")); - return; - } - - // Parse density - double density; - String str; - str = attributes.remove("density"); - if (str == null) { - warnings.add(Warning.fromString("Illegal material specification, ignoring.")); - return; - } - try { - density = Double.parseDouble(str); - } catch (NumberFormatException e) { - warnings.add(Warning.fromString("Illegal material specification, ignoring.")); - return; - } - - // Parse thickness - // double thickness = 0; - // str = attributes.remove("thickness"); - // try { - // if (str != null) - // thickness = Double.parseDouble(str); - // } catch (NumberFormatException e){ - // warnings.add(Warning.fromString("Illegal material specification, ignoring.")); - // return; - // } - - // Check type if specified - str = attributes.remove("type"); - if (str != null && !type.name().toLowerCase(Locale.ENGLISH).equals(str)) { - warnings.add(Warning.fromString("Illegal material type specified, ignoring.")); - return; - } - - mat = Databases.findMaterial(type, name, density); - - setMethod.invoke(c, mat); - } -} - - - - - -class PositionSetter implements Setter { - - @Override - public void set(RocketComponent c, String value, HashMap attributes, - WarningSet warnings) { - - RocketComponent.Position type = (Position) DocumentConfig.findEnum(attributes.get("type"), - RocketComponent.Position.class); - if (type == null) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - double pos; - try { - pos = Double.parseDouble(value); - } catch (NumberFormatException e) { - warnings.add(Warning.FILE_INVALID_PARAMETER); - return; - } - - if (c instanceof FinSet) { - ((FinSet) c).setRelativePosition(type); - c.setPositionValue(pos); - } else if (c instanceof LaunchLug) { - ((LaunchLug) c).setRelativePosition(type); - c.setPositionValue(pos); - } else if (c instanceof InternalComponent) { - ((InternalComponent) c).setRelativePosition(type); - c.setPositionValue(pos); - } else { - warnings.add(Warning.FILE_INVALID_PARAMETER); - } - - } -} - - -class FinTabPositionSetter extends DoubleSetter { - - public FinTabPositionSetter() { - super(Reflection.findMethod(FinSet.class, "setTabShift", double.class)); - } - - @Override - public void set(RocketComponent c, String s, HashMap attributes, - WarningSet warnings) { - - if (!(c instanceof FinSet)) { - throw new IllegalStateException("FinTabPositionSetter called for component " + c); - } - - String relative = attributes.get("relativeto"); - FinSet.TabRelativePosition position = - (TabRelativePosition) DocumentConfig.findEnum(relative, - FinSet.TabRelativePosition.class); - - if (position != null) { - - ((FinSet) c).setTabRelativePosition(position); - - } else { - if (relative == null) { - warnings.add("Required attribute 'relativeto' not found for fin tab position."); - } else { - warnings.add("Illegal attribute value '" + relative + "' encountered."); - } - } - - super.set(c, s, attributes, warnings); - } - - -} - - -class ClusterConfigurationSetter implements Setter { - - @Override - public void set(RocketComponent component, String value, HashMap attributes, - WarningSet warnings) { - - if (!(component instanceof Clusterable)) { - warnings.add("Illegal component defined as cluster."); - return; - } - - ClusterConfiguration config = null; - for (ClusterConfiguration c : ClusterConfiguration.CONFIGURATIONS) { - if (c.getXMLName().equals(value)) { - config = c; - break; - } - } - - if (config == null) { - warnings.add("Illegal cluster configuration specified."); - return; - } - - ((Clusterable) component).setClusterConfiguration(config); - } -} diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OverrideSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/OverrideSetter.java new file mode 100644 index 000000000..1cdf8585b --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OverrideSetter.java @@ -0,0 +1,31 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +class OverrideSetter implements Setter { + private final Reflection.Method setMethod; + private final Reflection.Method enabledMethod; + + public OverrideSetter(Reflection.Method set, Reflection.Method enabledMethod) { + this.setMethod = set; + this.enabledMethod = enabledMethod; + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + + try { + double d = Double.parseDouble(s); + setMethod.invoke(c, d); + enabledMethod.invoke(c, true); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/PositionSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/PositionSetter.java new file mode 100644 index 000000000..fc7d00779 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/PositionSetter.java @@ -0,0 +1,48 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.InternalComponent; +import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.RocketComponent.Position; + +class PositionSetter implements Setter { + + @Override + public void set(RocketComponent c, String value, HashMap attributes, + WarningSet warnings) { + + RocketComponent.Position type = (Position) DocumentConfig.findEnum(attributes.get("type"), + RocketComponent.Position.class); + if (type == null) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + double pos; + try { + pos = Double.parseDouble(value); + } catch (NumberFormatException e) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + + if (c instanceof FinSet) { + ((FinSet) c).setRelativePosition(type); + c.setPositionValue(pos); + } else if (c instanceof LaunchLug) { + ((LaunchLug) c).setRelativePosition(type); + c.setPositionValue(pos); + } else if (c instanceof InternalComponent) { + ((InternalComponent) c).setRelativePosition(type); + c.setPositionValue(pos); + } else { + warnings.add(Warning.FILE_INVALID_PARAMETER); + } + + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/Setter.java b/core/src/net/sf/openrocket/file/openrocket/importt/Setter.java new file mode 100644 index 000000000..79dc82aa0 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/Setter.java @@ -0,0 +1,20 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; + +//// Interface +interface Setter { + /** + * Set the specified value to the given component. + * + * @param component the component to which to set. + * @param value the value within the element. + * @param attributes attributes for the element. + * @param warnings the warning set to use. + */ + public void set(RocketComponent component, String value, + HashMap attributes, WarningSet warnings); +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/SimulationConditionsHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/SimulationConditionsHandler.java new file mode 100644 index 000000000..f0cde3a4c --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/SimulationConditionsHandler.java @@ -0,0 +1,123 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.simulation.SimulationOptions; +import net.sf.openrocket.util.GeodeticComputationStrategy; + +class SimulationConditionsHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private SimulationOptions conditions; + private AtmosphereHandler atmosphereHandler; + + public SimulationConditionsHandler(Rocket rocket, DocumentLoadingContext context) { + this.context = context; + conditions = new SimulationOptions(rocket); + // Set up default loading settings (which may differ from the new defaults) + conditions.setGeodeticComputation(GeodeticComputationStrategy.FLAT); + } + + public SimulationOptions getConditions() { + return conditions; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + if (element.equals("atmosphere")) { + atmosphereHandler = new AtmosphereHandler(attributes.get("model"), context); + return atmosphereHandler; + } + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + double d = Double.NaN; + try { + d = Double.parseDouble(content); + } catch (NumberFormatException ignore) { + } + + + if (element.equals("configid")) { + if (content.equals("")) { + conditions.setMotorConfigurationID(null); + } else { + conditions.setMotorConfigurationID(content); + } + } else if (element.equals("launchrodlength")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch rod length defined, ignoring."); + } else { + conditions.setLaunchRodLength(d); + } + } else if (element.equals("launchrodangle")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch rod angle defined, ignoring."); + } else { + conditions.setLaunchRodAngle(d * Math.PI / 180); + } + } else if (element.equals("launchroddirection")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch rod direction defined, ignoring."); + } else { + conditions.setLaunchRodDirection(d * Math.PI / 180); + } + } else if (element.equals("windaverage")) { + if (Double.isNaN(d)) { + warnings.add("Illegal average windspeed defined, ignoring."); + } else { + conditions.setWindSpeedAverage(d); + } + } else if (element.equals("windturbulence")) { + if (Double.isNaN(d)) { + warnings.add("Illegal wind turbulence intensity defined, ignoring."); + } else { + conditions.setWindTurbulenceIntensity(d); + } + } else if (element.equals("launchaltitude")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch altitude defined, ignoring."); + } else { + conditions.setLaunchAltitude(d); + } + } else if (element.equals("launchlatitude")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch latitude defined, ignoring."); + } else { + conditions.setLaunchLatitude(d); + } + } else if (element.equals("launchlongitude")) { + if (Double.isNaN(d)) { + warnings.add("Illegal launch longitude."); + } else { + conditions.setLaunchLongitude(d); + } + } else if (element.equals("geodeticmethod")) { + GeodeticComputationStrategy gcs = + (GeodeticComputationStrategy) DocumentConfig.findEnum(content, GeodeticComputationStrategy.class); + if (gcs != null) { + conditions.setGeodeticComputation(gcs); + } else { + warnings.add("Unknown geodetic computation method '" + content + "'"); + } + } else if (element.equals("atmosphere")) { + atmosphereHandler.storeSettings(conditions, warnings); + } else if (element.equals("timestep")) { + if (Double.isNaN(d) || d <= 0 ) { + warnings.add("Illegal time step defined, ignoring."); + } else { + conditions.setTimeStep(d); + } + } + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/SimulationsHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/SimulationsHandler.java new file mode 100644 index 000000000..c5b0f705b --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/SimulationsHandler.java @@ -0,0 +1,49 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.simulation.customexpression.CustomExpression; + +import org.xml.sax.SAXException; + +class SimulationsHandler extends AbstractElementHandler { + private final DocumentLoadingContext context; + private final OpenRocketDocument doc; + private SingleSimulationHandler handler; + + public SimulationsHandler(OpenRocketDocument doc, DocumentLoadingContext context) { + this.doc = doc; + this.context = context; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (!element.equals("simulation")) { + warnings.add("Unknown element '" + element + "', ignoring."); + return null; + } + + handler = new SingleSimulationHandler(doc, context); + return handler; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + attributes.remove("status"); + + //Finished loading. Rebuilding custom expressions in case something has changed such as listener variable come available. + for (CustomExpression exp : doc.getCustomExpressions()){ + exp.setExpression(exp.getExpressionString()); + } + + super.closeElement(element, attributes, content, warnings); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/SingleSimulationHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/SingleSimulationHandler.java new file mode 100644 index 000000000..e0e09e24a --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/SingleSimulationHandler.java @@ -0,0 +1,112 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.document.Simulation.Status; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.simulation.FlightData; +import net.sf.openrocket.simulation.SimulationOptions; + +class SingleSimulationHandler extends AbstractElementHandler { + + private final DocumentLoadingContext context; + + private final OpenRocketDocument doc; + + private String name; + + private SimulationConditionsHandler conditionHandler; + private FlightDataHandler dataHandler; + + private final List listeners = new ArrayList(); + + public SingleSimulationHandler(OpenRocketDocument doc, DocumentLoadingContext context) { + this.doc = doc; + this.context = context; + } + + public OpenRocketDocument getDocument() { + return doc; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + if (element.equals("name") || element.equals("simulator") || + element.equals("calculator") || element.equals("listener")) { + return PlainTextHandler.INSTANCE; + } else if (element.equals("conditions")) { + conditionHandler = new SimulationConditionsHandler(doc.getRocket(), context); + return conditionHandler; + } else if (element.equals("flightdata")) { + dataHandler = new FlightDataHandler(this, context); + return dataHandler; + } else { + warnings.add("Unknown element '" + element + "', ignoring."); + return null; + } + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) { + + if (element.equals("name")) { + name = content; + } else if (element.equals("simulator")) { + if (!content.trim().equals("RK4Simulator")) { + warnings.add("Unknown simulator '" + content.trim() + "' specified, ignoring."); + } + } else if (element.equals("calculator")) { + if (!content.trim().equals("BarrowmanCalculator")) { + warnings.add("Unknown calculator '" + content.trim() + "' specified, ignoring."); + } + } else if (element.equals("listener") && content.trim().length() > 0) { + listeners.add(content.trim()); + } + + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) { + + String s = attributes.get("status"); + Simulation.Status status = (Status) DocumentConfig.findEnum(s, Simulation.Status.class); + if (status == null) { + warnings.add("Simulation status unknown, assuming outdated."); + status = Simulation.Status.OUTDATED; + } + + SimulationOptions conditions; + if (conditionHandler != null) { + conditions = conditionHandler.getConditions(); + } else { + warnings.add("Simulation conditions not defined, using defaults."); + conditions = new SimulationOptions(doc.getRocket()); + } + + if (name == null) + name = "Simulation"; + + FlightData data; + if (dataHandler == null) + data = null; + else + data = dataHandler.getFlightData(); + + Simulation simulation = new Simulation(doc.getRocket(), status, name, + conditions, listeners, data); + + doc.addSimulation(simulation); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/StageSeparationConfigurationHandler.java b/core/src/net/sf/openrocket/file/openrocket/importt/StageSeparationConfigurationHandler.java new file mode 100644 index 000000000..a349c91df --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/StageSeparationConfigurationHandler.java @@ -0,0 +1,74 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.simplesax.AbstractElementHandler; +import net.sf.openrocket.file.simplesax.ElementHandler; +import net.sf.openrocket.file.simplesax.PlainTextHandler; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration.SeparationEvent; + +import org.xml.sax.SAXException; + +class StageSeparationConfigurationHandler extends AbstractElementHandler { + private final Stage stage; + + private SeparationEvent event = null; + private double delay = Double.NaN; + + public StageSeparationConfigurationHandler(Stage stage, DocumentLoadingContext context) { + this.stage = stage; + } + + + public StageSeparationConfiguration getConfiguration(StageSeparationConfiguration def) { + StageSeparationConfiguration config = def.clone(); + if (event != null) { + config.setSeparationEvent(event); + } + if (!Double.isNaN(delay)) { + config.setSeparationDelay(delay); + } + return config; + } + + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) + throws SAXException { + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, + WarningSet warnings) throws SAXException { + + content = content.trim(); + + if ("separationevent".equals(element)) { + event = (SeparationEvent) DocumentConfig.findEnum(content, SeparationEvent.class); + if (event == null) { + warnings.add(Warning.FILE_INVALID_PARAMETER); + return; + } + return; + } else if ("separationdelay".equals(element)) { + delay = parseDouble(content, warnings, Warning.FILE_INVALID_PARAMETER); + return; + } + super.closeElement(element, attributes, content, warnings); + + } + + @Override + public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { + String configId = attributes.get("configid"); + StageSeparationConfiguration def = stage.getStageSeparationConfiguration().getDefault(); + stage.getStageSeparationConfiguration().set(configId, getConfiguration(def)); + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/StringSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/StringSetter.java new file mode 100644 index 000000000..b1c6fc1c4 --- /dev/null +++ b/core/src/net/sf/openrocket/file/openrocket/importt/StringSetter.java @@ -0,0 +1,22 @@ +package net.sf.openrocket.file.openrocket.importt; + +import java.util.HashMap; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Reflection; + +//// StringSetter - sets the value to the contained String +class StringSetter implements Setter { + private final Reflection.Method setMethod; + + public StringSetter(Reflection.Method set) { + setMethod = set; + } + + @Override + public void set(RocketComponent c, String s, HashMap attributes, + WarningSet warnings) { + setMethod.invoke(c, s); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/RecoveryDeviceSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/RecoveryDeviceSaver.java index a73d8fd69..e4f06442d 100644 --- a/core/src/net/sf/openrocket/file/openrocket/savers/RecoveryDeviceSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/savers/RecoveryDeviceSaver.java @@ -1,9 +1,12 @@ package net.sf.openrocket.file.openrocket.savers; +import java.util.ArrayList; import java.util.List; import java.util.Locale; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.Rocket; public class RecoveryDeviceSaver extends MassObjectSaver { @@ -18,11 +21,39 @@ public class RecoveryDeviceSaver extends MassObjectSaver { elements.add("auto"); else elements.add("" + dev.getCD() + ""); - - elements.add("" + dev.getDeployEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "") + ""); - elements.add("" + dev.getDeployAltitude() + ""); - elements.add("" + dev.getDeployDelay() + ""); elements.add(materialParam(dev.getMaterial())); + + // NOTE: Default config must be BEFORE overridden config for proper backward compatibility later on + DeploymentConfiguration defaultConfig = dev.getDeploymentConfiguration().getDefault(); + elements.addAll(deploymentConfiguration(defaultConfig, false)); + + Rocket rocket = c.getRocket(); + // Note - getFlightConfigurationIDs returns at least one element. The first element + // is null and means "default". + String[] configs = rocket.getFlightConfigurationIDs(); + if (configs.length > 1) { + + for (String id : configs) { + if (id == null) { + continue; + } + if (dev.getDeploymentConfiguration().isDefault(id)) { + continue; + } + DeploymentConfiguration config = dev.getDeploymentConfiguration().get(id); + elements.add(""); + elements.addAll(deploymentConfiguration(config, true)); + elements.add(""); + } + } } + private List deploymentConfiguration(DeploymentConfiguration config, boolean indent) { + List elements = new ArrayList(3); + elements.add((indent ? " " : "") + "" + config.getDeployEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "") + ""); + elements.add((indent ? " " : "") + "" + config.getDeployAltitude() + ""); + elements.add((indent ? " " : "") + "" + config.getDeployDelay() + ""); + return elements; + + } } diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java index a4892902a..7df686e5a 100644 --- a/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java @@ -5,21 +5,26 @@ import java.util.Collections; import java.util.List; import java.util.Locale; -import net.sf.openrocket.file.RocketSaver; +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; +import net.sf.openrocket.appearance.Decal.EdgeMode; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.material.Material; import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.rocketcomponent.ComponentAssembly; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; +import net.sf.openrocket.rocketcomponent.MotorConfiguration; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.LineStyle; - +import net.sf.openrocket.util.TextUtil; public class RocketComponentSaver { private static final Translator trans = Application.getTranslator(); @@ -29,7 +34,7 @@ public class RocketComponentSaver { } protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) { - elements.add("" + RocketSaver.escapeXML(c.getName()) + ""); + elements.add("" + TextUtil.escapeXML(c.getName()) + ""); ComponentPreset preset = c.getPresetComponent(); if (preset != null) { @@ -38,14 +43,33 @@ public class RocketComponentSaver { "\" partno=\"" + preset.getPartNo() + "\" digest=\"" + preset.getDigest() + "\"/>"); } + Appearance ap = c.getAppearance(); + if (ap != null) { + elements.add(""); + Color paint = ap.getPaint(); + emitColor("paint", elements, paint); + elements.add("" + ap.getShine() + ""); + Decal decal = ap.getTexture(); + if (decal != null) { + String name = decal.getImage().getName(); + double rotation = decal.getRotation(); + EdgeMode edgeMode = decal.getEdgeMode(); + elements.add(""); + Coordinate center = decal.getCenter(); + elements.add("
"); + Coordinate offset = decal.getOffset(); + elements.add(""); + Coordinate scale = decal.getScale(); + elements.add(""); + elements.add(""); + } + elements.add(""); + } // Save color and line style if significant if (!(c instanceof Rocket || c instanceof ComponentAssembly)) { Color color = c.getColor(); - if (color != null) { - elements.add(""); - } + emitColor("color", elements, color); LineStyle style = c.getLineStyle(); if (style != null) { @@ -81,7 +105,7 @@ public class RocketComponentSaver { // Comment if (c.getComment().length() > 0) { - elements.add("" + RocketSaver.escapeXML(c.getComment()) + ""); + elements.add("" + TextUtil.escapeXML(c.getComment()) + ""); } } @@ -113,22 +137,28 @@ public class RocketComponentSaver { String baseName = trans.getBaseText("material", mat.getName()); - return str + " density=\"" + mat.getDensity() + "\">" + RocketSaver.escapeXML(baseName) + ""; + return str + " density=\"" + mat.getDensity() + "\">" + TextUtil.escapeXML(baseName) + ""; } protected final List motorMountParams(MotorMount mount) { if (!mount.isMotorMount()) return Collections.emptyList(); - - String[] motorConfigIDs = ((RocketComponent) mount).getRocket().getMotorConfigurationIDs(); + String[] motorConfigIDs = ((RocketComponent) mount).getRocket().getFlightConfigurationIDs(); List elements = new ArrayList(); elements.add(""); + // NOTE: Default config must be BEFORE overridden config for proper backward compatibility later on + elements.add(" " + + mount.getIgnitionConfiguration().getDefault().getIgnitionEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "") + + ""); + elements.add(" " + mount.getIgnitionConfiguration().getDefault().getIgnitionDelay() + ""); + elements.add(" " + mount.getMotorOverhang() + ""); + for (String id : motorConfigIDs) { - Motor motor = mount.getMotor(id); - + MotorConfiguration motorConfig = mount.getMotorConfiguration().get(id); + Motor motor = motorConfig.getMotor(); // Nothing is stored if no motor loaded if (motor == null) continue; @@ -139,34 +169,44 @@ public class RocketComponentSaver { } if (motor instanceof ThrustCurveMotor) { ThrustCurveMotor m = (ThrustCurveMotor) motor; - elements.add(" " + RocketSaver.escapeXML(m.getManufacturer().getSimpleName()) + + elements.add(" " + TextUtil.escapeXML(m.getManufacturer().getSimpleName()) + ""); elements.add(" " + m.getDigest() + ""); } - elements.add(" " + RocketSaver.escapeXML(motor.getDesignation()) + ""); + elements.add(" " + TextUtil.escapeXML(motor.getDesignation()) + ""); elements.add(" " + motor.getDiameter() + ""); elements.add(" " + motor.getLength() + ""); // Motor delay - if (mount.getMotorDelay(id) == Motor.PLUGGED) { + if (motorConfig.getEjectionDelay() == Motor.PLUGGED) { elements.add(" none"); } else { - elements.add(" " + mount.getMotorDelay(id) + ""); + elements.add(" " + motorConfig.getEjectionDelay() + ""); } elements.add(" "); + + if (!mount.getIgnitionConfiguration().isDefault(id)) { + IgnitionConfiguration ignition = mount.getIgnitionConfiguration().get(id); + elements.add(" "); + elements.add(" " + ignition.getIgnitionEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "") + ""); + elements.add(" " + ignition.getIgnitionDelay() + ""); + elements.add(" "); + + } } - elements.add(" " - + mount.getIgnitionEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "") - + ""); - - elements.add(" " + mount.getIgnitionDelay() + ""); - elements.add(" " + mount.getMotorOverhang() + ""); - elements.add(""); return elements; } + private final static void emitColor(String elementName, List elements, Color color) { + if (color != null) { + elements.add("<" + elementName + " red=\"" + color.getRed() + "\" green=\"" + color.getGreen() + + "\" blue=\"" + color.getBlue() + "\"/>"); + } + + } + } diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/RocketSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/RocketSaver.java index be7c52af8..59af2484b 100644 --- a/core/src/net/sf/openrocket/file/openrocket/savers/RocketSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/savers/RocketSaver.java @@ -29,19 +29,19 @@ public class RocketSaver extends RocketComponentSaver { if (rocket.getDesigner().length() > 0) { elements.add("" - + net.sf.openrocket.file.RocketSaver.escapeXML(rocket.getDesigner()) + + net.sf.openrocket.util.TextUtil.escapeXML(rocket.getDesigner()) + ""); } if (rocket.getRevision().length() > 0) { elements.add("" - + net.sf.openrocket.file.RocketSaver.escapeXML(rocket.getRevision()) + + net.sf.openrocket.util.TextUtil.escapeXML(rocket.getRevision()) + ""); } // Motor configurations - String defId = rocket.getDefaultConfiguration().getMotorConfigurationID(); - for (String id : rocket.getMotorConfigurationIDs()) { + String defId = rocket.getDefaultConfiguration().getFlightConfigurationID(); + for (String id : rocket.getFlightConfigurationIDs()) { if (id == null) continue; @@ -49,10 +49,11 @@ public class RocketSaver extends RocketComponentSaver { if (id.equals(defId)) str += " default=\"true\""; - if (rocket.getMotorConfigurationName(id) == "") { + + if (rocket.getFlightConfigurationName(id).equals(Rocket.DEFAULT_NAME)) { str += "/>"; } else { - str += ">" + net.sf.openrocket.file.RocketSaver.escapeXML(rocket.getMotorConfigurationName(id)) + str += ">" + net.sf.openrocket.util.TextUtil.escapeXML(rocket.getFlightConfigurationName(id)) + ""; } elements.add(str); diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java index 23f591f31..fb875379f 100644 --- a/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java @@ -4,8 +4,10 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; public class StageSaver extends ComponentAssemblySaver { @@ -27,10 +29,38 @@ public class StageSaver extends ComponentAssemblySaver { Stage stage = (Stage) c; if (stage.getStageNumber() > 0) { - elements.add("" - + stage.getSeparationEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "") - + ""); - elements.add("" + stage.getSeparationDelay() + ""); + // NOTE: Default config must be BEFORE overridden config for proper backward compatibility later on + elements.addAll(separationConfig(stage.getStageSeparationConfiguration().getDefault(), false)); + + Rocket rocket = stage.getRocket(); + // Note - getFlightConfigurationIDs returns at least one element. The first element + // is null and means "default". + String[] configs = rocket.getFlightConfigurationIDs(); + if (configs.length > 1) { + + for (String id : configs) { + if (id == null) { + continue; + } + if (stage.getStageSeparationConfiguration().isDefault(id)) { + continue; + } + StageSeparationConfiguration config = stage.getStageSeparationConfiguration().get(id); + elements.add(""); + elements.addAll(separationConfig(config, true)); + elements.add(""); + } + } } } + + private List separationConfig(StageSeparationConfiguration config, boolean indent) { + List elements = new ArrayList(2); + elements.add((indent ? " " : "") + "" + + config.getSeparationEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "") + + ""); + elements.add((indent ? " " : "") + "" + config.getSeparationDelay() + ""); + return elements; + + } } diff --git a/core/src/net/sf/openrocket/file/rocksim/RocksimCommonConstants.java b/core/src/net/sf/openrocket/file/rocksim/RocksimCommonConstants.java index 431db2650..bde3d072a 100644 --- a/core/src/net/sf/openrocket/file/rocksim/RocksimCommonConstants.java +++ b/core/src/net/sf/openrocket/file/rocksim/RocksimCommonConstants.java @@ -82,6 +82,7 @@ public class RocksimCommonConstants { public static final String TUBE_FIN_SET = "TubeFinSet"; public static final String RING_TAIL = "RingTail"; public static final String EXTERNAL_POD = "ExternalPod"; + public static final String TEXTURE = "Texture"; /** * Length conversion. Rocksim is in millimeters, OpenRocket in meters. diff --git a/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java b/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java index 36f44d527..eff692e2a 100644 --- a/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java +++ b/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java @@ -1,5 +1,14 @@ package net.sf.openrocket.file.rocksim.export; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.StringWriter; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Marshaller; + import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.StorageOptions; import net.sf.openrocket.file.RocketSaver; @@ -12,14 +21,6 @@ import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.Stage; import net.sf.openrocket.startup.Application; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Marshaller; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.StringWriter; - /** * This class is responsible for converting an OpenRocket design to a Rocksim design. */ @@ -61,7 +62,6 @@ public class RocksimSaver extends RocketSaver { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(dest, "UTF-8")); writer.write(marshalToRocksim(doc)); writer.flush(); - writer.close(); } @Override diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/AttachedPartsHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/AttachedPartsHandler.java index cb019fa37..7a909a4c5 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/AttachedPartsHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/AttachedPartsHandler.java @@ -3,74 +3,77 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.simplesax.AbstractElementHandler; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.rocketcomponent.RocketComponent; -import java.util.HashMap; - /** * A SAX handler for the Rocksim AttachedParts XML type. */ class AttachedPartsHandler extends AbstractElementHandler { - /** The parent component. */ - private final RocketComponent component; - - /** - * Constructor. - * - * @param c the parent - * - * @throws IllegalArgumentException thrown if c is null - */ - public AttachedPartsHandler(RocketComponent c) throws IllegalArgumentException { - if (c == null) { - throw new IllegalArgumentException("The parent component of any attached part may not be null."); - } - component = c; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - if (RocksimCommonConstants.FIN_SET.equals(element)) { - return new FinSetHandler(component); - } - if (RocksimCommonConstants.CUSTOM_FIN_SET.equals(element)) { - return new FinSetHandler(component); - } - if (RocksimCommonConstants.LAUNCH_LUG.equals(element)) { - return new LaunchLugHandler(component, warnings); - } - if (RocksimCommonConstants.PARACHUTE.equals(element)) { - return new ParachuteHandler(component, warnings); - } - if (RocksimCommonConstants.STREAMER.equals(element)) { - return new StreamerHandler(component, warnings); - } - if (RocksimCommonConstants.MASS_OBJECT.equals(element)) { - return new MassObjectHandler(component, warnings); - } - if (RocksimCommonConstants.RING.equals(element)) { - return new RingHandler(component, warnings); - } - if (RocksimCommonConstants.BODY_TUBE.equals(element)) { - return new InnerBodyTubeHandler(component, warnings); - } - if (RocksimCommonConstants.TRANSITION.equals(element)) { - return new TransitionHandler(component, warnings); - } - if (RocksimCommonConstants.TUBE_FIN_SET.equals(element)) { - warnings.add("Tube fins are not currently supported. Ignoring."); - } - if (RocksimCommonConstants.RING_TAIL.equals(element)) { - warnings.add("Ring tails are not currently supported. Ignoring."); - } - if (RocksimCommonConstants.EXTERNAL_POD.equals(element)) { - warnings.add("Pods are not currently supported. Ignoring."); - } - return null; - } + private final DocumentLoadingContext context; + + /** The parent component. */ + private final RocketComponent component; + + /** + * Constructor. + * + * @param c the parent + * + * @throws IllegalArgumentException thrown if c is null + */ + public AttachedPartsHandler(DocumentLoadingContext context, RocketComponent c) throws IllegalArgumentException { + if (c == null) { + throw new IllegalArgumentException("The parent component of any attached part may not be null."); + } + this.context = context; + component = c; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + if (RocksimCommonConstants.FIN_SET.equals(element)) { + return new FinSetHandler(context, component); + } + if (RocksimCommonConstants.CUSTOM_FIN_SET.equals(element)) { + return new FinSetHandler(context, component); + } + if (RocksimCommonConstants.LAUNCH_LUG.equals(element)) { + return new LaunchLugHandler(context, component, warnings); + } + if (RocksimCommonConstants.PARACHUTE.equals(element)) { + return new ParachuteHandler(context, component, warnings); + } + if (RocksimCommonConstants.STREAMER.equals(element)) { + return new StreamerHandler(context, component, warnings); + } + if (RocksimCommonConstants.MASS_OBJECT.equals(element)) { + return new MassObjectHandler(context, component, warnings); + } + if (RocksimCommonConstants.RING.equals(element)) { + return new RingHandler(context, component, warnings); + } + if (RocksimCommonConstants.BODY_TUBE.equals(element)) { + return new InnerBodyTubeHandler(context, component, warnings); + } + if (RocksimCommonConstants.TRANSITION.equals(element)) { + return new TransitionHandler(context, component, warnings); + } + if (RocksimCommonConstants.TUBE_FIN_SET.equals(element)) { + warnings.add("Tube fins are not currently supported. Ignoring."); + } + if (RocksimCommonConstants.RING_TAIL.equals(element)) { + warnings.add("Ring tails are not currently supported. Ignoring."); + } + if (RocksimCommonConstants.EXTERNAL_POD.equals(element)) { + warnings.add("Pods are not currently supported. Ignoring."); + } + return null; + } } - diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/BaseHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/BaseHandler.java index 733b969d4..5bf50a513 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/BaseHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/BaseHandler.java @@ -9,11 +9,14 @@ import java.util.HashMap; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.database.Databases; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.rocksim.RocksimDensityType; import net.sf.openrocket.file.simplesax.AbstractElementHandler; import net.sf.openrocket.material.Material; +import net.sf.openrocket.rocketcomponent.ExternalComponent; import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; import org.xml.sax.SAXException; @@ -50,6 +53,14 @@ public abstract class BaseHandler extends AbstractEle */ private String materialName = ""; + protected final DocumentLoadingContext context; + private final RockSimAppearanceBuilder appearanceBuilder; + + public BaseHandler(DocumentLoadingContext context) { + this.context = context; + appearanceBuilder = new RockSimAppearanceBuilder(context); + } + /** * The SAX method called when the closing element tag is reached. * @@ -85,6 +96,8 @@ public abstract class BaseHandler extends AbstractEle if (RocksimCommonConstants.DENSITY_TYPE.equals(element)) { densityType = RocksimDensityType.fromCode(Integer.parseInt(content)); } + + appearanceBuilder.processElement(element, content, warnings); } catch (NumberFormatException nfe) { warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); } @@ -101,6 +114,17 @@ public abstract class BaseHandler extends AbstractEle */ density = computeDensity(densityType, density); RocketComponent component = getComponent(); + + //TODO - What RockSim components can have Appearances? + if (component instanceof ExternalComponent) { + //If a symmetric component is set to PreventSeam then it is repeated + //twice as many times around the rocket. + if (component instanceof SymmetricComponent && appearanceBuilder.isPreventSeam()) { + appearanceBuilder.setScaleU(appearanceBuilder.getScaleU() * 2); + } + component.setAppearance(appearanceBuilder.getAppearance()); + } + updateComponentMaterial(component, materialName, getMaterialType(), density); } @@ -276,7 +300,7 @@ public abstract class BaseHandler extends AbstractEle * * @return the Method instance, or null */ - private static Method getMethod(RocketComponent component, String name, Class[] args) { + private static Method getMethod(RocketComponent component, String name, Class[] args) { Method method = null; try { method = component.getClass().getMethod(name, args); diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/BodyTubeHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/BodyTubeHandler.java index 8997768a5..9d2eb3899 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/BodyTubeHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/BodyTubeHandler.java @@ -3,7 +3,10 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.rocksim.RocksimFinishCode; import net.sf.openrocket.file.simplesax.ElementHandler; @@ -11,96 +14,93 @@ import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.material.Material; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.RocketComponent; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * A SAX handler for Rocksim Body Tubes. */ class BodyTubeHandler extends BaseHandler { - /** - * The OpenRocket BodyTube. - */ - private final BodyTube bodyTube; - - /** - * Constructor. - * - * @param c parent component - * @param warnings the warning set - * @throws IllegalArgumentException thrown if c is null - */ - public BodyTubeHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException { - if (c == null) { - throw new IllegalArgumentException("The parent component of a body tube may not be null."); - } - bodyTube = new BodyTube(); - if (isCompatible(c, BodyTube.class, warnings)) { - c.addChild(bodyTube); - } - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - if (RocksimCommonConstants.ATTACHED_PARTS.equals(element)) { - return new AttachedPartsHandler(bodyTube); - } - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - super.closeElement(element, attributes, content, warnings); - - try { - if (RocksimCommonConstants.OD.equals(element)) { - bodyTube.setOuterRadius(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS); - } - if (RocksimCommonConstants.ID.equals(element)) { - final double r = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS; - bodyTube.setInnerRadius(r); - } - if (RocksimCommonConstants.LEN.equals(element)) { - bodyTube.setLength(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); - } - if (RocksimCommonConstants.FINISH_CODE.equals(element)) { - bodyTube.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket()); - } - if (RocksimCommonConstants.IS_MOTOR_MOUNT.equals(element)) { - bodyTube.setMotorMount("1".equals(content)); - } - if (RocksimCommonConstants.ENGINE_OVERHANG.equals(element)) { - bodyTube.setMotorOverhang(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); - } - if (RocksimCommonConstants.MATERIAL.equals(element)) { - setMaterialName(content); - } - } - catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - - /** - * Get the component this handler is working upon. - * - * @return a component - */ - @Override - public BodyTube getComponent() { - return bodyTube; - } - - /** - * Get the required type of material for this component. - * - * @return BULK - */ - @Override - public Material.Type getMaterialType() { - return Material.Type.BULK; - } + /** + * The OpenRocket BodyTube. + */ + private final BodyTube bodyTube; + + /** + * Constructor. + * + * @param c parent component + * @param warnings the warning set + * @throws IllegalArgumentException thrown if c is null + */ + public BodyTubeHandler(DocumentLoadingContext context, RocketComponent c, WarningSet warnings) throws IllegalArgumentException { + super(context); + if (c == null) { + throw new IllegalArgumentException("The parent component of a body tube may not be null."); + } + bodyTube = new BodyTube(); + if (isCompatible(c, BodyTube.class, warnings)) { + c.addChild(bodyTube); + } + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + if (RocksimCommonConstants.ATTACHED_PARTS.equals(element)) { + return new AttachedPartsHandler(context, bodyTube); + } + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.closeElement(element, attributes, content, warnings); + + try { + if (RocksimCommonConstants.OD.equals(element)) { + bodyTube.setOuterRadius(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS); + } + if (RocksimCommonConstants.ID.equals(element)) { + final double r = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS; + bodyTube.setInnerRadius(r); + } + if (RocksimCommonConstants.LEN.equals(element)) { + bodyTube.setLength(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); + } + if (RocksimCommonConstants.FINISH_CODE.equals(element)) { + bodyTube.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket()); + } + if (RocksimCommonConstants.IS_MOTOR_MOUNT.equals(element)) { + bodyTube.setMotorMount("1".equals(content)); + } + if (RocksimCommonConstants.ENGINE_OVERHANG.equals(element)) { + bodyTube.setMotorOverhang(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); + } + if (RocksimCommonConstants.MATERIAL.equals(element)) { + setMaterialName(content); + } + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + /** + * Get the component this handler is working upon. + * + * @return a component + */ + @Override + public BodyTube getComponent() { + return bodyTube; + } + + /** + * Get the required type of material for this component. + * + * @return BULK + */ + public Material.Type getMaterialType() { + return Material.Type.BULK; + } } - diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/FinSetHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/FinSetHandler.java index 6361ab8f2..04ead9a45 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/FinSetHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/FinSetHandler.java @@ -3,7 +3,13 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.rocksim.RocksimFinishCode; import net.sf.openrocket.file.rocksim.RocksimLocationMode; @@ -19,12 +25,8 @@ import net.sf.openrocket.rocketcomponent.IllegalFinPointException; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; import net.sf.openrocket.util.Coordinate; -import org.xml.sax.SAXException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; +import org.xml.sax.SAXException; /** * A SAX handler for Rocksim fin sets. Because the type of fin may not be known first (in Rocksim file format, the fin @@ -33,366 +35,368 @@ import java.util.List; * to construct the corresponding OpenRocket FinSet. */ class FinSetHandler extends AbstractElementHandler { - /** - * The parent component. - */ - private final RocketComponent component; - - /** - * The name of the fin. - */ - private String name; - /** - * The Rocksim fin shape code. - */ - private int shapeCode; - /** - * The location of the fin on its parent. - */ - private double location = 0.0d; - /** - * The OpenRocket Position which gives the absolute/relative positioning for location. - */ - private RocketComponent.Position position; - /** - * The number of fins in this fin set. - */ - private int finCount; - /** - * The length of the root chord. - */ - private double rootChord = 0.0d; - /** - * The length of the tip chord. - */ - private double tipChord = 0.0d; - /** - * The length of the mid-chord (aka height). - */ - @SuppressWarnings("unused") + /** + * The parent component. + */ + private final RocketComponent component; + + /** + * The name of the fin. + */ + private String name; + /** + * The Rocksim fin shape code. + */ + private int shapeCode; + /** + * The location of the fin on its parent. + */ + private double location = 0.0d; + /** + * The OpenRocket Position which gives the absolute/relative positioning for location. + */ + private RocketComponent.Position position; + /** + * The number of fins in this fin set. + */ + private int finCount; + /** + * The length of the root chord. + */ + private double rootChord = 0.0d; + /** + * The length of the tip chord. + */ + private double tipChord = 0.0d; + /** + * The length of the mid-chord (aka height). + */ private double midChordLen = 0.0d; - /** - * The distance of the leading edge from root to top. - */ - private double sweepDistance = 0.0d; - /** - * The angle the fins have been rotated from the y-axis, if looking down the tube, in radians. - */ - private double radialAngle = 0.0d; - /** - * The thickness of the fins. - */ - private double thickness; - /** - * The finish of the fins. - */ - private ExternalComponent.Finish finish; - /** - * The shape of the tip. - */ - private int tipShapeCode; - /** - * The length of the TTW tab. - */ - private double tabLength = 0.0d; - /** - * The depth of the TTW tab. - */ - private double tabDepth = 0.0d; - /** - * The offset of the tab, from the front of the fin. - */ - private double taboffset = 0.0d; - /** - * The elliptical semi-span (height). - */ - private double semiSpan; - /** - * The list of custom points. - */ - private String pointList; - /** - * Override the Cg and mass. - */ - private boolean override = false; - /** - * The overridden mass. - */ - private Double mass = 0d; - /** - * The overridden Cg. - */ - private Double cg = 0d; - /** - * The density of the material in the component. - */ - private Double density = 0d; - /** - * The material name. - */ - private String materialName = ""; - /** - * The Rocksim calculated mass. - */ - private Double calcMass = 0d; - /** - * The Rocksim calculated cg. - */ - private Double calcCg = 0d; - - - /** - * Constructor. - * - * @param c the parent - * - * @throws IllegalArgumentException thrown if c is null - */ - public FinSetHandler (RocketComponent c) throws IllegalArgumentException { - if (c == null) { - throw new IllegalArgumentException("The parent component of a fin set may not be null."); - } - component = c; - } - - @Override - public ElementHandler openElement (String element, HashMap attributes, WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement (String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - try { - if (RocksimCommonConstants.NAME.equals(element)) { - name = content; - } - if (RocksimCommonConstants.MATERIAL.equals(element)) { - materialName = content; - } - if (RocksimCommonConstants.FINISH_CODE.equals(element)) { - finish = RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket(); - } - if (RocksimCommonConstants.XB.equals(element)) { - location = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if (RocksimCommonConstants.LOCATION_MODE.equals(element)) { - position = RocksimLocationMode.fromCode(Integer.parseInt(content)).asOpenRocket(); - } - if (RocksimCommonConstants.FIN_COUNT.equals(element)) { - finCount = Integer.parseInt(content); - } - if (RocksimCommonConstants.ROOT_CHORD.equals(element)) { - rootChord = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if (RocksimCommonConstants.TIP_CHORD.equals(element)) { - tipChord = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if (RocksimCommonConstants.SEMI_SPAN.equals(element)) { - semiSpan = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if ("MidChordLen".equals(element)) { - midChordLen = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if (RocksimCommonConstants.SWEEP_DISTANCE.equals(element)) { - sweepDistance = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if (RocksimCommonConstants.THICKNESS.equals(element)) { - thickness = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if (RocksimCommonConstants.TIP_SHAPE_CODE.equals(element)) { - tipShapeCode = Integer.parseInt(content); - } - if (RocksimCommonConstants.TAB_LENGTH.equals(element)) { - tabLength = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if (RocksimCommonConstants.TAB_DEPTH.equals(element)) { - tabDepth = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if (RocksimCommonConstants.TAB_OFFSET.equals(element)) { - taboffset = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if (RocksimCommonConstants.RADIAL_ANGLE.equals(element)) { - radialAngle = Double.parseDouble(content); - } - if (RocksimCommonConstants.SHAPE_CODE.equals(element)) { - shapeCode = Integer.parseInt(content); - } - if (RocksimCommonConstants.POINT_LIST.equals(element)) { - pointList = content; - } - if (RocksimCommonConstants.KNOWN_MASS.equals(element)) { - mass = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS); - } - if (RocksimCommonConstants.DENSITY.equals(element)) { - density = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_BULK_DENSITY); - } - if (RocksimCommonConstants.KNOWN_CG.equals(element)) { - cg = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS); - } - if (RocksimCommonConstants.USE_KNOWN_CG.equals(element)) { - override = "1".equals(content); - } - if (RocksimCommonConstants.CALC_MASS.equals(element)) { - calcMass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS; - } - if (RocksimCommonConstants.CALC_CG.equals(element)) { - calcCg = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - } - catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - - @Override - public void endHandler (String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - //Create the fin set and correct for overrides and actual material densities - final FinSet finSet = asOpenRocket(warnings); - if (component.isCompatible(finSet)) { - BaseHandler.setOverride(finSet, override, mass, cg); - if (!override && finSet.getCrossSection().equals(FinSet.CrossSection.AIRFOIL)) { - //Override mass anyway. This is done only for AIRFOIL because Rocksim does not compute different - //mass/cg for different cross sections, but OpenRocket does. This can lead to drastic differences - //in mass. To counteract that, the cross section value is retained but the mass/cg is overridden - //with the calculated values from Rocksim. This will best approximate the Rocksim design in OpenRocket. - BaseHandler.setOverride(finSet, true, calcMass, calcCg); - } - BaseHandler.updateComponentMaterial(finSet, materialName, Material.Type.BULK, density); - component.addChild(finSet); - } - else { - warnings.add(finSet.getComponentName() + " can not be attached to " - + component.getComponentName() + ", ignoring component."); - } - } - - - /** - * Convert the parsed Rocksim data values in this object to an instance of OpenRocket's FinSet. - * - * @param warnings the warning set to convey incompatibilities to the user - * - * @return a FinSet instance - */ - public FinSet asOpenRocket (WarningSet warnings) { - FinSet result; - - if (shapeCode == 0) { - //Trapezoidal - result = new TrapezoidFinSet(); - ((TrapezoidFinSet) result).setFinShape(rootChord, tipChord, sweepDistance, semiSpan, thickness); - } - else if (shapeCode == 1) { - //Elliptical - result = new EllipticalFinSet(); - ((EllipticalFinSet) result).setHeight(semiSpan); - ((EllipticalFinSet) result).setLength(rootChord); - } - else if (shapeCode == 2) { - - result = new FreeformFinSet(); - try { - ((FreeformFinSet) result).setPoints(toCoordinates(pointList, warnings)); - } - catch (IllegalFinPointException e) { - warnings.add("Illegal fin point set. " + e.getMessage() + " Ignoring."); - } - } - else { - return null; - } - result.setThickness(thickness); - result.setName(name); - result.setFinCount(finCount); - result.setFinish(finish); - //All TTW tabs in Rocksim are relative to the front of the fin. - result.setTabRelativePosition(FinSet.TabRelativePosition.FRONT); - result.setTabHeight(tabDepth); - result.setTabLength(tabLength); - result.setTabShift(taboffset); - result.setBaseRotation(radialAngle); - result.setCrossSection(convertTipShapeCode(tipShapeCode)); - result.setRelativePosition(position); - PositionDependentHandler.setLocation(result, position, location); - return result; - - } - - /** - * Convert a Rocksim string that represents fin plan points into an array of OpenRocket coordinates. - * - * @param myPointList a comma and pipe delimited string of X,Y coordinates from Rocksim. This is of the format: - *
x0,y0|x1,y1|x2,y2|... 
- * @param warnings the warning set to convey incompatibilities to the user - * - * @return an array of OpenRocket Coordinates - */ - private Coordinate[] toCoordinates (String myPointList, WarningSet warnings) { - List result = new ArrayList(); - if (myPointList != null && myPointList.length() > 0) { - String[] points = myPointList.split("\\Q|\\E"); - for (String point : points) { - String[] aPoint = point.split(","); - try { - if (aPoint.length > 1) { - Coordinate c = new Coordinate( - Double.parseDouble(aPoint[0]) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH, - Double.parseDouble(aPoint[1]) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); - result.add(c); - } - else { - warnings.add("Invalid fin point pair."); - } - } - catch (NumberFormatException nfe) { - warnings.add("Fin point not in numeric format."); - } - } - if (!result.isEmpty()) { - //OpenRocket requires fin plan points be ordered from leading root chord to trailing root chord in the - //Coordinate array. - Coordinate last = result.get(result.size() - 1); - if (last.x == 0 && last.y == 0) { - Collections.reverse(result); - } - } - } - final Coordinate[] coords = new Coordinate[result.size()]; - return result.toArray(coords); - } - - - /** - * Convert a Rocksim tip shape to an OpenRocket CrossSection. - * - * @param tipShape the tip shape code from Rocksim - * - * @return a CrossSection instance - */ - public static FinSet.CrossSection convertTipShapeCode (int tipShape) { - switch (tipShape) { - case 0: - return FinSet.CrossSection.SQUARE; - case 1: - return FinSet.CrossSection.ROUNDED; - case 2: - return FinSet.CrossSection.AIRFOIL; - default: - return FinSet.CrossSection.SQUARE; - } - } - - public static int convertTipShapeCode (FinSet.CrossSection cs) { - if (FinSet.CrossSection.ROUNDED.equals(cs)) { - return 1; - } - if (FinSet.CrossSection.AIRFOIL.equals(cs)) { - return 2; - } - return 0; - } - + /** + * The distance of the leading edge from root to top. + */ + private double sweepDistance = 0.0d; + /** + * The angle the fins have been rotated from the y-axis, if looking down the tube, in radians. + */ + private double radialAngle = 0.0d; + /** + * The thickness of the fins. + */ + private double thickness; + /** + * The finish of the fins. + */ + private ExternalComponent.Finish finish; + /** + * The shape of the tip. + */ + private int tipShapeCode; + /** + * The length of the TTW tab. + */ + private double tabLength = 0.0d; + /** + * The depth of the TTW tab. + */ + private double tabDepth = 0.0d; + /** + * The offset of the tab, from the front of the fin. + */ + private double taboffset = 0.0d; + /** + * The elliptical semi-span (height). + */ + private double semiSpan; + /** + * The list of custom points. + */ + private String pointList; + /** + * Override the Cg and mass. + */ + private boolean override = false; + /** + * The overridden mass. + */ + private Double mass = 0d; + /** + * The overridden Cg. + */ + private Double cg = 0d; + /** + * The density of the material in the component. + */ + private Double density = 0d; + /** + * The material name. + */ + private String materialName = ""; + /** + * The Rocksim calculated mass. + */ + private Double calcMass = 0d; + /** + * The Rocksim calculated cg. + */ + private Double calcCg = 0d; + + private final RockSimAppearanceBuilder appearanceBuilder; + + /** + * Constructor. + * + * @param c the parent + * + * @throws IllegalArgumentException thrown if c is null + */ + public FinSetHandler(DocumentLoadingContext context, RocketComponent c) throws IllegalArgumentException { + if (c == null) { + throw new IllegalArgumentException("The parent component of a fin set may not be null."); + } + appearanceBuilder = new RockSimAppearanceBuilder(context); + component = c; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + try { + if (RocksimCommonConstants.NAME.equals(element)) { + name = content; + } + if (RocksimCommonConstants.MATERIAL.equals(element)) { + materialName = content; + } + if (RocksimCommonConstants.FINISH_CODE.equals(element)) { + finish = RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket(); + } + if (RocksimCommonConstants.XB.equals(element)) { + location = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if (RocksimCommonConstants.LOCATION_MODE.equals(element)) { + position = RocksimLocationMode.fromCode(Integer.parseInt(content)).asOpenRocket(); + } + if (RocksimCommonConstants.FIN_COUNT.equals(element)) { + finCount = Integer.parseInt(content); + } + if (RocksimCommonConstants.ROOT_CHORD.equals(element)) { + rootChord = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if (RocksimCommonConstants.TIP_CHORD.equals(element)) { + tipChord = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if (RocksimCommonConstants.SEMI_SPAN.equals(element)) { + semiSpan = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if ("MidChordLen".equals(element)) { + midChordLen = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if (RocksimCommonConstants.SWEEP_DISTANCE.equals(element)) { + sweepDistance = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if (RocksimCommonConstants.THICKNESS.equals(element)) { + thickness = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if (RocksimCommonConstants.TIP_SHAPE_CODE.equals(element)) { + tipShapeCode = Integer.parseInt(content); + } + if (RocksimCommonConstants.TAB_LENGTH.equals(element)) { + tabLength = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if (RocksimCommonConstants.TAB_DEPTH.equals(element)) { + tabDepth = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if (RocksimCommonConstants.TAB_OFFSET.equals(element)) { + taboffset = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if (RocksimCommonConstants.RADIAL_ANGLE.equals(element)) { + radialAngle = Double.parseDouble(content); + } + if (RocksimCommonConstants.SHAPE_CODE.equals(element)) { + shapeCode = Integer.parseInt(content); + } + if (RocksimCommonConstants.POINT_LIST.equals(element)) { + pointList = content; + } + if (RocksimCommonConstants.KNOWN_MASS.equals(element)) { + mass = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS); + } + if (RocksimCommonConstants.DENSITY.equals(element)) { + density = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_BULK_DENSITY); + } + if (RocksimCommonConstants.KNOWN_CG.equals(element)) { + cg = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS); + } + if (RocksimCommonConstants.USE_KNOWN_CG.equals(element)) { + override = "1".equals(content); + } + if (RocksimCommonConstants.CALC_MASS.equals(element)) { + calcMass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS; + } + if (RocksimCommonConstants.CALC_CG.equals(element)) { + calcCg = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + + appearanceBuilder.processElement(element, content, warnings); + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + //Create the fin set and correct for overrides and actual material densities + final FinSet finSet = asOpenRocket(warnings); + + finSet.setAppearance(appearanceBuilder.getAppearance()); + + if (component.isCompatible(finSet)) { + BaseHandler.setOverride(finSet, override, mass, cg); + if (!override && finSet.getCrossSection().equals(FinSet.CrossSection.AIRFOIL)) { + //Override mass anyway. This is done only for AIRFOIL because Rocksim does not compute different + //mass/cg for different cross sections, but OpenRocket does. This can lead to drastic differences + //in mass. To counteract that, the cross section value is retained but the mass/cg is overridden + //with the calculated values from Rocksim. This will best approximate the Rocksim design in OpenRocket. + BaseHandler.setOverride(finSet, true, calcMass, calcCg); + } + BaseHandler.updateComponentMaterial(finSet, materialName, Material.Type.BULK, density); + component.addChild(finSet); + } + else { + warnings.add(finSet.getComponentName() + " can not be attached to " + + component.getComponentName() + ", ignoring component."); + } + } + + + /** + * Convert the parsed Rocksim data values in this object to an instance of OpenRocket's FinSet. + * + * @param warnings the warning set to convey incompatibilities to the user + * + * @return a FinSet instance + */ + public FinSet asOpenRocket(WarningSet warnings) { + FinSet result; + + if (shapeCode == 0) { + //Trapezoidal + result = new TrapezoidFinSet(); + ((TrapezoidFinSet) result).setFinShape(rootChord, tipChord, sweepDistance, semiSpan, thickness); + } + else if (shapeCode == 1) { + //Elliptical + result = new EllipticalFinSet(); + ((EllipticalFinSet) result).setHeight(semiSpan); + ((EllipticalFinSet) result).setLength(rootChord); + } + else if (shapeCode == 2) { + + result = new FreeformFinSet(); + try { + ((FreeformFinSet) result).setPoints(toCoordinates(pointList, warnings)); + } catch (IllegalFinPointException e) { + warnings.add("Illegal fin point set. " + e.getMessage() + " Ignoring."); + } + } + else { + return null; + } + result.setThickness(thickness); + result.setName(name); + result.setFinCount(finCount); + result.setFinish(finish); + //All TTW tabs in Rocksim are relative to the front of the fin. + result.setTabRelativePosition(FinSet.TabRelativePosition.FRONT); + result.setTabHeight(tabDepth); + result.setTabLength(tabLength); + result.setTabShift(taboffset); + result.setBaseRotation(radialAngle); + result.setCrossSection(convertTipShapeCode(tipShapeCode)); + result.setRelativePosition(position); + PositionDependentHandler.setLocation(result, position, location); + return result; + + } + + /** + * Convert a Rocksim string that represents fin plan points into an array of OpenRocket coordinates. + * + * @param pointList a comma and pipe delimited string of X,Y coordinates from Rocksim. This is of the format: + *
x0,y0|x1,y1|x2,y2|... 
+ * @param warnings the warning set to convey incompatibilities to the user + * + * @return an array of OpenRocket Coordinates + */ + private Coordinate[] toCoordinates(String pointList, WarningSet warnings) { + List result = new ArrayList(); + if (pointList != null && pointList.length() > 0) { + String[] points = pointList.split("\\Q|\\E"); + for (String point : points) { + String[] aPoint = point.split(","); + try { + if (aPoint.length > 1) { + Coordinate c = new Coordinate( + Double.parseDouble(aPoint[0]) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH, + Double.parseDouble(aPoint[1]) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); + result.add(c); + } + else { + warnings.add("Invalid fin point pair."); + } + } catch (NumberFormatException nfe) { + warnings.add("Fin point not in numeric format."); + } + } + if (!result.isEmpty()) { + //OpenRocket requires fin plan points be ordered from leading root chord to trailing root chord in the + //Coordinate array. + Coordinate last = result.get(result.size() - 1); + if (last.x == 0 && last.y == 0) { + Collections.reverse(result); + } + } + } + final Coordinate[] coords = new Coordinate[result.size()]; + return result.toArray(coords); + } + + + /** + * Convert a Rocksim tip shape to an OpenRocket CrossSection. + * + * @param tipShape the tip shape code from Rocksim + * + * @return a CrossSection instance + */ + public static FinSet.CrossSection convertTipShapeCode(int tipShape) { + switch (tipShape) { + case 0: + return FinSet.CrossSection.SQUARE; + case 1: + return FinSet.CrossSection.ROUNDED; + case 2: + return FinSet.CrossSection.AIRFOIL; + default: + return FinSet.CrossSection.SQUARE; + } + } + + public static int convertTipShapeCode(FinSet.CrossSection cs) { + if (FinSet.CrossSection.ROUNDED.equals(cs)) { + return 1; + } + if (FinSet.CrossSection.AIRFOIL.equals(cs)) { + return 2; + } + return 0; + } + } - diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/InnerBodyTubeHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/InnerBodyTubeHandler.java index 0d264fe05..d6e0f2e98 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/InnerBodyTubeHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/InnerBodyTubeHandler.java @@ -3,118 +3,120 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.material.Material; import net.sf.openrocket.rocketcomponent.InnerTube; import net.sf.openrocket.rocketcomponent.RocketComponent; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * A SAX handler for Rocksim inside tubes. */ class InnerBodyTubeHandler extends PositionDependentHandler { - - /** - * The OpenRocket InnerTube instance. - */ - private final InnerTube bodyTube; - - /** - * Constructor. - * - * @param c the parent component - * @param warnings the warning set - * @throws IllegalArgumentException thrown if c is null - */ - public InnerBodyTubeHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException { - if (c == null) { - throw new IllegalArgumentException("The parent component of an inner tube may not be null."); - } - bodyTube = new InnerTube(); - if (isCompatible(c, InnerTube.class, warnings)) { - c.addChild(bodyTube); - } - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - if (RocksimCommonConstants.ATTACHED_PARTS.equals(element)) { - return new AttachedPartsHandler(bodyTube); - } - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - super.closeElement(element, attributes, content, warnings); - - try { - if (RocksimCommonConstants.OD.equals(element)) { - bodyTube.setOuterRadius(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS); - } - if (RocksimCommonConstants.ID.equals(element)) { - final double r = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS; - bodyTube.setInnerRadius(r); - } - if (RocksimCommonConstants.LEN.equals(element)) { - bodyTube.setLength(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); - } - if (RocksimCommonConstants.IS_MOTOR_MOUNT.equals(element)) { - bodyTube.setMotorMount("1".equals(content)); - } - if (RocksimCommonConstants.ENGINE_OVERHANG.equals(element)) { - bodyTube.setMotorOverhang(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); - } - if (RocksimCommonConstants.MATERIAL.equals(element)) { - setMaterialName(content); - } - if (RocksimCommonConstants.RADIAL_ANGLE.equals(element)) { - bodyTube.setRadialDirection(Double.parseDouble(content)); - } - if (RocksimCommonConstants.RADIAL_LOC.equals(element)) { - bodyTube.setRadialPosition(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); - } - } - catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - - /** - * Get the InnerTube component this handler is working upon. - * - * @return an InnerTube component - */ - @Override - public InnerTube getComponent() { - return bodyTube; - } - - /** - * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not - * public in all components. - * - * @param position the OpenRocket position - */ - @Override - public void setRelativePosition(RocketComponent.Position position) { - bodyTube.setRelativePosition(position); - } - - /** - * Get the required type of material for this component. - * - * @return BULK - */ - @Override - public Material.Type getMaterialType() { - return Material.Type.BULK; - } - + + /** + * The OpenRocket InnerTube instance. + */ + private final InnerTube bodyTube; + + /** + * Constructor. + * + * @param c the parent component + * @param warnings the warning set + * @throws IllegalArgumentException thrown if c is null + */ + public InnerBodyTubeHandler(DocumentLoadingContext context, RocketComponent c, WarningSet warnings) throws IllegalArgumentException { + super(context); + if (c == null) { + throw new IllegalArgumentException("The parent component of an inner tube may not be null."); + } + bodyTube = new InnerTube(); + if (isCompatible(c, InnerTube.class, warnings)) { + c.addChild(bodyTube); + } + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + if (RocksimCommonConstants.ATTACHED_PARTS.equals(element)) { + return new AttachedPartsHandler(context, bodyTube); + } + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.closeElement(element, attributes, content, warnings); + + try { + if (RocksimCommonConstants.OD.equals(element)) { + bodyTube.setOuterRadius(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS); + } + if (RocksimCommonConstants.ID.equals(element)) { + final double r = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS; + bodyTube.setInnerRadius(r); + } + if (RocksimCommonConstants.LEN.equals(element)) { + bodyTube.setLength(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); + } + if (RocksimCommonConstants.IS_MOTOR_MOUNT.equals(element)) { + bodyTube.setMotorMount("1".equals(content)); + } + if (RocksimCommonConstants.ENGINE_OVERHANG.equals(element)) { + bodyTube.setMotorOverhang(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); + } + if (RocksimCommonConstants.MATERIAL.equals(element)) { + setMaterialName(content); + } + if (RocksimCommonConstants.RADIAL_ANGLE.equals(element)) { + bodyTube.setRadialDirection(Double.parseDouble(content)); + } + if (RocksimCommonConstants.RADIAL_LOC.equals(element)) { + bodyTube.setRadialPosition(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); + } + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + /** + * Get the InnerTube component this handler is working upon. + * + * @return an InnerTube component + */ + @Override + public InnerTube getComponent() { + return bodyTube; + } + + /** + * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not + * public in all components. + * + * @param position the OpenRocket position + */ + @Override + public void setRelativePosition(RocketComponent.Position position) { + bodyTube.setRelativePosition(position); + } + + /** + * Get the required type of material for this component. + * + * @return BULK + */ + @Override + public Material.Type getMaterialType() { + return Material.Type.BULK; + } + } diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/LaunchLugHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/LaunchLugHandler.java index 33fd62d69..1573d69d8 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/LaunchLugHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/LaunchLugHandler.java @@ -3,7 +3,10 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.rocksim.RocksimFinishCode; import net.sf.openrocket.file.simplesax.ElementHandler; @@ -11,102 +14,100 @@ import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.material.Material; import net.sf.openrocket.rocketcomponent.LaunchLug; import net.sf.openrocket.rocketcomponent.RocketComponent; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * The SAX handler for Rocksim Launch Lugs. */ class LaunchLugHandler extends PositionDependentHandler { - - /** - * The OpenRocket LaunchLug instance. - */ - private final LaunchLug lug; - - /** - * Constructor. - * - * @param c the parent - * @param warnings the warning set - * - * @throws IllegalArgumentException thrown if c is null - */ - public LaunchLugHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException { - if (c == null) { - throw new IllegalArgumentException("The parent component of a launch lug may not be null."); - } - lug = new LaunchLug(); - if (isCompatible(c, LaunchLug.class, warnings)) { - c.addChild(lug); - } - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - super.closeElement(element, attributes, content, warnings); - - try { - if (RocksimCommonConstants.OD.equals(element)) { - lug.setOuterRadius(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); - } - if (RocksimCommonConstants.ID.equals(element)) { - lug.setInnerRadius(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); - } - if (RocksimCommonConstants.LEN.equals(element)) { - lug.setLength(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); - } - if (RocksimCommonConstants.MATERIAL.equals(element)) { - setMaterialName(content); - } - if (RocksimCommonConstants.RADIAL_ANGLE.equals(element)) { - lug.setRadialDirection(Double.parseDouble(content)); - } - if (RocksimCommonConstants.FINISH_CODE.equals(element)) { - lug.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket()); - } - } - catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - - /** - * Get the LaunchLug component this handler is working upon. - * - * @return a LaunchLug component - */ - @Override - public LaunchLug getComponent() { - return lug; - } - - /** - * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not - * public in all components. - * - * @param position the OpenRocket position - */ - @Override - public void setRelativePosition(RocketComponent.Position position) { - lug.setRelativePosition(position); - } - - /** - * Get the required type of material for this component. - * - * @return BULK - */ - @Override - public Material.Type getMaterialType() { - return Material.Type.BULK; - } + + /** + * The OpenRocket LaunchLug instance. + */ + private final LaunchLug lug; + + /** + * Constructor. + * + * @param c the parent + * @param warnings the warning set + * + * @throws IllegalArgumentException thrown if c is null + */ + public LaunchLugHandler(DocumentLoadingContext context, RocketComponent c, WarningSet warnings) throws IllegalArgumentException { + super(context); + if (c == null) { + throw new IllegalArgumentException("The parent component of a launch lug may not be null."); + } + lug = new LaunchLug(); + if (isCompatible(c, LaunchLug.class, warnings)) { + c.addChild(lug); + } + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.closeElement(element, attributes, content, warnings); + + try { + if (RocksimCommonConstants.OD.equals(element)) { + lug.setOuterRadius(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); + } + if (RocksimCommonConstants.ID.equals(element)) { + lug.setInnerRadius(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); + } + if (RocksimCommonConstants.LEN.equals(element)) { + lug.setLength(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); + } + if (RocksimCommonConstants.MATERIAL.equals(element)) { + setMaterialName(content); + } + if (RocksimCommonConstants.RADIAL_ANGLE.equals(element)) { + lug.setRadialDirection(Double.parseDouble(content)); + } + if (RocksimCommonConstants.FINISH_CODE.equals(element)) { + lug.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket()); + } + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + /** + * Get the LaunchLug component this handler is working upon. + * + * @return a LaunchLug component + */ + @Override + public LaunchLug getComponent() { + return lug; + } + + /** + * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not + * public in all components. + * + * @param position the OpenRocket position + */ + @Override + public void setRelativePosition(RocketComponent.Position position) { + lug.setRelativePosition(position); + } + + /** + * Get the required type of material for this component. + * + * @return BULK + */ + @Override + public Material.Type getMaterialType() { + return Material.Type.BULK; + } } - diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/MassObjectHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/MassObjectHandler.java index cc2ddee49..33890dce3 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/MassObjectHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/MassObjectHandler.java @@ -3,7 +3,10 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.rocksim.RocksimDensityType; import net.sf.openrocket.file.simplesax.ElementHandler; @@ -14,193 +17,191 @@ import net.sf.openrocket.rocketcomponent.MassComponent; import net.sf.openrocket.rocketcomponent.MassObject; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.ShockCord; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * A SAX handler for Rocksim's MassObject XML type. */ class MassObjectHandler extends PositionDependentHandler { - - /** - * The Rocksim Mass length fudge factor. Rocksim completely exaggerates the length of a mass object to the point - * that it looks ridiculous in OpenRocket. This fudge factor is here merely to get the typical mass object to - * render in the OpenRocket UI with it's bounds mostly inside it's parent. The odd thing about it is that Rocksim - * does not expose the length of a mass object in the UI and actually treats mass objects as point objects - not 3 - * or even 2 dimensional. - */ - public static final int MASS_LEN_FUDGE_FACTOR = 100; - - /** - * The OpenRocket MassComponent - counterpart to the RS MassObject. - */ - private final MassComponent mass; - - /** - * Reference to answer for getComponent(). - */ - private MassObject current; - - /** - * Parent. - */ - private RocketComponent parent; - - /** - * 0 == General, 1 == Shock Cord - */ - private int typeCode = 0; - - /** - * Constructor. l - * - * @param c the parent component - * @param warnings the warning set - * - * @throws IllegalArgumentException thrown if c is null - */ - public MassObjectHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException { - if (c == null) { - throw new IllegalArgumentException("The parent component of a mass component may not be null."); - } - mass = new MassComponent(); - current = mass; - parent = c; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - super.closeElement(element, attributes, content, warnings); - try { - if (RocksimCommonConstants.LEN.equals(element)) { - mass.setLength(Double.parseDouble(content) / (RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); - } - if (RocksimCommonConstants.KNOWN_MASS.equals(element)) { - mass.setComponentMass(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS); - } - if (RocksimCommonConstants.KNOWN_CG.equals(element)) { - //Setting the CG of the Mass Object to 0 is important because of the different ways that Rocksim and - //OpenRocket treat mass objects. Rocksim treats them as points (even though the data file contains a - //length) and because Rocksim sets the CG of the mass object to really be relative to the front of - //the parent. But that value is already assumed in the position and position value for the component. - //Thus it needs to be set to 0 to say that the mass object's CG is at the point of the mass object. - super.setCG(0); - } - if (RocksimCommonConstants.TYPE_CODE.equals(element)) { - typeCode = Integer.parseInt(content); - } - if (RocksimCommonConstants.MATERIAL.equals(element)) { - setMaterialName(content); - } - } - catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - - @Override - public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws - SAXException { - if (inferAsShockCord(typeCode, warnings)) { //Shock Cord - mapMassObjectAsShockCord(element, attributes, content, warnings); - } - else { // typeCode == 0 General Mass Object - if (isCompatible(parent, MassComponent.class, warnings)) { - parent.addChild(mass); - } - super.endHandler(element, attributes, content, warnings); - } - } - - /** - * Rocksim does not have a separate entity for Shock Cords. It has to be inferred. Sometimes the typeCode - * indicates it's a shock cord, but most times it does not. This is due to bugs in the Rocksim Component and - * Material databases. Try to infer a shock cord based on it's length and it's material type. It's somewhat - * arbitrary, but if the mass object's length is more than twice the length of it's parent component and it's a LINE - * material, then assume a shock cord. - * - * @param theTypeCode the code from the RKT XML file - * - * @return true if we think it's a shock cord - */ - private boolean inferAsShockCord(int theTypeCode, WarningSet warnings) { - return (theTypeCode == 1 || (mass.getLength() >= 2 * parent.getLength() && RocksimDensityType.ROCKSIM_LINE - .equals(getDensityType()))) && isCompatible(parent, ShockCord.class, warnings, true); - } - - /** - * If it appears that the mass object is a shock cord, then create an OR shock cord instance. - * - * @param element the element name - * @param attributes the attributes - * @param content the content of the element - * @param warnings the warning set to store warnings in. - * - * @throws org.xml.sax.SAXException not thrown - */ - private void mapMassObjectAsShockCord(final String element, final HashMap attributes, - final String content, final WarningSet warnings) throws SAXException { - ShockCord cord = new ShockCord(); - current = cord; - if (isCompatible(parent, ShockCord.class, warnings)) { - parent.addChild(cord); - } - super.endHandler(element, attributes, content, warnings); - cord.setName(mass.getName()); - - setOverride(cord, mass.isMassOverridden(), mass.getOverrideMass(), mass.getOverrideCGX()); - - cord.setRadialDirection(mass.getRadialDirection()); - cord.setRadialPosition(mass.getRadialPosition()); - cord.setRadius(mass.getRadius()); - - //Rocksim does not distinguish between total length of the cord and the packed length. Fudge the - //packed length and set the real length. - cord.setCordLength(mass.getLength()); - cord.setLength(cord.getCordLength() / MASS_LEN_FUDGE_FACTOR); - if (parent instanceof Coaxial) { - Coaxial parentCoaxial = (Coaxial) parent; - cord.setRadius(parentCoaxial.getInnerRadius()); - } - } - - /** - * Get the component this handler is working upon. This changes depending upon the type of mass object. - * - * @return a component - */ - @Override - public MassObject getComponent() { - return current; - } - - /** - * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not - * public in all components. - * - * @param position the OpenRocket position - */ - @Override - public void setRelativePosition(RocketComponent.Position position) { - current.setRelativePosition(position); - } - - /** - * Get the required type of material for this component. Does not apply to MassComponents, but does apply to Shock - * Cords. - * - * @return LINE - */ - @Override - public Material.Type getMaterialType() { - return Material.Type.LINE; - } - + + /** + * The Rocksim Mass length fudge factor. Rocksim completely exaggerates the length of a mass object to the point + * that it looks ridiculous in OpenRocket. This fudge factor is here merely to get the typical mass object to + * render in the OpenRocket UI with it's bounds mostly inside it's parent. The odd thing about it is that Rocksim + * does not expose the length of a mass object in the UI and actually treats mass objects as point objects - not 3 + * or even 2 dimensional. + */ + public static final int MASS_LEN_FUDGE_FACTOR = 100; + + /** + * The OpenRocket MassComponent - counterpart to the RS MassObject. + */ + private final MassComponent mass; + + /** + * Reference to answer for getComponent(). + */ + private MassObject current; + + /** + * Parent. + */ + private RocketComponent parent; + + /** + * 0 == General, 1 == Shock Cord + */ + private int typeCode = 0; + + /** + * Constructor. l + * + * @param c the parent component + * @param warnings the warning set + * + * @throws IllegalArgumentException thrown if c is null + */ + public MassObjectHandler(DocumentLoadingContext context, RocketComponent c, WarningSet warnings) throws IllegalArgumentException { + super(context); + if (c == null) { + throw new IllegalArgumentException("The parent component of a mass component may not be null."); + } + mass = new MassComponent(); + current = mass; + parent = c; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.closeElement(element, attributes, content, warnings); + try { + if (RocksimCommonConstants.LEN.equals(element)) { + mass.setLength(Double.parseDouble(content) / (RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); + } + if (RocksimCommonConstants.KNOWN_MASS.equals(element)) { + mass.setComponentMass(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS); + } + if (RocksimCommonConstants.KNOWN_CG.equals(element)) { + //Setting the CG of the Mass Object to 0 is important because of the different ways that Rocksim and + //OpenRocket treat mass objects. Rocksim treats them as points (even though the data file contains a + //length) and because Rocksim sets the CG of the mass object to really be relative to the front of + //the parent. But that value is already assumed in the position and position value for the component. + //Thus it needs to be set to 0 to say that the mass object's CG is at the point of the mass object. + super.setCG(0); + } + if (RocksimCommonConstants.TYPE_CODE.equals(element)) { + typeCode = Integer.parseInt(content); + } + if (RocksimCommonConstants.MATERIAL.equals(element)) { + setMaterialName(content); + } + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + @Override + public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws + SAXException { + if (inferAsShockCord(typeCode, warnings)) { //Shock Cord + mapMassObjectAsShockCord(element, attributes, content, warnings); + } + else { // typeCode == 0 General Mass Object + if (isCompatible(parent, MassComponent.class, warnings)) { + parent.addChild(mass); + } + super.endHandler(element, attributes, content, warnings); + } + } + + /** + * Rocksim does not have a separate entity for Shock Cords. It has to be inferred. Sometimes the typeCode + * indicates it's a shock cord, but most times it does not. This is due to bugs in the Rocksim Component and + * Material databases. Try to infer a shock cord based on it's length and it's material type. It's somewhat + * arbitrary, but if the mass object's length is more than twice the length of it's parent component and it's a LINE + * material, then assume a shock cord. + * + * @param theTypeCode the code from the RKT XML file + * + * @return true if we think it's a shock cord + */ + private boolean inferAsShockCord(int theTypeCode, WarningSet warnings) { + return (theTypeCode == 1 || (mass.getLength() >= 2 * parent.getLength() && RocksimDensityType.ROCKSIM_LINE + .equals(getDensityType()))) && isCompatible(parent, ShockCord.class, warnings, true); + } + + /** + * If it appears that the mass object is a shock cord, then create an OR shock cord instance. + * + * @param element the element name + * @param attributes the attributes + * @param content the content of the element + * @param warnings the warning set to store warnings in. + * + * @throws org.xml.sax.SAXException not thrown + */ + private void mapMassObjectAsShockCord(final String element, final HashMap attributes, + final String content, final WarningSet warnings) throws SAXException { + ShockCord cord = new ShockCord(); + current = cord; + if (isCompatible(parent, ShockCord.class, warnings)) { + parent.addChild(cord); + } + super.endHandler(element, attributes, content, warnings); + cord.setName(mass.getName()); + + setOverride(cord, mass.isMassOverridden(), mass.getOverrideMass(), mass.getOverrideCGX()); + + cord.setRadialDirection(mass.getRadialDirection()); + cord.setRadialPosition(mass.getRadialPosition()); + cord.setRadius(mass.getRadius()); + + //Rocksim does not distinguish between total length of the cord and the packed length. Fudge the + //packed length and set the real length. + cord.setCordLength(mass.getLength()); + cord.setLength(cord.getCordLength() / MASS_LEN_FUDGE_FACTOR); + if (parent instanceof Coaxial) { + Coaxial parentCoaxial = (Coaxial) parent; + cord.setRadius(parentCoaxial.getInnerRadius()); + } + } + + /** + * Get the component this handler is working upon. This changes depending upon the type of mass object. + * + * @return a component + */ + @Override + public MassObject getComponent() { + return current; + } + + /** + * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not + * public in all components. + * + * @param position the OpenRocket position + */ + public void setRelativePosition(RocketComponent.Position position) { + current.setRelativePosition(position); + } + + /** + * Get the required type of material for this component. Does not apply to MassComponents, but does apply to Shock + * Cords. + * + * @return LINE + */ + @Override + public Material.Type getMaterialType() { + return Material.Type.LINE; + } + } diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/NoseConeHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/NoseConeHandler.java index 49de907c0..a43e1c66a 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/NoseConeHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/NoseConeHandler.java @@ -3,7 +3,10 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.rocksim.RocksimFinishCode; import net.sf.openrocket.file.rocksim.RocksimNoseConeCode; @@ -13,143 +16,141 @@ import net.sf.openrocket.material.Material; import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Transition; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * The SAX nose cone handler for Rocksim NoseCones. */ class NoseConeHandler extends BaseHandler { - - /** - * The OpenRocket NoseCone. - */ - private final NoseCone noseCone = new NoseCone(); - - /** - * The wall thickness. Used for hollow nose cones. - */ - private double thickness = 0d; - - /** - * Constructor. - * - * @param c the parent component to the nosecone - * @param warnings the warning set - * - * @throws IllegalArgumentException thrown if c is null - */ - public NoseConeHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException { - if (c == null) { - throw new IllegalArgumentException("The parent component of a nose cone may not be null."); - } - if (isCompatible(c, NoseCone.class, warnings)) { - c.addChild(noseCone); - noseCone.setAftRadiusAutomatic(false); - } - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - //Nose cones in Rocksim may have attached parts - namely Mass Objects - as children. - if (RocksimCommonConstants.ATTACHED_PARTS.equals(element)) { - return new AttachedPartsHandler(noseCone); - } - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - super.closeElement(element, attributes, content, warnings); - - try { - if (RocksimCommonConstants.SHAPE_CODE.equals(element)) { - noseCone.setType(RocksimNoseConeCode.fromCode(Integer.parseInt(content)).asOpenRocket()); - } - if (RocksimCommonConstants.LEN.equals(element)) { - noseCone.setLength(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); - } - if (RocksimCommonConstants.BASE_DIA.equals(element)) { - noseCone.setAftRadius(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); - } - if (RocksimCommonConstants.WALL_THICKNESS.equals(element)) { - thickness = Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); - } - if (RocksimCommonConstants.SHOULDER_OD.equals(element)) { - noseCone.setAftShoulderRadius(Math.max(0, Double.parseDouble( - content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); - } - if (RocksimCommonConstants.SHOULDER_LEN.equals(element)) { - noseCone.setAftShoulderLength(Math.max(0, Double.parseDouble( - content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); - } - if (RocksimCommonConstants.SHAPE_PARAMETER.equals(element)) { - //The Rocksim ShapeParameter only applies to certain shapes, although it is included - //in the design file for all nose cones. Applying it when it should not be causes oddities so - //a check is made for the allowable shapes. - if (Transition.Shape.POWER.equals(noseCone.getType()) || - Transition.Shape.HAACK.equals(noseCone.getType()) || - Transition.Shape.PARABOLIC.equals(noseCone.getType())) { - noseCone.setShapeParameter(Double.parseDouble(content)); - } - } - if (RocksimCommonConstants.CONSTRUCTION_TYPE.equals(element)) { - int typeCode = Integer.parseInt(content); - if (typeCode == 0) { - //SOLID - noseCone.setFilled(true); - } - else if (typeCode == 1) { - //HOLLOW - noseCone.setFilled(false); - } - } - if (RocksimCommonConstants.FINISH_CODE.equals(element)) { - noseCone.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket()); - } - if (RocksimCommonConstants.MATERIAL.equals(element)) { - setMaterialName(content); - } - } - catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - - @Override - public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - super.endHandler(element, attributes, content, warnings); - - if (noseCone.isFilled()) { - noseCone.setAftShoulderThickness(noseCone.getAftShoulderRadius()); - } - else { - noseCone.setThickness(thickness); - noseCone.setAftShoulderThickness(thickness); - } - } - - /** - * Get the nose cone component this handler is working upon. - * - * @return a nose cone component - */ - @Override - public NoseCone getComponent() { - return noseCone; - } - - /** - * Get the required type of material for this component. - * - * @return BULK - */ - @Override - public Material.Type getMaterialType() { - return Material.Type.BULK; - } - + + /** + * The OpenRocket NoseCone. + */ + private final NoseCone noseCone = new NoseCone(); + + /** + * The wall thickness. Used for hollow nose cones. + */ + private double thickness = 0d; + + /** + * Constructor. + * + * @param c the parent component to the nosecone + * @param warnings the warning set + * + * @throws IllegalArgumentException thrown if c is null + */ + public NoseConeHandler(DocumentLoadingContext context, RocketComponent c, WarningSet warnings) throws IllegalArgumentException { + super(context); + if (c == null) { + throw new IllegalArgumentException("The parent component of a nose cone may not be null."); + } + if (isCompatible(c, NoseCone.class, warnings)) { + c.addChild(noseCone); + noseCone.setAftRadiusAutomatic(false); + } + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + //Nose cones in Rocksim may have attached parts - namely Mass Objects - as children. + if (RocksimCommonConstants.ATTACHED_PARTS.equals(element)) { + return new AttachedPartsHandler(context, noseCone); + } + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + super.closeElement(element, attributes, content, warnings); + + try { + if (RocksimCommonConstants.SHAPE_CODE.equals(element)) { + noseCone.setType(RocksimNoseConeCode.fromCode(Integer.parseInt(content)).asOpenRocket()); + } + if (RocksimCommonConstants.LEN.equals(element)) { + noseCone.setLength(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); + } + if (RocksimCommonConstants.BASE_DIA.equals(element)) { + noseCone.setAftRadius(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); + } + if (RocksimCommonConstants.WALL_THICKNESS.equals(element)) { + thickness = Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); + } + if (RocksimCommonConstants.SHOULDER_OD.equals(element)) { + noseCone.setAftShoulderRadius(Math.max(0, Double.parseDouble( + content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); + } + if (RocksimCommonConstants.SHOULDER_LEN.equals(element)) { + noseCone.setAftShoulderLength(Math.max(0, Double.parseDouble( + content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); + } + if (RocksimCommonConstants.SHAPE_PARAMETER.equals(element)) { + //The Rocksim ShapeParameter only applies to certain shapes, although it is included + //in the design file for all nose cones. Applying it when it should not be causes oddities so + //a check is made for the allowable shapes. + if (Transition.Shape.POWER.equals(noseCone.getType()) || + Transition.Shape.HAACK.equals(noseCone.getType()) || + Transition.Shape.PARABOLIC.equals(noseCone.getType())) { + noseCone.setShapeParameter(Double.parseDouble(content)); + } + } + if (RocksimCommonConstants.CONSTRUCTION_TYPE.equals(element)) { + int typeCode = Integer.parseInt(content); + if (typeCode == 0) { + //SOLID + noseCone.setFilled(true); + } + else if (typeCode == 1) { + //HOLLOW + noseCone.setFilled(false); + } + } + if (RocksimCommonConstants.FINISH_CODE.equals(element)) { + noseCone.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket()); + } + if (RocksimCommonConstants.MATERIAL.equals(element)) { + setMaterialName(content); + } + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + @Override + public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.endHandler(element, attributes, content, warnings); + + if (noseCone.isFilled()) { + noseCone.setAftShoulderThickness(noseCone.getAftShoulderRadius()); + } + else { + noseCone.setThickness(thickness); + noseCone.setAftShoulderThickness(thickness); + } + } + + /** + * Get the nose cone component this handler is working upon. + * + * @return a nose cone component + */ + @Override + public NoseCone getComponent() { + return noseCone; + } + + /** + * Get the required type of material for this component. + * + * @return BULK + */ + public Material.Type getMaterialType() { + return Material.Type.BULK; + } + } diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/ParachuteHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/ParachuteHandler.java index dedfad96a..eb2ec4564 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/ParachuteHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/ParachuteHandler.java @@ -3,7 +3,10 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; @@ -12,112 +15,110 @@ import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.InnerTube; import net.sf.openrocket.rocketcomponent.Parachute; import net.sf.openrocket.rocketcomponent.RocketComponent; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * A SAX handler for Rocksim's Parachute XML type. */ class ParachuteHandler extends RecoveryDeviceHandler { - /** - * The OpenRocket Parachute instance - */ - private final Parachute chute; - /** - * The shroud line density. - */ - private double shroudLineDensity = 0.0d; - - /** - * Constructor. - * - * @param c the parent component - * @param warnings the warning set - * - * @throws IllegalArgumentException thrown if c is null - */ - public ParachuteHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException { - if (c == null) { - throw new IllegalArgumentException("The parent of a parachute may not be null."); - } - chute = new Parachute(); - if (isCompatible(c, Parachute.class, warnings)) { - c.addChild(chute); - } - } - - /** - * {@inheritDoc} - */ - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - /** - * {@inheritDoc} - */ - @Override - public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - super.closeElement(element, attributes, content, warnings); - try { - if (RocksimCommonConstants.DIAMETER.equals(element)) { - chute.setDiameter(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); - /* Rocksim doesn't have a packed parachute radius, so we approximate it. */ - double packed; - RocketComponent parent = chute.getParent(); - if (parent instanceof BodyTube) { - packed = ((BodyTube) parent).getOuterRadius() * 0.9; - } - else if (parent instanceof InnerTube) { - packed = ((InnerTube) parent).getInnerRadius() * 0.9; - } - else { - packed = chute.getDiameter() * 0.025; - } - chute.setRadius(packed); - } - if (RocksimCommonConstants.SHROUD_LINE_COUNT.equals(element)) { - chute.setLineCount(Math.max(0, Integer.parseInt(content))); - } - if (RocksimCommonConstants.SHROUD_LINE_LEN.equals(element)) { - chute.setLineLength(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); - } - if (RocksimCommonConstants.SPILL_HOLE_DIA.equals(element)) { - //Not supported in OpenRocket - @SuppressWarnings("unused") + /** + * The OpenRocket Parachute instance + */ + private final Parachute chute; + /** + * The shroud line density. + */ + private double shroudLineDensity = 0.0d; + + /** + * Constructor. + * + * @param c the parent component + * @param warnings the warning set + * + * @throws IllegalArgumentException thrown if c is null + */ + public ParachuteHandler(DocumentLoadingContext context, RocketComponent c, WarningSet warnings) throws IllegalArgumentException { + super(context); + if (c == null) { + throw new IllegalArgumentException("The parent of a parachute may not be null."); + } + chute = new Parachute(); + if (isCompatible(c, Parachute.class, warnings)) { + c.addChild(chute); + } + } + + /** + * {@inheritDoc} + */ + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + /** + * {@inheritDoc} + */ + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.closeElement(element, attributes, content, warnings); + try { + if (RocksimCommonConstants.DIAMETER.equals(element)) { + chute.setDiameter(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); + /* Rocksim doesn't have a packed parachute radius, so we approximate it. */ + double packed; + RocketComponent parent = chute.getParent(); + if (parent instanceof BodyTube) { + packed = ((BodyTube) parent).getOuterRadius() * 0.9; + } + else if (parent instanceof InnerTube) { + packed = ((InnerTube) parent).getInnerRadius() * 0.9; + } + else { + packed = chute.getDiameter() * 0.025; + } + chute.setRadius(packed); + } + if (RocksimCommonConstants.SHROUD_LINE_COUNT.equals(element)) { + chute.setLineCount(Math.max(0, Integer.parseInt(content))); + } + if (RocksimCommonConstants.SHROUD_LINE_LEN.equals(element)) { + chute.setLineLength(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); + } + if (RocksimCommonConstants.SPILL_HOLE_DIA.equals(element)) { + //Not supported in OpenRocket double spillHoleRadius = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS; - warnings.add("Parachute spill holes are not supported. Ignoring."); - } - if (RocksimCommonConstants.SHROUD_LINE_MASS_PER_MM.equals(element)) { - shroudLineDensity = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LINE_DENSITY; - } - if (RocksimCommonConstants.SHROUD_LINE_MATERIAL.equals(element)) { - chute.setLineMaterial(createCustomMaterial(Material.Type.LINE, content, shroudLineDensity)); - } - if (RocksimCommonConstants.DRAG_COEFFICIENT.equals(element)) { - chute.setCD(Double.parseDouble(content)); - } - if (RocksimCommonConstants.MATERIAL.equals(element)) { - setMaterialName(content); - } - } - catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - - /** - * Get the component this handler is working upon. - * - * @return a component - */ - @Override - public Parachute getComponent() { - return chute; - } - + if (spillHoleRadius > 0) { + warnings.add("Parachute spill holes are not supported. Ignoring."); + } + } + if (RocksimCommonConstants.SHROUD_LINE_MASS_PER_MM.equals(element)) { + shroudLineDensity = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LINE_DENSITY; + } + if (RocksimCommonConstants.SHROUD_LINE_MATERIAL.equals(element)) { + chute.setLineMaterial(createCustomMaterial(Material.Type.LINE, content, shroudLineDensity)); + } + if (RocksimCommonConstants.DRAG_COEFFICIENT.equals(element)) { + chute.setCD(Double.parseDouble(content)); + } + if (RocksimCommonConstants.MATERIAL.equals(element)) { + setMaterialName(content); + } + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + /** + * Get the component this handler is working upon. + * + * @return a component + */ + public Parachute getComponent() { + return chute; + } + } - diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/PositionDependentHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/PositionDependentHandler.java index 0d04128a5..2cf4a6130 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/PositionDependentHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/PositionDependentHandler.java @@ -3,13 +3,15 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.rocksim.RocksimLocationMode; import net.sf.openrocket.rocketcomponent.RocketComponent; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * An abstract base class that handles position dependencies for all lower level components that @@ -18,70 +20,74 @@ import java.util.HashMap; * @param the specific position dependent RocketComponent subtype for which the concrete handler can create */ public abstract class PositionDependentHandler extends BaseHandler { - - /** Temporary position value. */ - private Double positionValue = 0d; - - /** Temporary position. */ - private RocketComponent.Position position = RocketComponent.Position.TOP; - - /** - * {@inheritDoc} - */ - @Override - public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - super.closeElement(element, attributes, content, warnings); - if (RocksimCommonConstants.XB.equals(element)) { - positionValue = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if (RocksimCommonConstants.LOCATION_MODE.equals(element)) { - position = RocksimLocationMode.fromCode(Integer.parseInt( - content)).asOpenRocket(); - } - } - - /** - * This method sets the position information onto the component. Rocksim splits the location/position - * information into two disparate data elements. Both pieces of data are necessary to map into OpenRocket's - * position model. - * - * @param element the element name - * @param attributes the attributes - * @param content the content of the element - * @param warnings the warning set to store warnings in. - * @throws org.xml.sax.SAXException not thrown - */ - @Override - public void endHandler(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - super.endHandler(element, attributes, content, warnings); - setRelativePosition(position); - setLocation(getComponent(), position, positionValue); - } - - /** - * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not - * public in all components. - * - * @param position the OpenRocket position - */ - protected abstract void setRelativePosition(RocketComponent.Position position); - - /** - * Set the position of a component. - * - * @param component the component - * @param position the relative position - * @param location the actual position value - */ - public static void setLocation(RocketComponent component, RocketComponent.Position position, double location) { - if (position.equals(RocketComponent.Position.BOTTOM)) { - component.setPositionValue(-1d * location); - } - else { - component.setPositionValue(location); - } - } - + + /** Temporary position value. */ + private Double positionValue = 0d; + + /** Temporary position. */ + private RocketComponent.Position position = RocketComponent.Position.TOP; + + public PositionDependentHandler(DocumentLoadingContext context) { + super(context); + } + + /** + * {@inheritDoc} + */ + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.closeElement(element, attributes, content, warnings); + if (RocksimCommonConstants.XB.equals(element)) { + positionValue = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if (RocksimCommonConstants.LOCATION_MODE.equals(element)) { + position = RocksimLocationMode.fromCode(Integer.parseInt( + content)).asOpenRocket(); + } + } + + /** + * This method sets the position information onto the component. Rocksim splits the location/position + * information into two disparate data elements. Both pieces of data are necessary to map into OpenRocket's + * position model. + * + * @param element the element name + * @param attributes the attributes + * @param content the content of the element + * @param warnings the warning set to store warnings in. + * @throws org.xml.sax.SAXException not thrown + */ + @Override + public void endHandler(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + super.endHandler(element, attributes, content, warnings); + setRelativePosition(position); + setLocation(getComponent(), position, positionValue); + } + + /** + * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not + * public in all components. + * + * @param position the OpenRocket position + */ + protected abstract void setRelativePosition(RocketComponent.Position position); + + /** + * Set the position of a component. + * + * @param component the component + * @param position the relative position + * @param location the actual position value + */ + public static void setLocation(RocketComponent component, RocketComponent.Position position, double location) { + if (position.equals(RocketComponent.Position.BOTTOM)) { + component.setPositionValue(-1d * location); + } + else { + component.setPositionValue(location); + } + } + } diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/RecoveryDeviceHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/RecoveryDeviceHandler.java index bf608a01f..b175cc4c7 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/RecoveryDeviceHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/RecoveryDeviceHandler.java @@ -3,15 +3,17 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.rocksim.RocksimDensityType; import net.sf.openrocket.material.Material; import net.sf.openrocket.rocketcomponent.RecoveryDevice; import net.sf.openrocket.rocketcomponent.RocketComponent; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * A handler specific to streamers and parachutes. This is done because Rocksim allows any type of material to be @@ -21,97 +23,99 @@ import java.util.HashMap; * @param either a Streamer or Parachute */ public abstract class RecoveryDeviceHandler extends PositionDependentHandler { - - /** - * The thickness. Not used by every component, and some component handlers may parse it for their own purposes. - */ - private double thickness = 0d; - /** - * The Rocksim calculated mass. Used only when not overridden and when Rocksim says density == 0 (Rocksim bug). - */ - private Double calcMass = 0d; - - /** - * {@inheritDoc} - */ - @Override - public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - super.closeElement(element, attributes, content, warnings); - - try { - if (RocksimCommonConstants.THICKNESS.equals(element)) { - thickness = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if (RocksimCommonConstants.CALC_MASS.equals(element)) { - calcMass = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS); - } - } - catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - - - /** - * Compute the density. Rocksim does strange things with densities. For some streamer material it's in cubic, - * rather than square, units. In those cases it needs to be converted to an appropriate SURFACE material density. - * - * @param type the rocksim density - * @param rawDensity the density as specified in the Rocksim design file - * @return a value in OpenRocket SURFACE density units - */ - @Override - protected double computeDensity(RocksimDensityType type, double rawDensity) { - - double result; - - if (rawDensity > 0d) { - //ROCKSIM_SURFACE is a square area density; compute normally - //ROCKSIM_LINE is a single length dimension (kg/m) but Rocksim ignores thickness for this type and treats - //it like a SURFACE. - if (RocksimDensityType.ROCKSIM_SURFACE.equals(type) || RocksimDensityType.ROCKSIM_LINE.equals(type)) { - result = rawDensity / RocksimDensityType.ROCKSIM_SURFACE.asOpenRocket(); - } - //ROCKSIM_BULK is a cubic area density; multiple by thickness to make per square area; the result, when - //multiplied by the area will then equal Rocksim's computed mass. - else { - result = (rawDensity / type.asOpenRocket()) * thickness; - } - } - else { - result = calcMass / getComponent().getArea(); - //A Rocksim bug on streamers/parachutes results in a 0 density at times. When that is detected, try - //to compute an approximate density from Rocksim's computed mass. - if (RocksimDensityType.ROCKSIM_BULK.equals(type)) { - //ROCKSIM_BULK is a cubic area density; multiple by thickness to make per square area - result *= thickness; - } - } - return result; - } - - /** - * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not - * public in all components. - * - * @param position the OpenRocket position - */ - @Override - public void setRelativePosition(RocketComponent.Position position) { - getComponent().setRelativePosition(position); - } - - /** - * Get the required type of material for this component. This is the OpenRocket type, which does NOT always - * correspond to Rocksim. Some streamer material is defined as BULK in the Rocksim file. In those cases - * it is adjusted in this handler. - * - * @return SURFACE - */ - @Override - public Material.Type getMaterialType() { - return Material.Type.SURFACE; - } - + + /** + * The thickness. Not used by every component, and some component handlers may parse it for their own purposes. + */ + private double thickness = 0d; + /** + * The Rocksim calculated mass. Used only when not overridden and when Rocksim says density == 0 (Rocksim bug). + */ + private Double calcMass = 0d; + + public RecoveryDeviceHandler(DocumentLoadingContext context) { + super(context); + } + + /** + * {@inheritDoc} + */ + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.closeElement(element, attributes, content, warnings); + + try { + if (RocksimCommonConstants.THICKNESS.equals(element)) { + thickness = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if (RocksimCommonConstants.CALC_MASS.equals(element)) { + calcMass = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS); + } + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + + /** + * Compute the density. Rocksim does strange things with densities. For some streamer material it's in cubic, + * rather than square, units. In those cases it needs to be converted to an appropriate SURFACE material density. + * + * @param type the rocksim density + * @param rawDensity the density as specified in the Rocksim design file + * @return a value in OpenRocket SURFACE density units + */ + protected double computeDensity(RocksimDensityType type, double rawDensity) { + + double result; + + if (rawDensity > 0d) { + //ROCKSIM_SURFACE is a square area density; compute normally + //ROCKSIM_LINE is a single length dimension (kg/m) but Rocksim ignores thickness for this type and treats + //it like a SURFACE. + if (RocksimDensityType.ROCKSIM_SURFACE.equals(type) || RocksimDensityType.ROCKSIM_LINE.equals(type)) { + result = rawDensity / RocksimDensityType.ROCKSIM_SURFACE.asOpenRocket(); + } + //ROCKSIM_BULK is a cubic area density; multiple by thickness to make per square area; the result, when + //multiplied by the area will then equal Rocksim's computed mass. + else { + result = (rawDensity / type.asOpenRocket()) * thickness; + } + } + else { + result = calcMass / getComponent().getArea(); + //A Rocksim bug on streamers/parachutes results in a 0 density at times. When that is detected, try + //to compute an approximate density from Rocksim's computed mass. + if (RocksimDensityType.ROCKSIM_BULK.equals(type)) { + //ROCKSIM_BULK is a cubic area density; multiple by thickness to make per square area + result *= thickness; + } + } + return result; + } + + /** + * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not + * public in all components. + * + * @param position the OpenRocket position + */ + @Override + public void setRelativePosition(RocketComponent.Position position) { + getComponent().setRelativePosition(position); + } + + /** + * Get the required type of material for this component. This is the OpenRocket type, which does NOT always + * correspond to Rocksim. Some streamer material is defined as BULK in the Rocksim file. In those cases + * it is adjusted in this handler. + * + * @return SURFACE + */ + @Override + public Material.Type getMaterialType() { + return Material.Type.SURFACE; + } + } diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/RingHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/RingHandler.java index 3418718aa..51d43789e 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/RingHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/RingHandler.java @@ -3,7 +3,10 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; @@ -14,221 +17,221 @@ import net.sf.openrocket.rocketcomponent.EngineBlock; import net.sf.openrocket.rocketcomponent.RingComponent; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.TubeCoupler; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * A SAX handler for centering rings, tube couplers, and bulkheads. */ class RingHandler extends PositionDependentHandler { - - /** - * The OpenRocket Ring. - */ - private final CenteringRing ring = new CenteringRing(); - - /** - * The parent component. - */ - private final RocketComponent parent; - - /** - * The parsed Rocksim UsageCode. - */ - private int usageCode = 0; - - /** - * Constructor. - * - * @param theParent the parent component - * @param warnings the warning set - * @throws IllegalArgumentException thrown if c is null - */ - public RingHandler(RocketComponent theParent, WarningSet warnings) throws IllegalArgumentException { - if (theParent == null) { - throw new IllegalArgumentException("The parent of a ring may not be null."); - } - parent = theParent; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - super.closeElement(element, attributes, content, warnings); - - try { - if (RocksimCommonConstants.OD.equals(element)) { - ring.setOuterRadius(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS); - } - if (RocksimCommonConstants.ID.equals(element)) { - ring.setInnerRadius(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS); - } - if (RocksimCommonConstants.LEN.equals(element)) { - ring.setLength(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); - } - if (RocksimCommonConstants.MATERIAL.equals(element)) { - setMaterialName(content); - } - if (RocksimCommonConstants.USAGE_CODE.equals(element)) { - usageCode = Integer.parseInt(content); - } - } catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - - /** - * Get the ring component this handler is working upon. - * - * @return a component - */ - @Override - public CenteringRing getComponent() { - return ring; - } - - /** - * This method adds the CenteringRing as a child of the parent rocket component. - * - * @param warnings the warning set - */ - public void asCenteringRing(WarningSet warnings) { - - if (isCompatible(parent, CenteringRing.class, warnings)) { - parent.addChild(ring); - } - } - - /** - * Convert the parsed Rocksim data values in this object to an instance of OpenRocket's Bulkhead. - *

- * Side Effect Warning: This method adds the resulting Bulkhead as a child of the parent rocket component! - * - * @param warnings the warning set - */ - public void asBulkhead(WarningSet warnings) { - - Bulkhead result = new Bulkhead(); - - copyValues(result); - - if (isCompatible(parent, Bulkhead.class, warnings)) { - parent.addChild(result); - } - } - - /** - * Convert the parsed Rocksim data values in this object to an instance of OpenRocket's TubeCoupler. - *

- * Side Effect Warning: This method adds the resulting TubeCoupler as a child of the parent rocket component! - * - * @param warnings the warning set - */ - public void asTubeCoupler(WarningSet warnings) { - - TubeCoupler result = new TubeCoupler(); - - copyValues(result); - - if (isCompatible(parent, TubeCoupler.class, warnings)) { - parent.addChild(result); - } - } - - /** - * Convert the parsed Rocksim data values in this object to an instance of OpenRocket's Engine Block. - *

- * Side Effect Warning: This method adds the resulting EngineBlock as a child of the parent rocket component! - * - * @param warnings the warning set - */ - public void asEngineBlock(WarningSet warnings) { - - EngineBlock result = new EngineBlock(); - - copyValues(result); - - if (isCompatible(parent, EngineBlock.class, warnings)) { - parent.addChild(result); - } - } - - /** - * Copy values from the base ring to the specific component. - * - * @param result the target to which ring values will be copied - */ - private void copyValues(RingComponent result) { - result.setOuterRadius(ring.getOuterRadius()); - result.setInnerRadius(ring.getInnerRadius()); - result.setLength(ring.getLength()); - result.setName(ring.getName()); - setOverride(result, ring.isOverrideSubcomponentsEnabled(), ring.getOverrideMass(), ring.getOverrideCGX()); - result.setRelativePosition(ring.getRelativePosition()); - result.setPositionValue(ring.getPositionValue()); - result.setMaterial(ring.getMaterial()); - result.setThickness(result.getThickness()); - } - - /** - * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not - * public in all components. - * - * @param position the OpenRocket position - */ - @Override - public void setRelativePosition(RocketComponent.Position position) { - ring.setRelativePosition(position); - } - - @Override - public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { - super.endHandler(element, attributes, content, warnings); - - // The XML element in Rocksim design file is used for many types of components, unfortunately. - // Additional subelements are used to indicate the type of the rocket component. When parsing using SAX - // this poses a problem because we can't "look ahead" to see what type is being represented at the start - // of parsing - something that would be nice to do so that we can instantiate the correct OR component - // at the start, then just call setters for the appropriate data. - - // To overcome that, a CenteringRing is instantiated at the start of parsing, it's mutators are called, - // and then at the end (this method) converts the CenteringRing to a more appropriate type. CenteringRing - // is generic enough to support the representation of all similar types without loss of data. - - //UsageCode - // 0 == Centering Ring - // 1 == Bulkhead - // 2 == Engine Block - // 3 == Sleeve - // 4 == Tube Coupler - - if (usageCode == 1) { - //Bulkhead - asBulkhead(warnings); - } else if (usageCode == 2) { - asEngineBlock(warnings); - } else if (usageCode == 4) { - //TubeCoupler - asTubeCoupler(warnings); - } else { - //Default - asCenteringRing(warnings); - } - } - - /** - * Get the required type of material for this component. - * - * @return BULK - */ - @Override - public Material.Type getMaterialType() { - return Material.Type.BULK; - } + + /** + * The OpenRocket Ring. + */ + private final CenteringRing ring = new CenteringRing(); + + /** + * The parent component. + */ + private final RocketComponent parent; + + /** + * The parsed Rocksim UsageCode. + */ + private int usageCode = 0; + + /** + * Constructor. + * + * @param theParent the parent component + * @param warnings the warning set + * @throws IllegalArgumentException thrown if c is null + */ + public RingHandler(DocumentLoadingContext context, RocketComponent theParent, WarningSet warnings) throws IllegalArgumentException { + super(context); + if (theParent == null) { + throw new IllegalArgumentException("The parent of a ring may not be null."); + } + parent = theParent; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.closeElement(element, attributes, content, warnings); + + try { + if (RocksimCommonConstants.OD.equals(element)) { + ring.setOuterRadius(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS); + } + if (RocksimCommonConstants.ID.equals(element)) { + ring.setInnerRadius(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS); + } + if (RocksimCommonConstants.LEN.equals(element)) { + ring.setLength(Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); + } + if (RocksimCommonConstants.MATERIAL.equals(element)) { + setMaterialName(content); + } + if (RocksimCommonConstants.USAGE_CODE.equals(element)) { + usageCode = Integer.parseInt(content); + } + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + /** + * Get the ring component this handler is working upon. + * + * @return a component + */ + @Override + public CenteringRing getComponent() { + return ring; + } + + /** + * This method adds the CenteringRing as a child of the parent rocket component. + * + * @param warnings the warning set + */ + public void asCenteringRing(WarningSet warnings) { + + if (isCompatible(parent, CenteringRing.class, warnings)) { + parent.addChild(ring); + } + } + + /** + * Convert the parsed Rocksim data values in this object to an instance of OpenRocket's Bulkhead. + *

+ * Side Effect Warning: This method adds the resulting Bulkhead as a child of the parent rocket component! + * + * @param warnings the warning set + */ + public void asBulkhead(WarningSet warnings) { + + Bulkhead result = new Bulkhead(); + + copyValues(result); + + if (isCompatible(parent, Bulkhead.class, warnings)) { + parent.addChild(result); + } + } + + /** + * Convert the parsed Rocksim data values in this object to an instance of OpenRocket's TubeCoupler. + *

+ * Side Effect Warning: This method adds the resulting TubeCoupler as a child of the parent rocket component! + * + * @param warnings the warning set + */ + public void asTubeCoupler(WarningSet warnings) { + + TubeCoupler result = new TubeCoupler(); + + copyValues(result); + + if (isCompatible(parent, TubeCoupler.class, warnings)) { + parent.addChild(result); + } + } + + /** + * Convert the parsed Rocksim data values in this object to an instance of OpenRocket's Engine Block. + *

+ * Side Effect Warning: This method adds the resulting EngineBlock as a child of the parent rocket component! + * + * @param warnings the warning set + */ + public void asEngineBlock(WarningSet warnings) { + + EngineBlock result = new EngineBlock(); + + copyValues(result); + + if (isCompatible(parent, EngineBlock.class, warnings)) { + parent.addChild(result); + } + } + + /** + * Copy values from the base ring to the specific component. + * + * @param result the target to which ring values will be copied + */ + private void copyValues(RingComponent result) { + result.setOuterRadius(ring.getOuterRadius()); + result.setInnerRadius(ring.getInnerRadius()); + result.setLength(ring.getLength()); + result.setName(ring.getName()); + setOverride(result, ring.isOverrideSubcomponentsEnabled(), ring.getOverrideMass(), ring.getOverrideCGX()); + result.setRelativePosition(ring.getRelativePosition()); + result.setPositionValue(ring.getPositionValue()); + result.setMaterial(ring.getMaterial()); + result.setThickness(result.getThickness()); + } + + /** + * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not + * public in all components. + * + * @param position the OpenRocket position + */ + @Override + public void setRelativePosition(RocketComponent.Position position) { + ring.setRelativePosition(position); + } + + @Override + public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { + super.endHandler(element, attributes, content, warnings); + + // The XML element in Rocksim design file is used for many types of components, unfortunately. + // Additional subelements are used to indicate the type of the rocket component. When parsing using SAX + // this poses a problem because we can't "look ahead" to see what type is being represented at the start + // of parsing - something that would be nice to do so that we can instantiate the correct OR component + // at the start, then just call setters for the appropriate data. + + // To overcome that, a CenteringRing is instantiated at the start of parsing, it's mutators are called, + // and then at the end (this method) converts the CenteringRing to a more appropriate type. CenteringRing + // is generic enough to support the representation of all similar types without loss of data. + + //UsageCode + // 0 == Centering Ring + // 1 == Bulkhead + // 2 == Engine Block + // 3 == Sleeve + // 4 == Tube Coupler + + if (usageCode == 1) { + //Bulkhead + asBulkhead(warnings); + } else if (usageCode == 2) { + asEngineBlock(warnings); + } else if (usageCode == 4) { + //TubeCoupler + asTubeCoupler(warnings); + } else { + //Default + asCenteringRing(warnings); + } + } + + /** + * Get the required type of material for this component. + * + * @return BULK + */ + @Override + public Material.Type getMaterialType() { + return Material.Type.BULK; + } } \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/RockSimAppearanceBuilder.java b/core/src/net/sf/openrocket/file/rocksim/importt/RockSimAppearanceBuilder.java new file mode 100644 index 000000000..4a05f6ae3 --- /dev/null +++ b/core/src/net/sf/openrocket/file/rocksim/importt/RockSimAppearanceBuilder.java @@ -0,0 +1,171 @@ +package net.sf.openrocket.file.rocksim.importt; + +import java.io.File; +import java.io.FileNotFoundException; +import java.net.MalformedURLException; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.appearance.AppearanceBuilder; +import net.sf.openrocket.appearance.Decal.EdgeMode; +import net.sf.openrocket.document.Attachment; +import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.rocksim.RocksimCommonConstants; +import net.sf.openrocket.util.Color; + +public class RockSimAppearanceBuilder extends AppearanceBuilder { + + boolean preventSeam = false; + boolean repeat = false; + + private final DocumentLoadingContext context; + + public RockSimAppearanceBuilder(DocumentLoadingContext context) { + this.context = context; + } + + public void processElement(String element, String content, WarningSet warnings) { + try { + if (RocksimCommonConstants.TEXTURE.equals(element)) { + parseTexture(content); + } else if ("Ambient".equals(element)) { + //ignored + } else if ("Diffuse".equals(element)) { + //ignored + } else if ("Specular".equals(element)) { + //ignored + } else if ("AbientColor".equals(element)) { + setPaint(parseColor(content)); + } else if ("DiffuseColor".equals(element)) { + //ignored + } else if ("SpecularColor".equals(element)) { + //Ignored + } else if ("UseSingleColor".equals(element) || "SimpleColorModel".equals(element)) { + //Ignored + } + } catch (Exception e) { + warnings.add("Could not convert " + element + " value of " + content + ": " + e.getMessage()); + } + } + + private void parseTexture(String s) throws FileNotFoundException, MalformedURLException { + s = s.trim(); + if (s.isEmpty()) { + return; + } + final String[] parts = s.split("\\|"); + + boolean interpolate = false; + boolean flipr = false; + boolean flips = false; + boolean flipt = false; + + + for (String part : parts) { + // Sometimes it is name=(value) and sometimes name(value) + final String name = part.substring(0, part.indexOf("(")).replace("=", ""); + + final String value = part.substring(part.indexOf("(") + 1, part.length() - 1); + + if ("file".equals(name)) { + if (value.length() > 0) { + final File f = new File(value); + if (!f.exists()) { + //Find out how to get path of current rocksim file + //so I can look in it's directory + } + Attachment a = context.getAttachmentFactory().getAttachment(name); + setImage(context.getOpenRocketDocument().getDecalImage(a)); + } + } else if ("repeat".equals(name)) { + repeat = "1".equals(value); + } else if ("interpolate".equals(name)) { + interpolate = "1".equals(value); + } else if ("flipr".equals(name)) { + flipr = "1".equals(value); + } else if ("flips".equals(name)) { + flips = "1".equals(value); + } else if ("flipt".equals(name)) { + flipt = "1".equals(value); + } else if ("preventseam".equals(name)) { + preventSeam = "1".equals(value); + } else if ("position".equals(name)) { + String[] c = value.split(","); + setOffset(Double.parseDouble(c[0]), Double.parseDouble(c[1])); + } else if ("origin".equals(name)) { + String[] c = value.split(","); + setCenter(Double.parseDouble(c[0]), Double.parseDouble(c[1])); + } else if ("scale".equals(name)) { + String[] c = value.split(","); + setScaleUV(Double.parseDouble(c[0]), Double.parseDouble(c[1])); + } + } + + if (repeat) { + setEdgeMode(EdgeMode.REPEAT); + } + + if (preventSeam) { + setEdgeMode(EdgeMode.MIRROR); + } + + if (!flips) { + setScaleUV(getScaleU(), getScaleV() * -1); + setOffset(getOffsetU(), -1 - getOffsetV()); + } + if (!flipr) { + setScaleUV(getScaleU() * -1, getScaleV()); + setOffset(-1 - getOffsetU(), getOffsetV()); + } + + //TODO Make use of these values + //System.out.println("Interpolate: " + interpolate); + //System.out.println("FlipT: " + flipt); + ; + + } + + static Color weight(Color c, double w) { + return new Color((int) (c.getRed() * w), (int) (c.getGreen() * w), (int) (c.getBlue() * w), c.getAlpha()); + } + + static Color parseColor(String s) { + // blue and white came from a real file. + if ("blue".equals(s)) { + return new Color(0, 0, 255); + } + if ("white".equals(s)) { + return new Color(255, 255, 255); + } + // I guessed these are valid color names in Rksim. + if ("red".equals(s)) { + return new Color(255, 0, 0); + } + if ("green".equals(s)) { + return new Color(0, 255, 0); + } + if ("black".equals(s)) { + return new Color(0, 0, 0); + } + s = s.replace("rgb(", ""); + s = s.replace(")", ""); + String ss[] = s.split(","); + return new Color(Integer.parseInt(ss[0]), Integer.parseInt(ss[1]), Integer.parseInt(ss[2])); + } + + public boolean isPreventSeam() { + return preventSeam; + } + + public void setPreventSeam(boolean preventSeam) { + this.preventSeam = preventSeam; + } + + public boolean isRepeat() { + return repeat; + } + + public void setRepeat(boolean repeat) { + this.repeat = repeat; + } + +} diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/RocksimHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/RocksimHandler.java index bf41c1687..0178df8dc 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/RocksimHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/RocksimHandler.java @@ -4,9 +4,12 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.simplesax.AbstractElementHandler; import net.sf.openrocket.file.simplesax.ElementHandler; @@ -14,9 +17,8 @@ import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Stage; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * This class is a Sax element handler for Rocksim version 9 design files. It parses the Rocksim file (typically @@ -26,118 +28,123 @@ import java.util.HashMap; * Limitations: Rocksim flight simulations are not imported; tube fins are not supported; Rocksim 'pods' are not supported. */ public class RocksimHandler extends AbstractElementHandler { - - /** - * The main content handler. - */ - private RocksimContentHandler handler = null; - - /** - * Return the OpenRocketDocument read from the file, or null if a document - * has not been read yet. - * - * @return the document read, or null. - */ - public OpenRocketDocument getDocument() { - return handler.getDocument(); - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - - // Check for unknown elements - if (!element.equals("RockSimDocument")) { - warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); - return null; - } - - // Check for first call - if (handler != null) { - warnings.add(Warning.fromString("Multiple document elements found, ignoring later " - + "ones.")); - return null; - } - - handler = new RocksimContentHandler(); - return handler; - } - + + /** + * The main content handler. + */ + private RocksimContentHandler handler = null; + + private final DocumentLoadingContext context; + + public RocksimHandler(DocumentLoadingContext context) { + super(); + this.context = context; + } + + /** + * Return the OpenRocketDocument read from the file, or null if a document + * has not been read yet. + * + * @return the document read, or null. + */ + public OpenRocketDocument getDocument() { + return context.getOpenRocketDocument(); + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + + // Check for unknown elements + if (!element.equals("RockSimDocument")) { + warnings.add(Warning.fromString("Unknown element " + element + ", ignoring.")); + return null; + } + + // Check for first call + if (handler != null) { + warnings.add(Warning.fromString("Multiple document elements found, ignoring later " + + "ones.")); + return null; + } + + handler = new RocksimContentHandler(context); + return handler; + } + } /** * Handles the content of the tag. */ class RocksimContentHandler extends AbstractElementHandler { - /** - * The OpenRocketDocument that is the container for the rocket. - */ - private final OpenRocketDocument doc; - - /** - * The top-level component, from which all child components are added. - */ - private final Rocket rocket; - - /** - * The rocksim file version. - */ - private String version; - - /** - * Constructor. - */ - public RocksimContentHandler() { - this.rocket = new Rocket(); - this.doc = new OpenRocketDocument(rocket); - } - - /** - * Get the OpenRocket document that has been created from parsing the Rocksim design file. - * - * @return the instantiated OpenRocketDocument - */ - public OpenRocketDocument getDocument() { - return doc; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, - WarningSet warnings) { - if (RocksimCommonConstants.DESIGN_INFORMATION.equals(element)) { - //The next sub-element is "RocketDesign", which is really the only thing that matters. Rather than - //create another handler just for that element, handle it here. - return this; - } - if (RocksimCommonConstants.FILE_VERSION.equals(element)) { - return PlainTextHandler.INSTANCE; - } - if (RocksimCommonConstants.ROCKET_DESIGN.equals(element)) { - return new RocketDesignHandler(rocket); - } - return null; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - /** - * SAX handler for Rocksim file version number. The value is not used currently, but could be used in the future - * for backward/forward compatibility reasons (different lower level handlers could be called via a strategy pattern). - */ - if (RocksimCommonConstants.FILE_VERSION.equals(element)) { - version = content; - } - } - - /** - * Answer the file version. - * - * @return the version of the Rocksim design file - */ - public String getVersion() { - return version; - } + /** + * The DocumentLoadingContext + */ + private final DocumentLoadingContext context; + + /** + * The top-level component, from which all child components are added. + */ + private final Rocket rocket; + + /** + * The rocksim file version. + */ + private String version; + + public RocksimContentHandler(DocumentLoadingContext context) { + super(); + this.context = context; + this.rocket = context.getOpenRocketDocument().getRocket(); + } + + /** + * Get the OpenRocket document that has been created from parsing the Rocksim design file. + * + * @return the instantiated OpenRocketDocument + */ + public OpenRocketDocument getDocument() { + return context.getOpenRocketDocument(); + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, + WarningSet warnings) { + if (RocksimCommonConstants.DESIGN_INFORMATION.equals(element)) { + //The next sub-element is "RocketDesign", which is really the only thing that matters. Rather than + //create another handler just for that element, handle it here. + return this; + } + if (RocksimCommonConstants.FILE_VERSION.equals(element)) { + return PlainTextHandler.INSTANCE; + } + if (RocksimCommonConstants.ROCKET_DESIGN.equals(element)) { + return new RocketDesignHandler(context, rocket); + } + return null; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + /** + * SAX handler for Rocksim file version number. The value is not used currently, but could be used in the future + * for backward/forward compatibility reasons (different lower level handlers could be called via a strategy pattern). + */ + if (RocksimCommonConstants.FILE_VERSION.equals(element)) { + version = content; + } + } + + /** + * Answer the file version. + * + * @return the version of the Rocksim design file + */ + public String getVersion() { + return version; + } } @@ -147,199 +154,202 @@ class RocksimContentHandler extends AbstractElementHandler { * structures. If that invariant is not true, then behavior will be unpredictable. */ class RocketDesignHandler extends AbstractElementHandler { - /** - * The parent component. - */ - private final RocketComponent component; - /** - * The parsed stage count. Defaults to 1. - */ - private int stageCount = 1; - /** - * The overridden stage 1 mass. - */ - private double stage1Mass = 0d; - /** - * The overridden stage 2 mass. - */ - private double stage2Mass = 0d; - /** - * The overridden stage 3 mass. - */ - private double stage3Mass = 0d; - /** - * The overridden stage 1 Cg. - */ - private double stage1CG = 0d; - /** - * The overridden stage 2 Cg. - */ - private double stage2CG = 0d; - /** - * The overridden stage 3 Cg. - */ - private double stage3CG = 0d; - - /** - * Constructor. - * - * @param c the parent component - */ - public RocketDesignHandler(RocketComponent c) { - component = c; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - /** - * In Rocksim stages are from the top down, so a single stage rocket is actually stage '3'. A 2-stage - * rocket defines stage '2' as the initial booster with stage '3' sitting atop it. And so on. - */ - if ("Stage3Parts".equals(element)) { - final Stage stage = new Stage(); - if (stage3Mass > 0.0d) { - stage.setMassOverridden(true); - stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override - stage.setOverrideMass(stage3Mass); - } - if (stage3CG > 0.0d) { - stage.setCGOverridden(true); - stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override - stage.setOverrideCGX(stage3CG); - } - component.addChild(stage); - return new StageHandler(stage); - } - if ("Stage2Parts".equals(element)) { - if (stageCount >= 2) { - final Stage stage = new Stage(); - if (stage2Mass > 0.0d) { - stage.setMassOverridden(true); - stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override - stage.setOverrideMass(stage2Mass); - } - if (stage2CG > 0.0d) { - stage.setCGOverridden(true); - stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override - stage.setOverrideCGX(stage2CG); - } - component.addChild(stage); - return new StageHandler(stage); - } - } - if ("Stage1Parts".equals(element)) { - if (stageCount == 3) { - final Stage stage = new Stage(); - if (stage1Mass > 0.0d) { - stage.setMassOverridden(true); - stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override - stage.setOverrideMass(stage1Mass); - } - if (stage1CG > 0.0d) { - stage.setCGOverridden(true); - stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override - stage.setOverrideCGX(stage1CG); - } - component.addChild(stage); - return new StageHandler(stage); - } - } - if (RocksimCommonConstants.NAME.equals(element)) { - return PlainTextHandler.INSTANCE; - } - if ("StageCount".equals(element)) { - return PlainTextHandler.INSTANCE; - } - if ("Stage3Mass".equals(element)) { - return PlainTextHandler.INSTANCE; - } - if ("Stage2Mass".equals(element)) { - return PlainTextHandler.INSTANCE; - } - if ("Stage1Mass".equals(element)) { - return PlainTextHandler.INSTANCE; - } - if ("Stage3CG".equals(element)) { - return PlainTextHandler.INSTANCE; - } - if ("Stage2CGAlone".equals(element)) { - return PlainTextHandler.INSTANCE; - } - if ("Stage1CGAlone".equals(element)) { - return PlainTextHandler.INSTANCE; - } - return null; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - try { - if (RocksimCommonConstants.NAME.equals(element)) { - component.setName(content); - } - if ("StageCount".equals(element)) { - stageCount = Integer.parseInt(content); - } - if ("Stage3Mass".equals(element)) { - stage3Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS; - } - if ("Stage2Mass".equals(element)) { - stage2Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS; - } - if ("Stage1Mass".equals(element)) { - stage1Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS; - } - if ("Stage3CG".equals(element)) { - stage3CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if ("Stage2CGAlone".equals(element)) { - stage2CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - if ("Stage1CGAlone".equals(element)) { - stage1CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; - } - } - catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - + private final DocumentLoadingContext context; + /** + * The parent component. + */ + private final RocketComponent component; + /** + * The parsed stage count. Defaults to 1. + */ + private int stageCount = 1; + /** + * The overridden stage 1 mass. + */ + private double stage1Mass = 0d; + /** + * The overridden stage 2 mass. + */ + private double stage2Mass = 0d; + /** + * The overridden stage 3 mass. + */ + private double stage3Mass = 0d; + /** + * The overridden stage 1 Cg. + */ + private double stage1CG = 0d; + /** + * The overridden stage 2 Cg. + */ + private double stage2CG = 0d; + /** + * The overridden stage 3 Cg. + */ + private double stage3CG = 0d; + + /** + * Constructor. + * + * @param c the parent component + */ + public RocketDesignHandler(DocumentLoadingContext context, RocketComponent c) { + this.context = context; + component = c; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + /** + * In Rocksim stages are from the top down, so a single stage rocket is actually stage '3'. A 2-stage + * rocket defines stage '2' as the initial booster with stage '3' sitting atop it. And so on. + */ + if ("Stage3Parts".equals(element)) { + final Stage stage = new Stage(); + if (stage3Mass > 0.0d) { + stage.setMassOverridden(true); + stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override + stage.setOverrideMass(stage3Mass); + } + if (stage3CG > 0.0d) { + stage.setCGOverridden(true); + stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override + stage.setOverrideCGX(stage3CG); + } + component.addChild(stage); + return new StageHandler(context, stage); + } + if ("Stage2Parts".equals(element)) { + if (stageCount >= 2) { + final Stage stage = new Stage(); + if (stage2Mass > 0.0d) { + stage.setMassOverridden(true); + stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override + stage.setOverrideMass(stage2Mass); + } + if (stage2CG > 0.0d) { + stage.setCGOverridden(true); + stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override + stage.setOverrideCGX(stage2CG); + } + component.addChild(stage); + return new StageHandler(context, stage); + } + } + if ("Stage1Parts".equals(element)) { + if (stageCount == 3) { + final Stage stage = new Stage(); + if (stage1Mass > 0.0d) { + stage.setMassOverridden(true); + stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override + stage.setOverrideMass(stage1Mass); + } + if (stage1CG > 0.0d) { + stage.setCGOverridden(true); + stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override + stage.setOverrideCGX(stage1CG); + } + component.addChild(stage); + return new StageHandler(context, stage); + } + } + if (RocksimCommonConstants.NAME.equals(element)) { + return PlainTextHandler.INSTANCE; + } + if ("StageCount".equals(element)) { + return PlainTextHandler.INSTANCE; + } + if ("Stage3Mass".equals(element)) { + return PlainTextHandler.INSTANCE; + } + if ("Stage2Mass".equals(element)) { + return PlainTextHandler.INSTANCE; + } + if ("Stage1Mass".equals(element)) { + return PlainTextHandler.INSTANCE; + } + if ("Stage3CG".equals(element)) { + return PlainTextHandler.INSTANCE; + } + if ("Stage2CGAlone".equals(element)) { + return PlainTextHandler.INSTANCE; + } + if ("Stage1CGAlone".equals(element)) { + return PlainTextHandler.INSTANCE; + } + return null; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + try { + if (RocksimCommonConstants.NAME.equals(element)) { + component.setName(content); + } + if ("StageCount".equals(element)) { + stageCount = Integer.parseInt(content); + } + if ("Stage3Mass".equals(element)) { + stage3Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS; + } + if ("Stage2Mass".equals(element)) { + stage2Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS; + } + if ("Stage1Mass".equals(element)) { + stage1Mass = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS; + } + if ("Stage3CG".equals(element)) { + stage3CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if ("Stage2CGAlone".equals(element)) { + stage2CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if ("Stage1CGAlone".equals(element)) { + stage1CG = Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH; + } + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + } /** * A SAX handler for a Rocksim stage. */ class StageHandler extends AbstractElementHandler { - /** - * The parent OpenRocket component. - */ - private final RocketComponent component; - - /** - * Constructor. - * - * @param c the parent component - * @throws IllegalArgumentException thrown if c is null - */ - public StageHandler(RocketComponent c) throws IllegalArgumentException { - if (c == null) { - throw new IllegalArgumentException("The stage component may not be null."); - } - component = c; - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - if (RocksimCommonConstants.NOSE_CONE.equals(element)) { - return new NoseConeHandler(component, warnings); - } - if (RocksimCommonConstants.BODY_TUBE.equals(element)) { - return new BodyTubeHandler(component, warnings); - } - if (RocksimCommonConstants.TRANSITION.equals(element)) { - return new TransitionHandler(component, warnings); - } - return null; - } + private final DocumentLoadingContext context; + /** + * The parent OpenRocket component. + */ + private final RocketComponent component; + + /** + * Constructor. + * + * @param c the parent component + * @throws IllegalArgumentException thrown if c is null + */ + public StageHandler(DocumentLoadingContext context, RocketComponent c) throws IllegalArgumentException { + if (c == null) { + throw new IllegalArgumentException("The stage component may not be null."); + } + this.context = context; + component = c; + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + if (RocksimCommonConstants.NOSE_CONE.equals(element)) { + return new NoseConeHandler(context, component, warnings); + } + if (RocksimCommonConstants.BODY_TUBE.equals(element)) { + return new BodyTubeHandler(context, component, warnings); + } + if (RocksimCommonConstants.TRANSITION.equals(element)) { + return new TransitionHandler(context, component, warnings); + } + return null; + } } diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/RocksimLoader.java b/core/src/net/sf/openrocket/file/rocksim/importt/RocksimLoader.java index 41fdaad4c..60bea006a 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/RocksimLoader.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/RocksimLoader.java @@ -6,9 +6,8 @@ package net.sf.openrocket.file.rocksim.importt; import java.io.IOException; import java.io.InputStream; -import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.file.AbstractRocketLoader; -import net.sf.openrocket.file.MotorFinder; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.RocketLoadException; import net.sf.openrocket.file.simplesax.SimpleSAX; @@ -39,11 +38,11 @@ public class RocksimLoader extends AbstractRocketLoader { * if an error occurs during loading. */ @Override - protected OpenRocketDocument loadFromStream(InputStream source, MotorFinder motorFinder) throws IOException, RocketLoadException { + protected void loadFromStream(DocumentLoadingContext context, InputStream source) throws IOException, RocketLoadException { InputSource xmlSource = new InputSource(source); - RocksimHandler handler = new RocksimHandler(); + RocksimHandler handler = new RocksimHandler(context); try { SimpleSAX.readXML(xmlSource, handler, warnings); @@ -51,9 +50,7 @@ public class RocksimLoader extends AbstractRocketLoader { throw new RocketLoadException("Malformed XML in input.", e); } - final OpenRocketDocument document = handler.getDocument(); - document.setFile(null); - document.clearUndo(); - return document; + context.getOpenRocketDocument().setFile(null); + context.getOpenRocketDocument().clearUndo(); } } diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/StreamerHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/StreamerHandler.java index cdf87a93c..4cf787d58 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/StreamerHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/StreamerHandler.java @@ -3,85 +3,87 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Streamer; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * A SAX handler for Streamer components. */ class StreamerHandler extends RecoveryDeviceHandler { - - /** - * The OpenRocket Streamer. - */ - private final Streamer streamer; - - /** - * Constructor. - * - * @param c the parent component - * @param warnings the warning set - * - * @throws IllegalArgumentException thrown if c is null - */ - public StreamerHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException { - if (c == null) { - throw new IllegalArgumentException("The parent of a streamer may not be null."); - } - streamer = new Streamer(); - if (isCompatible(c, Streamer.class, warnings)) { - c.addChild(streamer); - } - } - - /** - * {@inheritDoc} - */ - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - return PlainTextHandler.INSTANCE; - } - - /** - * {@inheritDoc} - */ - @Override - public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - super.closeElement(element, attributes, content, warnings); - - try { - if (RocksimCommonConstants.WIDTH.equals(element)) { - streamer.setStripWidth(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); - } - if (RocksimCommonConstants.LEN.equals(element)) { - streamer.setStripLength(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); - } - if (RocksimCommonConstants.DRAG_COEFFICIENT.equals(element)) { - streamer.setCD(Double.parseDouble(content)); - } - if (RocksimCommonConstants.MATERIAL.equals(element)) { - setMaterialName(content); - } - } - catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Streamer getComponent() { - return streamer; - } - + + /** + * The OpenRocket Streamer. + */ + private final Streamer streamer; + + /** + * Constructor. + * + * @param c the parent component + * @param warnings the warning set + * + * @throws IllegalArgumentException thrown if c is null + */ + public StreamerHandler(DocumentLoadingContext context, RocketComponent c, WarningSet warnings) throws IllegalArgumentException { + super(context); + if (c == null) { + throw new IllegalArgumentException("The parent of a streamer may not be null."); + } + streamer = new Streamer(); + if (isCompatible(c, Streamer.class, warnings)) { + c.addChild(streamer); + } + } + + /** + * {@inheritDoc} + */ + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + return PlainTextHandler.INSTANCE; + } + + /** + * {@inheritDoc} + */ + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.closeElement(element, attributes, content, warnings); + + try { + if (RocksimCommonConstants.WIDTH.equals(element)) { + streamer.setStripWidth(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); + } + if (RocksimCommonConstants.LEN.equals(element)) { + streamer.setStripLength(Math.max(0, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); + } + if (RocksimCommonConstants.DRAG_COEFFICIENT.equals(element)) { + streamer.setCD(Double.parseDouble(content)); + } + if (RocksimCommonConstants.MATERIAL.equals(element)) { + setMaterialName(content); + } + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Streamer getComponent() { + return streamer; + } + } \ No newline at end of file diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/TransitionHandler.java b/core/src/net/sf/openrocket/file/rocksim/importt/TransitionHandler.java index 21ac5dcb4..e02353e7f 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/TransitionHandler.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/TransitionHandler.java @@ -3,7 +3,10 @@ */ package net.sf.openrocket.file.rocksim.importt; +import java.util.HashMap; + import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; import net.sf.openrocket.file.rocksim.RocksimFinishCode; import net.sf.openrocket.file.rocksim.RocksimNoseConeCode; @@ -12,150 +15,147 @@ import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.material.Material; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Transition; -import org.xml.sax.SAXException; -import java.util.HashMap; +import org.xml.sax.SAXException; /** * The SAX handler for Transition components. */ class TransitionHandler extends BaseHandler { - /** - * The OpenRocket Transition. - */ - private final Transition transition = new Transition(); - - /** - * The wall thickness. Used for hollow nose cones. - */ - private double thickness = 0d; - - /** - * Constructor. - * - * @param c the parent component - * @param warnings the warning set - * @throws IllegalArgumentException thrown if c is null - */ - public TransitionHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException { - if (c == null) { - throw new IllegalArgumentException("The parent of a transition may not be null."); - } - if (isCompatible(c, Transition.class, warnings)) { - c.addChild(transition); - } - } - - @Override - public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { - if (RocksimCommonConstants.ATTACHED_PARTS.equals(element)) { - return new AttachedPartsHandler(transition); - } - return PlainTextHandler.INSTANCE; - } - - @Override - public void closeElement(String element, HashMap attributes, - String content, WarningSet warnings) throws SAXException { - super.closeElement(element, attributes, content, warnings); - - try { - if ("ShapeCode".equals(element)) { - transition.setType(RocksimNoseConeCode.fromCode(Integer.parseInt(content)).asOpenRocket()); - } - if ("Len".equals(element)) { - transition.setLength(Math.max(0, Double.parseDouble( - content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); - } - if ("FrontDia".equals(element)) { - transition.setForeRadius(Math.max(0, Double.parseDouble( - content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); - } - if ("RearDia".equals(element)) { - transition.setAftRadius(Math.max(0, Double.parseDouble( - content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); - } - if ("WallThickness".equals(element)) { - thickness = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); - } - if ("FrontShoulderDia".equals(element)) { - transition.setForeShoulderRadius(Math.max(0d, Double.parseDouble( - content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); - } - if ("RearShoulderDia".equals(element)) { - transition.setAftShoulderRadius(Math.max(0d, Double.parseDouble( - content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); - } - if ("FrontShoulderLen".equals(element)) { - transition.setForeShoulderLength(Math.max(0d, Double.parseDouble( - content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); - } - if ("RearShoulderLen".equals(element)) { - transition.setAftShoulderLength(Math.max(0d, Double.parseDouble( - content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); - } - if ("ShapeParameter".equals(element)) { - if (Transition.Shape.POWER.equals(transition.getType()) || - Transition.Shape.HAACK.equals(transition.getType()) || - Transition.Shape.PARABOLIC.equals(transition.getType())) { - transition.setShapeParameter(Double.parseDouble(content)); - } - } - if ("ConstructionType".equals(element)) { - int typeCode = Integer.parseInt(content); - if (typeCode == 0) { - //SOLID - transition.setFilled(true); - } - else if (typeCode == 1) { - //HOLLOW - transition.setFilled(false); - } - } - if ("FinishCode".equals(element)) { - transition.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket()); - } - if ("Material".equals(element)) { - setMaterialName(content); - } - } - catch (NumberFormatException nfe) { - warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); - } - } - - @Override - public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) - throws SAXException { - super.endHandler(element, attributes, content, warnings); - - if (transition.isFilled()) { - transition.setAftShoulderThickness(transition.getAftShoulderRadius()); - transition.setForeShoulderThickness(transition.getForeShoulderRadius()); - } - else { - transition.setThickness(thickness); - transition.setAftShoulderThickness(thickness); - transition.setForeShoulderThickness(thickness); - } - } - - - @Override - public Transition getComponent() { - return transition; - } - - /** - * Get the required type of material for this component. - * - * @return BULK - */ - @Override - public Material.Type getMaterialType() { - return Material.Type.BULK; - } - - + /** + * The OpenRocket Transition. + */ + private final Transition transition = new Transition(); + + /** + * The wall thickness. Used for hollow nose cones. + */ + private double thickness = 0d; + + /** + * Constructor. + * + * @param c the parent component + * @param warnings the warning set + * @throws IllegalArgumentException thrown if c is null + */ + public TransitionHandler(DocumentLoadingContext context, RocketComponent c, WarningSet warnings) throws IllegalArgumentException { + super(context); + if (c == null) { + throw new IllegalArgumentException("The parent of a transition may not be null."); + } + if (isCompatible(c, Transition.class, warnings)) { + c.addChild(transition); + } + } + + @Override + public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { + if (RocksimCommonConstants.ATTACHED_PARTS.equals(element)) { + return new AttachedPartsHandler(context, transition); + } + return PlainTextHandler.INSTANCE; + } + + @Override + public void closeElement(String element, HashMap attributes, + String content, WarningSet warnings) throws SAXException { + super.closeElement(element, attributes, content, warnings); + + try { + if ("ShapeCode".equals(element)) { + transition.setType(RocksimNoseConeCode.fromCode(Integer.parseInt(content)).asOpenRocket()); + } + if ("Len".equals(element)) { + transition.setLength(Math.max(0, Double.parseDouble( + content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); + } + if ("FrontDia".equals(element)) { + transition.setForeRadius(Math.max(0, Double.parseDouble( + content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); + } + if ("RearDia".equals(element)) { + transition.setAftRadius(Math.max(0, Double.parseDouble( + content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); + } + if ("WallThickness".equals(element)) { + thickness = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); + } + if ("FrontShoulderDia".equals(element)) { + transition.setForeShoulderRadius(Math.max(0d, Double.parseDouble( + content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); + } + if ("RearShoulderDia".equals(element)) { + transition.setAftShoulderRadius(Math.max(0d, Double.parseDouble( + content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS)); + } + if ("FrontShoulderLen".equals(element)) { + transition.setForeShoulderLength(Math.max(0d, Double.parseDouble( + content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); + } + if ("RearShoulderLen".equals(element)) { + transition.setAftShoulderLength(Math.max(0d, Double.parseDouble( + content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH)); + } + if ("ShapeParameter".equals(element)) { + if (Transition.Shape.POWER.equals(transition.getType()) || + Transition.Shape.HAACK.equals(transition.getType()) || + Transition.Shape.PARABOLIC.equals(transition.getType())) { + transition.setShapeParameter(Double.parseDouble(content)); + } + } + if ("ConstructionType".equals(element)) { + int typeCode = Integer.parseInt(content); + if (typeCode == 0) { + //SOLID + transition.setFilled(true); + } + else if (typeCode == 1) { + //HOLLOW + transition.setFilled(false); + } + } + if ("FinishCode".equals(element)) { + transition.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket()); + } + if ("Material".equals(element)) { + setMaterialName(content); + } + } catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + @Override + public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.endHandler(element, attributes, content, warnings); + + if (transition.isFilled()) { + transition.setAftShoulderThickness(transition.getAftShoulderRadius()); + transition.setForeShoulderThickness(transition.getForeShoulderRadius()); + } + else { + transition.setThickness(thickness); + transition.setAftShoulderThickness(thickness); + transition.setForeShoulderThickness(thickness); + } + } + + + @Override + public Transition getComponent() { + return transition; + } + + /** + * Get the required type of material for this component. + * + * @return BULK + */ + public Material.Type getMaterialType() { + return Material.Type.BULK; + } + + } - diff --git a/core/src/net/sf/openrocket/file/simplesax/AbstractElementHandler.java b/core/src/net/sf/openrocket/file/simplesax/AbstractElementHandler.java index 208f68c01..3085d2ffe 100644 --- a/core/src/net/sf/openrocket/file/simplesax/AbstractElementHandler.java +++ b/core/src/net/sf/openrocket/file/simplesax/AbstractElementHandler.java @@ -51,4 +51,21 @@ public abstract class AbstractElementHandler implements ElementHandler { // No-op } + + /** + * Helper method for parsing a double value safely. + * + * @param str the string to parse + * @param warnings the warning set + * @param warn the warning to add if the value fails to parse + * @return the double value, or NaN if an error occurred + */ + protected double parseDouble(String str, WarningSet warnings, Warning warn) { + try { + return Double.parseDouble(str); + } catch (NumberFormatException e) { + warnings.add(warn); + return Double.NaN; + } + } } diff --git a/core/src/net/sf/openrocket/formatting/MotorDescriptionSubstitutor.java b/core/src/net/sf/openrocket/formatting/MotorDescriptionSubstitutor.java new file mode 100644 index 000000000..9963594c0 --- /dev/null +++ b/core/src/net/sf/openrocket/formatting/MotorDescriptionSubstitutor.java @@ -0,0 +1,155 @@ +package net.sf.openrocket.formatting; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.plugin.Plugin; +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.util.ArrayList; +import net.sf.openrocket.util.Chars; + +import com.google.inject.Inject; + +@Plugin +public class MotorDescriptionSubstitutor implements RocketSubstitutor { + public static final String SUBSTITUTION = "{motors}"; + + @Inject + private Translator trans; + + @Override + public boolean containsSubstitution(String str) { + return str.contains(SUBSTITUTION); + } + + @Override + public String substitute(String str, Rocket rocket, String configId) { + String description = getMotorConfigurationDescription(rocket, configId); + return str.replace(SUBSTITUTION, description); + } + + @Override + public Map getDescriptions() { + Map desc = new HashMap(); + desc.put(SUBSTITUTION, trans.get("MotorDescriptionSubstitutor.description")); + return null; + } + + + + public String getMotorConfigurationDescription(Rocket rocket, String id) { + String name; + int motorCount = 0; + + // Generate the description + + // First iterate over each stage and store the designations of each motor + List> list = new ArrayList>(); + List currentList = Collections.emptyList(); + + Iterator iterator = rocket.iterator(); + while (iterator.hasNext()) { + RocketComponent c = iterator.next(); + + if (c instanceof Stage) { + + currentList = new ArrayList(); + list.add(currentList); + + } else if (c instanceof MotorMount) { + + MotorMount mount = (MotorMount) c; + Motor motor = mount.getMotor(id); + + if (mount.isMotorMount() && motor != null) { + String designation = motor.getDesignation(mount.getMotorDelay(id)); + + for (int i = 0; i < mount.getMotorCount(); i++) { + currentList.add(designation); + motorCount++; + } + } + + } + } + + if (motorCount == 0) { + return trans.get("Rocket.motorCount.Nomotor"); + } + + // Change multiple occurrences of a motor to n x motor + List stages = new ArrayList(); + + for (List stage : list) { + String stageName = ""; + String previous = null; + int count = 0; + + Collections.sort(stage); + for (String current : stage) { + if (current.equals(previous)) { + + count++; + + } else { + + if (previous != null) { + String s = ""; + if (count > 1) { + s = "" + count + Chars.TIMES + previous; + } else { + s = previous; + } + + if (stageName.equals("")) + stageName = s; + else + stageName = stageName + "," + s; + } + + previous = current; + count = 1; + + } + } + if (previous != null) { + String s = ""; + if (count > 1) { + s = "" + count + Chars.TIMES + previous; + } else { + s = previous; + } + + if (stageName.equals("")) + stageName = s; + else + stageName = stageName + "," + s; + } + + stages.add(stageName); + } + + name = ""; + for (int i = 0; i < stages.size(); i++) { + String s = stages.get(i); + if (s.equals("")) + s = trans.get("Rocket.motorCount.noStageMotors"); + if (i == 0) + name = name + s; + else + name = name + "; " + s; + } + return name; + } + + + +} diff --git a/core/src/net/sf/openrocket/formatting/RocketDescriptor.java b/core/src/net/sf/openrocket/formatting/RocketDescriptor.java new file mode 100644 index 000000000..dba2ba00d --- /dev/null +++ b/core/src/net/sf/openrocket/formatting/RocketDescriptor.java @@ -0,0 +1,25 @@ +package net.sf.openrocket.formatting; + +import net.sf.openrocket.rocketcomponent.Rocket; + +/** + * Interface for formatting a flight configuration into a + * textual string. + */ +public interface RocketDescriptor { + + /** + * Return a string describing a particular flight configuration + * of the rocket. This uses the default flight configuration name + * as the basis. + */ + public String format(Rocket rocket, String configId); + + + /** + * Return a string describing a particular flight configuration + * of the rocket. This uses a custom-provided name as the basis. + */ + public String format(String name, Rocket rocket, String configId); + +} diff --git a/core/src/net/sf/openrocket/formatting/RocketDescriptorImpl.java b/core/src/net/sf/openrocket/formatting/RocketDescriptorImpl.java new file mode 100644 index 000000000..62df68e64 --- /dev/null +++ b/core/src/net/sf/openrocket/formatting/RocketDescriptorImpl.java @@ -0,0 +1,31 @@ +package net.sf.openrocket.formatting; + +import java.util.Set; + +import net.sf.openrocket.rocketcomponent.Rocket; + +import com.google.inject.Inject; + +public class RocketDescriptorImpl implements RocketDescriptor { + + @Inject + private Set substitutors; + + @Override + public String format(Rocket rocket, String configId) { + String name = rocket.getFlightConfigurationName(configId); + return format(name, rocket, configId); + } + + @Override + public String format(String name, Rocket rocket, String configId) { + for (RocketSubstitutor s : substitutors) { + while (s.containsSubstitution(name)) { + name = s.substitute(name, rocket, configId); + } + } + + return name; + } + +} diff --git a/core/src/net/sf/openrocket/formatting/RocketSubstitutor.java b/core/src/net/sf/openrocket/formatting/RocketSubstitutor.java new file mode 100644 index 000000000..006c33b34 --- /dev/null +++ b/core/src/net/sf/openrocket/formatting/RocketSubstitutor.java @@ -0,0 +1,20 @@ +package net.sf.openrocket.formatting; + +import java.util.Map; + +import net.sf.openrocket.plugin.Plugin; +import net.sf.openrocket.rocketcomponent.Rocket; + +/** + * A class that allows substitution to occur in a text string. + */ +@Plugin +public interface RocketSubstitutor { + + public boolean containsSubstitution(String str); + + public String substitute(String str, Rocket rocket, String configId); + + public Map getDescriptions(); + +} diff --git a/core/src/net/sf/openrocket/gui/ExportDecalDialog.java b/core/src/net/sf/openrocket/gui/ExportDecalDialog.java new file mode 100644 index 000000000..2f8714df2 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/ExportDecalDialog.java @@ -0,0 +1,91 @@ +package net.sf.openrocket.gui; + +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Collection; + +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.appearance.DecalImage; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.gui.util.FileHelper; +import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.startup.Application; + +public class ExportDecalDialog extends JDialog { + + private final static Translator trans = Application.getTranslator(); + + private final OpenRocketDocument document; + + private JComboBox decalComboBox; + + public ExportDecalDialog(Window parent, OpenRocketDocument doc) { + super(parent, trans.get("ExportDecalDialog.title"), ModalityType.APPLICATION_MODAL); + + this.document = doc; + + JPanel panel = new JPanel(new MigLayout()); + + //// decal list + JLabel label = new JLabel(trans.get("ExportDecalDialog.decalList.lbl")); + panel.add(label); + + Collection exportableDecals = document.getDecalList(); + + decalComboBox = new JComboBox(exportableDecals.toArray(new DecalImage[0])); + decalComboBox.setEditable(false); + panel.add(decalComboBox, "growx, wrap"); + + final JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); + chooser.setVisible(true); + chooser.setDialogType(JFileChooser.SAVE_DIALOG); + + chooser.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String command = e.getActionCommand(); + if (command.equals(JFileChooser.CANCEL_SELECTION)) { + ExportDecalDialog.this.dispose(); + } else if (command.equals(JFileChooser.APPROVE_SELECTION)) { + // Here we copy the bits out. + + DecalImage selectedDecal = (DecalImage) decalComboBox.getSelectedItem(); + File selectedFile = chooser.getSelectedFile(); + + if (FileHelper.confirmWrite(selectedFile, ExportDecalDialog.this)) { + export(selectedDecal, selectedFile); + // If the user doesn't confirm over write, then leave this dialog open. + ExportDecalDialog.this.dispose(); + } + } + } + }); + panel.add(chooser, "span, grow"); + + this.add(panel); + this.pack(); + } + + private void export(DecalImage decal, File selectedFile) { + + try { + decal.exportImage(selectedFile, false); + } catch (IOException iex) { + String message = MessageFormat.format(trans.get("ExportDecalDialog.exception"), selectedFile.getAbsoluteFile()); + JOptionPane.showMessageDialog(this, message, "", JOptionPane.ERROR_MESSAGE); + } + } +} diff --git a/core/src/net/sf/openrocket/gui/StorageOptionChooser.java b/core/src/net/sf/openrocket/gui/StorageOptionChooser.java index 49b21974c..eac318011 100644 --- a/core/src/net/sf/openrocket/gui/StorageOptionChooser.java +++ b/core/src/net/sf/openrocket/gui/StorageOptionChooser.java @@ -39,8 +39,6 @@ public class StorageOptionChooser extends JPanel { private JSpinner timeSpinner; - private JCheckBox compressButton; - private JLabel estimateLabel; @@ -122,15 +120,6 @@ public class StorageOptionChooser extends JPanel { noneButton.addActionListener(actionUpdater); this.add(noneButton, "spanx, wrap 20lp"); - - //// Compress file - compressButton = new JCheckBox(trans.get("StorageOptChooser.checkbox.Compfile")); - //// Using compression reduces the file size significantly. - compressButton.setToolTipText(trans.get("StorageOptChooser.lbl.UsingComp")); - compressButton.addActionListener(actionUpdater); - this.add(compressButton, "spanx, wrap para"); - - // Estimate is updated in loadOptions(opts) estimateLabel = new JLabel(""); //// An estimate on how large the resulting file would @@ -168,9 +157,6 @@ public class StorageOptionChooser extends JPanel { timeSpinner.setValue(t); artificialEvent = false; - // Compression checkbox - compressButton.setSelected(opts.isCompressionEnabled()); - updateEstimate(); } @@ -188,8 +174,6 @@ public class StorageOptionChooser extends JPanel { opts.setSimulationTimeSkip(t); - opts.setCompressionEnabled(compressButton.isSelected()); - opts.setExplicitlySet(true); } diff --git a/core/src/net/sf/openrocket/gui/adaptors/DecalModel.java b/core/src/net/sf/openrocket/gui/adaptors/DecalModel.java new file mode 100644 index 000000000..8ff9fe8dc --- /dev/null +++ b/core/src/net/sf/openrocket/gui/adaptors/DecalModel.java @@ -0,0 +1,96 @@ +package net.sf.openrocket.gui.adaptors; + +import java.awt.Component; +import java.io.File; + +import javax.swing.AbstractListModel; +import javax.swing.ComboBoxModel; +import javax.swing.JFileChooser; +import javax.swing.SwingUtilities; + +import net.sf.openrocket.appearance.AppearanceBuilder; +import net.sf.openrocket.appearance.DecalImage; +import net.sf.openrocket.document.Attachment; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.file.FileSystemAttachmentFactory; +import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.startup.Application; + +public class DecalModel extends AbstractListModel implements ComboBoxModel { + + private static final Translator trans = Application.getTranslator(); + + private static final String NONE_SELECTED = trans.get("lbl.select"); + private static final String SELECT_FILE = trans.get("lbl.choose"); + + private final OpenRocketDocument document; + private final Component parent; + private final AppearanceBuilder ab; + + private static File lastImageDir = null; + + private DecalImage[] decals; + + public DecalModel(Component parent, OpenRocketDocument document, AppearanceBuilder ab) { + this.document = document; + this.parent = parent; + this.ab = ab; + decals = document.getDecalList().toArray(new DecalImage[0]); + } + + @Override + public int getSize() { + return decals.length + 2; + } + + @Override + public Object getElementAt(int index) { + if (index == 0) { + return NONE_SELECTED; + } + if (index == getSize() - 1) { + return SELECT_FILE; + } + return decals[index - 1]; + } + + @Override + public void setSelectedItem(Object item) { + + if (item == null || item.equals(NONE_SELECTED)) { + ab.setImage(null); + } else if (item.equals(SELECT_FILE)) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + File current = lastImageDir; + lastImageDir = current; + + JFileChooser fc = new JFileChooser(current); + fc.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); + int action = fc.showOpenDialog(SwingUtilities.getWindowAncestor(parent)); + if (action == JFileChooser.APPROVE_OPTION) { + ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(fc.getCurrentDirectory()); + File file = fc.getSelectedFile(); + Attachment a = (new FileSystemAttachmentFactory().getAttachment(file)); + setSelectedItem(document.getDecalImage(a)); + } + } + }); + } else { + ab.setImage((DecalImage) item); + } + } + + @Override + public Object getSelectedItem() { + DecalImage decal = ab.getImage(); + if (decal == null || !document.getDecalList().contains(decal)) { + return NONE_SELECTED; + } else { + return decal; + } + } + +} diff --git a/core/src/net/sf/openrocket/gui/adaptors/DoubleModel.java b/core/src/net/sf/openrocket/gui/adaptors/DoubleModel.java index bfd259969..9058b7d6a 100644 --- a/core/src/net/sf/openrocket/gui/adaptors/DoubleModel.java +++ b/core/src/net/sf/openrocket/gui/adaptors/DoubleModel.java @@ -863,6 +863,16 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat * @param l Listener to add. */ @Override + public void addChangeListener(StateChangeListener l) { + addChangeListener((EventListener) l); + } + + + /** + * Add a listener to the model. Adds the model as a listener to the value source if this + * is the first listener. + * @param l Listener to add. + */ public void addChangeListener(EventListener l) { checkState(true); @@ -884,6 +894,15 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat * @param l Listener to remove. */ @Override + public void removeChangeListener(StateChangeListener l) { + removeChangeListener((EventListener) l); + } + + /** + * Remove a listener from the model. Removes the model from being a listener to the Component + * if this was the last listener of the model. + * @param l Listener to remove. + */ public void removeChangeListener(EventListener l) { checkState(false); diff --git a/core/src/net/sf/openrocket/gui/adaptors/MotorConfigurationModel.java b/core/src/net/sf/openrocket/gui/adaptors/FlightConfigurationModel.java similarity index 67% rename from core/src/net/sf/openrocket/gui/adaptors/MotorConfigurationModel.java rename to core/src/net/sf/openrocket/gui/adaptors/FlightConfigurationModel.java index 57852699e..e37c488db 100644 --- a/core/src/net/sf/openrocket/gui/adaptors/MotorConfigurationModel.java +++ b/core/src/net/sf/openrocket/gui/adaptors/FlightConfigurationModel.java @@ -11,7 +11,8 @@ import javax.swing.event.EventListenerList; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; -import net.sf.openrocket.gui.dialogs.EditMotorConfigurationDialog; +import net.sf.openrocket.formatting.RocketDescriptor; +import net.sf.openrocket.gui.dialogs.flightconfiguration.FlightConfigurationDialog; import net.sf.openrocket.gui.main.BasicFrame; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; @@ -20,32 +21,48 @@ import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.StateChangeListener; -public class MotorConfigurationModel implements ComboBoxModel, StateChangeListener { +/** + * A ComboBoxModel that contains a list of flight configurations. The list can + * optionally contain a last element that opens up the configuration edit dialog. + */ +public class FlightConfigurationModel implements ComboBoxModel, StateChangeListener { private static final Translator trans = Application.getTranslator(); - + private static final String EDIT = trans.get("MotorCfgModel.Editcfg"); + private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); + private EventListenerList listenerList = new EventListenerList(); private final Configuration config; private final Rocket rocket; + private final boolean showEditElement; private Map map = new HashMap(); - - public MotorConfigurationModel(Configuration config) { + + public FlightConfigurationModel(Configuration config) { + this(config, true); + } + + + public FlightConfigurationModel(Configuration config, boolean showEditElement) { this.config = config; this.rocket = config.getRocket(); + this.showEditElement = showEditElement; config.addChangeListener(this); } - @Override public Object getElementAt(int index) { - String[] ids = rocket.getMotorConfigurationIDs(); - if (index < 0 || index > ids.length) + String[] ids = rocket.getFlightConfigurationIDs(); + + if (index < 0) + return null; + if ((showEditElement && index > ids.length) || + (!showEditElement && index >= ids.length)) return null; if (index == ids.length) @@ -53,17 +70,21 @@ public class MotorConfigurationModel implements ComboBoxModel, StateChangeListen return get(ids[index]); } - + @Override public int getSize() { - return rocket.getMotorConfigurationIDs().length + 1; + if (showEditElement) { + return rocket.getFlightConfigurationIDs().length + 1; + } else { + return rocket.getFlightConfigurationIDs().length; + } } - + @Override public Object getSelectedItem() { - return get(config.getMotorConfigurationID()); + return get(config.getFlightConfigurationID()); } - + @Override public void setSelectedItem(Object item) { if (item == null) { @@ -76,22 +97,22 @@ public class MotorConfigurationModel implements ComboBoxModel, StateChangeListen SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - new EditMotorConfigurationDialog(rocket, BasicFrame.findFrame(rocket)) - .setVisible(true); + new FlightConfigurationDialog(rocket, BasicFrame.findFrame(rocket)) + .setVisible(true); } }); - + return; } if (!(item instanceof ID)) { - throw new IllegalArgumentException("MotorConfigurationModel item="+item); + throw new IllegalArgumentException("MotorConfigurationModel item=" + item); } ID idObject = (ID) item; - config.setMotorConfigurationID(idObject.getID()); + config.setFlightConfigurationID(idObject.getID()); } - - + + //////////////// Event/listener handling //////////////// @@ -100,31 +121,31 @@ public class MotorConfigurationModel implements ComboBoxModel, StateChangeListen public void addListDataListener(ListDataListener l) { listenerList.add(ListDataListener.class, l); } - + @Override public void removeListDataListener(ListDataListener l) { listenerList.remove(ListDataListener.class, l); } - + protected void fireListDataEvent() { Object[] listeners = listenerList.getListenerList(); ListDataEvent e = null; - - for (int i = listeners.length-2; i>=0; i-=2) { + + for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ListDataListener.class) { if (e == null) e = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, getSize()); - ((ListDataListener) listeners[i+1]).contentsChanged(e); + ((ListDataListener) listeners[i + 1]).contentsChanged(e); } } } - + @Override public void stateChanged(EventObject e) { if (e instanceof ComponentChangeEvent) { // Ignore unnecessary changes - if (!((ComponentChangeEvent)e).isMotorChange()) + if (!((ComponentChangeEvent) e).isMotorChange()) return; } fireListDataEvent(); @@ -162,9 +183,8 @@ public class MotorConfigurationModel implements ComboBoxModel, StateChangeListen @Override public String toString() { - return rocket.getMotorConfigurationNameOrDescription(id); + return descriptor.format(rocket, id); } } } - diff --git a/core/src/net/sf/openrocket/gui/adaptors/IntegerModel.java b/core/src/net/sf/openrocket/gui/adaptors/IntegerModel.java index e948918af..3b07d9054 100644 --- a/core/src/net/sf/openrocket/gui/adaptors/IntegerModel.java +++ b/core/src/net/sf/openrocket/gui/adaptors/IntegerModel.java @@ -6,6 +6,8 @@ import java.util.ArrayList; import java.util.EventListener; import java.util.EventObject; +import javax.swing.BoundedRangeModel; +import javax.swing.DefaultBoundedRangeModel; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; @@ -83,7 +85,44 @@ public class IntegerModel implements StateChangeListener { return new IntegerSpinnerModel(); } + private class ValueSliderModel extends DefaultBoundedRangeModel implements BoundedRangeModel, StateChangeListener { + ValueSliderModel(){ + super(IntegerModel.this.getValue(), 0, minValue, maxValue); + } + @Override + public void setValue(int newValue) { + IntegerModel.this.setValue(newValue); + } + + @Override + public int getValue(){ + return IntegerModel.this.getValue(); + } + @Override + public void stateChanged(EventObject e) { + IntegerModel.this.fireStateChanged(); + } + + @Override + public void addChangeListener(ChangeListener l) { + IntegerModel.this.addChangeListener(l); + } + + @Override + public void removeChangeListener(ChangeListener l) { + IntegerModel.this.removeChangeListener(l); + } + + } + /** + * Returns a new BoundedRangeModel with the same base as the IntegerModel. + * + * @return A compatibility layer for Sliders. + */ + public BoundedRangeModel getSliderModel(){ + return new ValueSliderModel(); + } //////////// Main model ///////////// diff --git a/core/src/net/sf/openrocket/gui/components/ColorIcon.java b/core/src/net/sf/openrocket/gui/components/ColorIcon.java index 747a42685..8da617d22 100644 --- a/core/src/net/sf/openrocket/gui/components/ColorIcon.java +++ b/core/src/net/sf/openrocket/gui/components/ColorIcon.java @@ -4,6 +4,8 @@ import java.awt.Color; import javax.swing.Icon; +import net.sf.openrocket.gui.util.ColorConversion; + /** * An Icon that displays a specific color, suitable for drawing into a button. * @@ -16,6 +18,10 @@ public class ColorIcon implements Icon { this.color = c; } + public ColorIcon(net.sf.openrocket.util.Color c){ + this.color = ColorConversion.toAwtColor(c); + } + @Override public int getIconHeight() { return 15; @@ -28,8 +34,13 @@ public class ColorIcon implements Icon { @Override public void paintIcon(java.awt.Component c, java.awt.Graphics g, int x, int y) { - g.setColor(color); - g.fill3DRect(x, y, getIconWidth(), getIconHeight(), false); + if ( c.isEnabled() ){ + g.setColor(color); + g.fillRect(x, y, getIconWidth(), getIconHeight()); + } else { + g.setColor(color); + g.drawRect(x, y, getIconWidth(), getIconHeight()); + } } } diff --git a/core/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java b/core/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java new file mode 100644 index 000000000..485a8d70b --- /dev/null +++ b/core/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java @@ -0,0 +1,363 @@ +package net.sf.openrocket.gui.configdialog; + +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.Method; +import java.util.EventObject; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JColorChooser; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.AppearanceBuilder; +import net.sf.openrocket.appearance.Decal.EdgeMode; +import net.sf.openrocket.appearance.defaults.DefaultAppearance; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.gui.SpinnerEditor; +import net.sf.openrocket.gui.adaptors.BooleanModel; +import net.sf.openrocket.gui.adaptors.DecalModel; +import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.adaptors.EnumModel; +import net.sf.openrocket.gui.components.BasicSlider; +import net.sf.openrocket.gui.components.ColorIcon; +import net.sf.openrocket.gui.components.StyledLabel; +import net.sf.openrocket.gui.components.StyledLabel.Style; +import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.util.ColorConversion; +import net.sf.openrocket.gui.util.EditDecalHelper; +import net.sf.openrocket.gui.util.EditDecalHelper.EditDecalHelperException; +import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.GeneralUnit; +import net.sf.openrocket.unit.Unit; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.LineStyle; +import net.sf.openrocket.util.StateChangeListener; + +public class AppearancePanel extends JPanel { + private static final Translator trans = Application.getTranslator(); + + private AppearanceBuilder ab; + + // We hang on to the user selected appearance when switching to default appearance. + // this appearance is restored if the user unchecks the "default" button. + private Appearance previousUserSelectedAppearance = null; + + // We cache the default appearance for this component to make switching faster. + private Appearance defaultAppearance = null; + + /** + * A non-unit that adjusts by a small amount, suitable for + * values that are on the 0-1 scale + */ + private final static UnitGroup TEXTURE_UNIT = new UnitGroup(); + static { + Unit no_unit = new GeneralUnit(1, "", 2) { + @Override + public double getNextValue(double value) { + return value + .1; + } + + @Override + public double getPreviousValue(double value) { + return value - .1; + } + + }; + TEXTURE_UNIT.addUnit(no_unit); + } + + private static final JColorChooser colorChooser = new JColorChooser(); + + private class ColorActionListener implements ActionListener { + private final String valueName; + private final Object o; + + ColorActionListener(final Object o, final String valueName) { + this.valueName = valueName; + this.o = o; + } + + @Override + public void actionPerformed(ActionEvent colorClickEvent) { + try { + final Method getMethod = o.getClass().getMethod("get" + valueName); + final Method setMethod = o.getClass().getMethod("set" + valueName, net.sf.openrocket.util.Color.class); + net.sf.openrocket.util.Color c = (net.sf.openrocket.util.Color) getMethod.invoke(o); + Color awtColor = ColorConversion.toAwtColor(c); + colorChooser.setColor(awtColor); + JDialog d = JColorChooser.createDialog(AppearancePanel.this, + trans.get("RocketCompCfg.lbl.Choosecolor"), true, colorChooser, new ActionListener() { + @Override + public void actionPerformed(ActionEvent okEvent) { + Color selected = colorChooser.getColor(); + if (selected == null) + return; + try { + setMethod.invoke(o, ColorConversion.fromAwtColor(selected)); + } catch (Throwable e1) { + Application.getExceptionHandler().handleErrorCondition(e1); + } + } + }, null); + d.setVisible(true); + } catch (Throwable e1) { + Application.getExceptionHandler().handleErrorCondition(e1); + } + } + } + + public AppearancePanel(final OpenRocketDocument document, final RocketComponent c) { + super(new MigLayout("fill", "[150][grow][150][grow]")); + + previousUserSelectedAppearance = c.getAppearance(); + defaultAppearance = DefaultAppearance.getDefaultAppearance(c); + if (previousUserSelectedAppearance == null) { + previousUserSelectedAppearance = new AppearanceBuilder().getAppearance(); + ab = new AppearanceBuilder(defaultAppearance); + } else { + ab = new AppearanceBuilder(previousUserSelectedAppearance); + } + + net.sf.openrocket.util.Color figureColor = c.getColor(); + if (figureColor == null) { + figureColor = Application.getPreferences().getDefaultColor(c.getClass()); + } + final JButton figureColorButton = new JButton(new ColorIcon(figureColor)); + + final JButton colorButton = new JButton(new ColorIcon(ab.getPaint())); + + final JComboBox textureDropDown = new JComboBox(new DecalModel(this, document, ab)); + + ab.addChangeListener(new StateChangeListener() { + @Override + public void stateChanged(EventObject e) { + figureColorButton.setIcon(new ColorIcon(c.getColor())); + colorButton.setIcon(new ColorIcon(ab.getPaint())); + c.setAppearance(ab.getAppearance()); + } + }); + + c.addChangeListener(new StateChangeListener() { + @Override + public void stateChanged(EventObject e) { + net.sf.openrocket.util.Color col = c.getColor(); + if (col == null) { + col = Application.getPreferences().getDefaultColor(c.getClass()); + } + figureColorButton.setIcon(new ColorIcon(col)); + } + }); + + figureColorButton.addActionListener(new ColorActionListener(c, "Color")); + colorButton.addActionListener(new ColorActionListener(ab, "Paint")); + + BooleanModel mDefault = new BooleanModel(c.getAppearance() == null); + BooleanModel fDefault = new BooleanModel(c.getColor() == null); + + + {// Style Header Row + final JCheckBox colorDefault = new JCheckBox(fDefault); + colorDefault.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (colorDefault.isSelected()) { + c.setColor(null); + c.setLineStyle(null); + } else { + c.setColor(((SwingPreferences) Application.getPreferences()).getDefaultColor(c.getClass())); + c.setLineStyle(((SwingPreferences) Application.getPreferences()).getDefaultLineStyle(c.getClass())); + } + } + }); + colorDefault.setText(trans.get("RocketCompCfg.checkbox.Usedefaultcolor")); + add(new StyledLabel(trans.get("RocketCompCfg.lbl.Figurestyle"), Style.BOLD)); + add(colorDefault); + + JButton button = new JButton(trans.get("RocketCompCfg.but.Saveasdefstyle")); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (c.getColor() != null) { + ((SwingPreferences) Application.getPreferences()).setDefaultColor(c.getClass(), c.getColor()); + c.setColor(null); + } + if (c.getLineStyle() != null) { + Application.getPreferences().setDefaultLineStyle(c.getClass(), c.getLineStyle()); + c.setLineStyle(null); + } + } + }); + fDefault.addEnableComponent(button, false); + add(button, "span 2, align right, wrap"); + } + + {// Figure Color + add(new JLabel(trans.get("RocketCompCfg.lbl.Componentcolor"))); + fDefault.addEnableComponent(figureColorButton, false); + add(figureColorButton); + } + + {// Line Style + + add(new JLabel(trans.get("RocketCompCfg.lbl.Complinestyle"))); + + LineStyle[] list = new LineStyle[LineStyle.values().length + 1]; + System.arraycopy(LineStyle.values(), 0, list, 1, LineStyle.values().length); + + JComboBox combo = new JComboBox(new EnumModel(c, "LineStyle", + //// Default style + list, trans.get("LineStyle.Defaultstyle"))); + + fDefault.addEnableComponent(combo, false); + + add(combo, "wrap"); + } + + add(new JSeparator(SwingConstants.HORIZONTAL), "span, wrap, growx"); + + {// Texture Header Row + add(new StyledLabel(trans.get("AppearanceCfg.lbl.Appearance"), Style.BOLD)); + final JCheckBox materialDefault = new JCheckBox(mDefault); + materialDefault.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (materialDefault.isSelected()) { + previousUserSelectedAppearance = (ab == null) ? null : ab.getAppearance(); + ab.setAppearance(defaultAppearance); + } else { + ab.setAppearance(previousUserSelectedAppearance); + } + } + }); + materialDefault.setText(trans.get("AppearanceCfg.lbl.Usedefault")); + add(materialDefault, "wrap"); + } + + {// Texture File + add(new JLabel(trans.get("AppearanceCfg.lbl.Texture"))); + JPanel p = new JPanel(new MigLayout("fill, ins 0", "[grow][]")); + mDefault.addEnableComponent(textureDropDown, false); + p.add(textureDropDown, "grow"); + add(p, "span 3, growx, wrap"); + final JButton editBtn = new JButton(trans.get("AppearanceCfg.but.edit")); + editBtn.setEnabled(ab.getImage() != null); + ab.addChangeListener(new StateChangeListener() { + @Override + public void stateChanged(EventObject e) { + editBtn.setEnabled(ab.getImage() != null); + } + }); + editBtn.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + try { + EditDecalHelper.editDecal(SwingUtilities.getWindowAncestor(AppearancePanel.this), document, c, ab.getImage()); + } catch (EditDecalHelperException ex) { + JOptionPane.showMessageDialog(AppearancePanel.this, ex.getMessage(), "", JOptionPane.ERROR_MESSAGE); + } + } + + }); + mDefault.addEnableComponent(editBtn, false); + p.add(editBtn); + } + + { // Color + add(new JLabel(trans.get("AppearanceCfg.lbl.color.Color"))); + mDefault.addEnableComponent(colorButton, false); + add(colorButton); + } + + { // Scale + add(new JLabel(trans.get("AppearanceCfg.lbl.texture.scale"))); + + add(new JLabel("x:"), "split 4"); + JSpinner scaleU = new JSpinner(new DoubleModel(ab, "ScaleX", TEXTURE_UNIT).getSpinnerModel()); + scaleU.setEditor(new SpinnerEditor(scaleU)); + mDefault.addEnableComponent(scaleU, false); + add(scaleU, "w 40"); + + add(new JLabel("y:")); + JSpinner scaleV = new JSpinner(new DoubleModel(ab, "ScaleY", TEXTURE_UNIT).getSpinnerModel()); + scaleV.setEditor(new SpinnerEditor(scaleV)); + mDefault.addEnableComponent(scaleV, false); + add(scaleV, "wrap, w 40"); + } + + {// Shine + add(new JLabel(trans.get("AppearanceCfg.lbl.shine"))); + DoubleModel shineModel = new DoubleModel(ab, "Shine", UnitGroup.UNITS_RELATIVE); + JSpinner spin = new JSpinner(shineModel.getSpinnerModel()); + spin.setEditor(new SpinnerEditor(spin)); + JSlider slide = new JSlider(shineModel.getSliderModel(0, 1)); + UnitSelector unit = new UnitSelector(shineModel); + + mDefault.addEnableComponent(slide, false); + mDefault.addEnableComponent(spin, false); + mDefault.addEnableComponent(unit, false); + + add(spin, "split 3, w 50"); + add(unit); + add(slide, "w 50"); + } + + + { // Offset + add(new JLabel(trans.get("AppearanceCfg.lbl.texture.offset"))); + + add(new JLabel("x:"), "split 4"); + JSpinner offsetU = new JSpinner(new DoubleModel(ab, "OffsetU", TEXTURE_UNIT).getSpinnerModel()); + offsetU.setEditor(new SpinnerEditor(offsetU)); + mDefault.addEnableComponent(offsetU, false); + add(offsetU, "w 40"); + + add(new JLabel("y:")); + JSpinner offsetV = new JSpinner(new DoubleModel(ab, "OffsetV", TEXTURE_UNIT).getSpinnerModel()); + offsetV.setEditor(new SpinnerEditor(offsetV)); + mDefault.addEnableComponent(offsetV, false); + add(offsetV, "wrap, w 40"); + } + + { // Repeat + add(new JLabel(trans.get("AppearanceCfg.lbl.texture.repeat"))); + EdgeMode[] list = new EdgeMode[EdgeMode.values().length + 1]; + System.arraycopy(EdgeMode.values(), 0, list, 1, EdgeMode.values().length); + JComboBox combo = new JComboBox(new EnumModel(ab, "EdgeMode", list)); + mDefault.addEnableComponent(combo, false); + add(combo); + } + + + { // Rotation + add(new JLabel(trans.get("AppearanceCfg.lbl.texture.rotation"))); + DoubleModel rotationModel = new DoubleModel(ab, "Rotation", UnitGroup.UNITS_ANGLE); + JSpinner rotation = new JSpinner(rotationModel.getSpinnerModel()); + rotation.setEditor(new SpinnerEditor(rotation)); + mDefault.addEnableComponent(rotation, false); + add(rotation, "split 3, w 50"); + add(new UnitSelector(rotationModel)); + BasicSlider bs = new BasicSlider(rotationModel.getSliderModel(-Math.PI, Math.PI)); + mDefault.addEnableComponent(bs, false); + add(bs, "w 50, wrap"); + } + + + } +} diff --git a/core/src/net/sf/openrocket/gui/configdialog/CommonStrings.java b/core/src/net/sf/openrocket/gui/configdialog/CommonStrings.java new file mode 100644 index 000000000..be0011c12 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/configdialog/CommonStrings.java @@ -0,0 +1,8 @@ +package net.sf.openrocket.gui.configdialog; + +import net.sf.openrocket.startup.Application; + +public class CommonStrings { + public final static String dagger = "\u2020"; + public final static String override_description = dagger + Application.getTranslator().get("Configuration.lbl.override"); +} diff --git a/core/src/net/sf/openrocket/gui/configdialog/MotorConfig.java b/core/src/net/sf/openrocket/gui/configdialog/MotorConfig.java index 45eca30f0..429f4bf1c 100644 --- a/core/src/net/sf/openrocket/gui/configdialog/MotorConfig.java +++ b/core/src/net/sf/openrocket/gui/configdialog/MotorConfig.java @@ -10,6 +10,7 @@ import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; +import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; @@ -22,17 +23,19 @@ import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.adaptors.BooleanModel; import net.sf.openrocket.gui.adaptors.DoubleModel; import net.sf.openrocket.gui.adaptors.EnumModel; -import net.sf.openrocket.gui.adaptors.MotorConfigurationModel; +import net.sf.openrocket.gui.adaptors.FlightConfigurationModel; import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.dialogs.flightconfiguration.FlightConfigurationDialog; import net.sf.openrocket.gui.dialogs.motor.MotorChooserDialog; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.rocketcomponent.Configuration; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; +import net.sf.openrocket.rocketcomponent.MotorConfiguration; import net.sf.openrocket.rocketcomponent.MotorMount; -import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; @@ -62,23 +65,23 @@ public class MotorConfig extends JPanel { check.setText(trans.get("MotorCfg.checkbox.compmotormount")); this.add(check, "wrap"); - + panel = new JPanel(new MigLayout("fill")); this.add(panel, "grow, wrap"); - + // Motor configuration selector //// Motor configuration: - panel.add(new JLabel(trans.get("MotorCfg.lbl.Motorcfg")), "shrink"); + panel.add(new JLabel(trans.get("MotorCfg.lbl.Flightcfg")), "shrink"); - JComboBox combo = new JComboBox(new MotorConfigurationModel(configuration)); + JComboBox combo = new JComboBox(new FlightConfigurationModel(configuration)); panel.add(combo, "growx"); - - configuration.addChangeListener(new ChangeListener() { + combo.addActionListener(new ActionListener() { @Override - public void stateChanged(ChangeEvent e) { + public void actionPerformed(ActionEvent e) { updateFields(); } + }); //// New button @@ -86,13 +89,24 @@ public class MotorConfig extends JPanel { button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - String id = rocket.newMotorConfigurationID(); - configuration.setMotorConfigurationID(id); + String id = rocket.newFlightConfigurationID(); + configuration.setFlightConfigurationID(id); + } + }); + panel.add(button, ""); + + //// Edit button + button = new JButton(trans.get("MotorCfg.but.FlightcfgEdit")); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JDialog configDialog = new FlightConfigurationDialog(rocket, SwingUtilities.windowForComponent(MotorConfig.this)); + configDialog.show(); } }); panel.add(button, "wrap unrel"); - + // Current motor: panel.add(new JLabel(trans.get("MotorCfg.lbl.Currentmotor")), "shrink"); @@ -101,8 +115,8 @@ public class MotorConfig extends JPanel { updateFields(); panel.add(motorLabel, "wrap unrel"); - - + + // Overhang //// Motor overhang: panel.add(new JLabel(trans.get("MotorCfg.lbl.Motoroverhang"))); @@ -116,29 +130,31 @@ public class MotorConfig extends JPanel { panel.add(new UnitSelector(dm), "width :30lp:"); panel.add(new BasicSlider(dm.getSliderModel(-0.02, 0.06)), "w 100lp, wrap unrel"); - - + + // Select ignition event //// Ignition at: - panel.add(new JLabel(trans.get("MotorCfg.lbl.Ignitionat")), ""); + panel.add(new JLabel(trans.get("MotorCfg.lbl.Ignitionat") + CommonStrings.dagger), ""); - combo = new JComboBox(new EnumModel(mount, "IgnitionEvent")); + IgnitionConfiguration ignitionConfig = mount.getIgnitionConfiguration().getDefault(); + combo = new JComboBox(new EnumModel(ignitionConfig, "IgnitionEvent")); panel.add(combo, "growx, wrap"); // ... and delay //// plus panel.add(new JLabel(trans.get("MotorCfg.lbl.plus")), "gap indent, skip 1, span, split"); - dm = new DoubleModel(mount, "IgnitionDelay", 0); + dm = new DoubleModel(ignitionConfig, "IgnitionDelay", 0); spin = new JSpinner(dm.getSpinnerModel()); - spin.setEditor(new SpinnerEditor(spin,3)); + spin.setEditor(new SpinnerEditor(spin, 3)); panel.add(spin, "gap rel rel"); //// seconds panel.add(new JLabel(trans.get("MotorCfg.lbl.seconds")), "wrap unrel"); - - + panel.add(new StyledLabel(CommonStrings.override_description, -1), "spanx, wrap para"); + + // Check stage count RocketComponent c = (RocketComponent) mount; c = c.getRocket(); @@ -150,23 +166,23 @@ public class MotorConfig extends JPanel { panel.add(new StyledLabel(trans.get("MotorCfg.lbl.longA1") + " " + trans.get("MotorCfg.lbl.longA2"), -1), - "spanx, right, wrap para"); + "spanx, wrap para"); } else { //// The current design has //// stages. panel.add(new StyledLabel(trans.get("MotorCfg.lbl.longB1") + " " + stages + " " + trans.get("MotorCfg.lbl.longB2"), -1), - "skip 1, spanx, wrap para"); + "spanx, wrap para"); } - + // Select etc. buttons //// Select motor button = new JButton(trans.get("MotorCfg.but.Selectmotor")); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - String id = configuration.getMotorConfigurationID(); + String id = configuration.getFlightConfigurationID(); MotorChooserDialog dialog = new MotorChooserDialog(mount.getMotor(id), mount.getMotorDelay(id), mount.getMotorMountDiameter(), @@ -177,11 +193,13 @@ public class MotorConfig extends JPanel { if (m != null) { if (id == null) { - id = rocket.newMotorConfigurationID(); - configuration.setMotorConfigurationID(id); + id = rocket.newFlightConfigurationID(); + configuration.setFlightConfigurationID(id); } - mount.setMotor(id, m); - mount.setMotorDelay(id, d); + MotorConfiguration config = new MotorConfiguration(); + config.setMotor(m); + config.setEjectionDelay(d); + mount.getMotorConfiguration().set(id, config); } updateFields(); } @@ -193,16 +211,16 @@ public class MotorConfig extends JPanel { button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - mount.setMotor(configuration.getMotorConfigurationID(), null); + mount.getMotorConfiguration().resetDefault(configuration.getFlightConfigurationID()); updateFields(); } }); panel.add(button, "growx, wrap"); - - - - + + + + // Set enabled status setDeepEnabled(panel, motorMount.isMotorMount()); @@ -216,7 +234,7 @@ public class MotorConfig extends JPanel { } public void updateFields() { - String id = configuration.getMotorConfigurationID(); + String id = configuration.getFlightConfigurationID(); Motor m = mount.getMotor(id); if (m == null) { //// None diff --git a/core/src/net/sf/openrocket/gui/configdialog/ParachuteConfig.java b/core/src/net/sf/openrocket/gui/configdialog/ParachuteConfig.java index 1e767a78a..0f50a27e0 100644 --- a/core/src/net/sf/openrocket/gui/configdialog/ParachuteConfig.java +++ b/core/src/net/sf/openrocket/gui/configdialog/ParachuteConfig.java @@ -24,8 +24,7 @@ import net.sf.openrocket.gui.components.StyledLabel.Style; import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.material.Material; -import net.sf.openrocket.rocketcomponent.MassObject; -import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; import net.sf.openrocket.rocketcomponent.Parachute; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; @@ -36,6 +35,7 @@ public class ParachuteConfig extends RecoveryDeviceConfig { public ParachuteConfig(OpenRocketDocument d, final RocketComponent component) { super(d, component); + Parachute parachute = (Parachute) component; JPanel primary = new JPanel(new MigLayout()); @@ -190,29 +190,30 @@ public class ParachuteConfig extends RecoveryDeviceConfig { //// Deployment //// Deploys at: - panel.add(new JLabel(trans.get("ParachuteCfg.lbl.Deploysat")), ""); + panel.add(new JLabel(trans.get("ParachuteCfg.lbl.Deploysat") + CommonStrings.dagger), ""); - combo = new JComboBox(new EnumModel(component, "DeployEvent")); + DeploymentConfiguration deploymentConfig = parachute.getDeploymentConfiguration().getDefault(); + combo = new JComboBox(new EnumModel(deploymentConfig, "DeployEvent")); panel.add(combo, "spanx 3, growx, wrap"); // ... and delay //// plus panel.add(new JLabel(trans.get("ParachuteCfg.lbl.plusdelay")), "right"); - m = new DoubleModel(component, "DeployDelay", 0); + m = new DoubleModel(deploymentConfig, "DeployDelay", 0); spin = new JSpinner(m.getSpinnerModel()); - spin.setEditor(new SpinnerEditor(spin,3)); + spin.setEditor(new SpinnerEditor(spin, 3)); panel.add(spin, "spanx, split"); //// seconds panel.add(new JLabel(trans.get("ParachuteCfg.lbl.seconds")), "wrap paragraph"); // Altitude: - label = new JLabel(trans.get("ParachuteCfg.lbl.Altitude")); + label = new JLabel(trans.get("ParachuteCfg.lbl.Altitude") + CommonStrings.dagger); altitudeComponents.add(label); panel.add(label); - m = new DoubleModel(component, "DeployAltitude", UnitGroup.UNITS_DISTANCE, 0); + m = new DoubleModel(deploymentConfig, "DeployAltitude", UnitGroup.UNITS_DISTANCE, 0); spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); @@ -225,6 +226,7 @@ public class ParachuteConfig extends RecoveryDeviceConfig { altitudeComponents.add(slider); panel.add(slider, "w 100lp, wrap"); + panel.add(new StyledLabel(CommonStrings.override_description, -1), "spanx, wrap para"); primary.add(panel, "grow"); @@ -241,7 +243,6 @@ public class ParachuteConfig extends RecoveryDeviceConfig { - protected JPanel positionTab() { JPanel panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", "")); @@ -277,8 +278,8 @@ public class ParachuteConfig extends RecoveryDeviceConfig { button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - ((MassObject) component).setRadialDirection(0.0); - ((MassObject) component).setRadialPosition(0.0); + ((Parachute) component).setRadialDirection(0.0); + ((Parachute) component).setRadialPosition(0.0); } }); panel.add(button, "spanx, right"); diff --git a/core/src/net/sf/openrocket/gui/configdialog/RecoveryDeviceConfig.java b/core/src/net/sf/openrocket/gui/configdialog/RecoveryDeviceConfig.java index f93613c96..2f5f37c30 100644 --- a/core/src/net/sf/openrocket/gui/configdialog/RecoveryDeviceConfig.java +++ b/core/src/net/sf/openrocket/gui/configdialog/RecoveryDeviceConfig.java @@ -6,6 +6,7 @@ import java.util.List; import javax.swing.JComponent; import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; import net.sf.openrocket.rocketcomponent.RecoveryDevice; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -19,7 +20,7 @@ public abstract class RecoveryDeviceConfig extends RocketComponentConfig { } - + @Override public void updateFields() { super.updateFields(); @@ -27,8 +28,7 @@ public abstract class RecoveryDeviceConfig extends RocketComponentConfig { if (altitudeComponents == null) return; - boolean enabled = (((RecoveryDevice) component).getDeployEvent() - == RecoveryDevice.DeployEvent.ALTITUDE); + boolean enabled = (((RecoveryDevice) component).getDeploymentConfiguration().getDefault().getDeployEvent() == DeployEvent.ALTITUDE); for (JComponent c : altitudeComponents) { c.setEnabled(enabled); diff --git a/core/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/core/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index c2fbd63ec..2e8a2a59d 100644 --- a/core/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/core/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -1,7 +1,6 @@ package net.sf.openrocket.gui.configdialog; -import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; @@ -14,7 +13,6 @@ import java.util.Locale; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; -import javax.swing.JColorChooser; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; @@ -34,13 +32,10 @@ import net.sf.openrocket.gui.adaptors.EnumModel; import net.sf.openrocket.gui.adaptors.MaterialModel; import net.sf.openrocket.gui.adaptors.PresetModel; import net.sf.openrocket.gui.components.BasicSlider; -import net.sf.openrocket.gui.components.ColorIcon; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.StyledLabel.Style; import net.sf.openrocket.gui.components.UnitSelector; -import net.sf.openrocket.gui.util.ColorConversion; import net.sf.openrocket.gui.util.GUIUtil; -import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.material.Material; import net.sf.openrocket.preset.ComponentPreset; @@ -52,7 +47,6 @@ import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.Invalidatable; -import net.sf.openrocket.util.LineStyle; public class RocketComponentConfig extends JPanel { @@ -70,8 +64,7 @@ public class RocketComponentConfig extends JPanel { protected final JTextField componentNameField; protected JTextArea commentTextArea; private final TextFieldListener textFieldListener; - private JButton colorButton; - private JCheckBox colorDefault; + private JPanel buttonPanel; private JLabel infoLabel; @@ -113,9 +106,11 @@ public class RocketComponentConfig extends JPanel { tabbedPane.addTab(trans.get("RocketCompCfg.tab.Override"), null, overrideTab(), trans.get("RocketCompCfg.tab.MassandCGoverride")); if (component.isMassive()) - //// Figure and Figure style options - tabbedPane.addTab(trans.get("RocketCompCfg.tab.Figure"), null, figureTab(), - trans.get("RocketCompCfg.tab.Figstyleopt")); + + //// Appearance options + tabbedPane.addTab("Appearance", null, new AppearancePanel(document,component), + "Appearance Tool Tip"); + //// Comment and Specify a comment for the component tabbedPane.addTab(trans.get("RocketCompCfg.tab.Comment"), null, commentTab(), trans.get("RocketCompCfg.tab.Specifyacomment")); @@ -165,14 +160,6 @@ public class RocketComponentConfig extends JPanel { // Component name componentNameField.setText(component.getName()); - // Component color and "Use default color" checkbox - if (colorButton != null && colorDefault != null) { - colorButton.setIcon(new ColorIcon(getColor())); - - if ((component.getColor() == null) != colorDefault.isSelected()) - colorDefault.setSelected(component.getColor() == null); - } - // Info label StringBuilder sb = new StringBuilder(); @@ -385,93 +372,6 @@ public class RocketComponentConfig extends JPanel { } - - private JPanel figureTab() { - JPanel panel = new JPanel(new MigLayout("align 20% 20%")); - - //// Figure style: - panel.add(new StyledLabel(trans.get("RocketCompCfg.lbl.Figurestyle"), Style.BOLD), "wrap para"); - - //// Component color: - panel.add(new JLabel(trans.get("RocketCompCfg.lbl.Componentcolor")), "gapleft para, gapright 10lp"); - - colorButton = new JButton(new ColorIcon(getColor())); - colorButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - net.sf.openrocket.util.Color c = component.getColor(); - if (c == null) { - c = Application.getPreferences().getDefaultColor(component.getClass()); - } - - //// Choose color - Color awtColor = ColorConversion.toAwtColor(c); - awtColor = JColorChooser.showDialog(tabbedPane, trans.get("RocketCompCfg.lbl.Choosecolor"), awtColor); - c = ColorConversion.fromAwtColor(awtColor); - if (c != null) { - component.setColor(c); - } - } - }); - panel.add(colorButton, "gapright 10lp"); - - //// Use default color - colorDefault = new JCheckBox(trans.get("RocketCompCfg.checkbox.Usedefaultcolor")); - if (component.getColor() == null) - colorDefault.setSelected(true); - colorDefault.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (colorDefault.isSelected()) - component.setColor(null); - else - component.setColor(((SwingPreferences) Application.getPreferences()).getDefaultColor(component.getClass())); - } - }); - panel.add(colorDefault, "wrap para"); - - //// Component line style: - panel.add(new JLabel(trans.get("RocketCompCfg.lbl.Complinestyle")), "gapleft para, gapright 10lp"); - - LineStyle[] list = new LineStyle[LineStyle.values().length + 1]; - System.arraycopy(LineStyle.values(), 0, list, 1, LineStyle.values().length); - - JComboBox combo = new JComboBox(new EnumModel(component, "LineStyle", - //// Default style - list, trans.get("LineStyle.Defaultstyle"))); - panel.add(combo, "spanx 2, growx, wrap 50lp"); - - //// Save as default style - JButton button = new JButton(trans.get("RocketCompCfg.but.Saveasdefstyle")); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (component.getColor() != null) { - ((SwingPreferences) Application.getPreferences()).setDefaultColor(component.getClass(), component.getColor()); - component.setColor(null); - } - if (component.getLineStyle() != null) { - Application.getPreferences().setDefaultLineStyle(component.getClass(), component.getLineStyle()); - component.setLineStyle(null); - } - } - }); - panel.add(button, "gapleft para, spanx 3, growx, wrap"); - - return panel; - } - - - private Color getColor() { - net.sf.openrocket.util.Color c = component.getColor(); - if (c == null) { - c = Application.getPreferences().getDefaultColor(component.getClass()); - } - return ColorConversion.toAwtColor(c); - } - - - protected JPanel shoulderTab() { JPanel panel = new JPanel(new MigLayout("fill")); JPanel sub; diff --git a/core/src/net/sf/openrocket/gui/configdialog/StageConfig.java b/core/src/net/sf/openrocket/gui/configdialog/StageConfig.java index 218172972..b1e868ec0 100644 --- a/core/src/net/sf/openrocket/gui/configdialog/StageConfig.java +++ b/core/src/net/sf/openrocket/gui/configdialog/StageConfig.java @@ -15,7 +15,7 @@ import net.sf.openrocket.gui.components.StyledLabel.Style; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Stage; -import net.sf.openrocket.rocketcomponent.Stage.SeparationEvent; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; import net.sf.openrocket.startup.Application; public class StageConfig extends RocketComponentConfig { @@ -38,22 +38,25 @@ public class StageConfig extends RocketComponentConfig { JPanel panel = new JPanel(new MigLayout("fill")); // Select separation event - panel.add(new StyledLabel(trans.get("separation.lbl.title"), Style.BOLD), "spanx, wrap rel"); + panel.add(new StyledLabel(trans.get("separation.lbl.title") + CommonStrings.dagger, Style.BOLD), "spanx, wrap rel"); - JComboBox combo = new JComboBox(new EnumModel(stage, "SeparationEvent")); + StageSeparationConfiguration config = stage.getStageSeparationConfiguration().getDefault(); + JComboBox combo = new JComboBox(new EnumModel(config, "SeparationEvent")); panel.add(combo, ""); // ... and delay panel.add(new JLabel(trans.get("separation.lbl.plus")), ""); - DoubleModel dm = new DoubleModel(stage, "SeparationDelay", 0); + DoubleModel dm = new DoubleModel(config, "SeparationDelay", 0); JSpinner spin = new JSpinner(dm.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); - panel.add(spin, ""); + panel.add(spin, "width 45"); //// seconds panel.add(new JLabel(trans.get("separation.lbl.seconds")), "wrap unrel"); + panel.add(new StyledLabel(CommonStrings.override_description, -1), "spanx, wrap para"); + return panel; } diff --git a/core/src/net/sf/openrocket/gui/configdialog/StreamerConfig.java b/core/src/net/sf/openrocket/gui/configdialog/StreamerConfig.java index 491370454..80a713c2b 100644 --- a/core/src/net/sf/openrocket/gui/configdialog/StreamerConfig.java +++ b/core/src/net/sf/openrocket/gui/configdialog/StreamerConfig.java @@ -23,9 +23,9 @@ import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.material.Material; -import net.sf.openrocket.rocketcomponent.MassComponent; -import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Streamer; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.UnitGroup; @@ -34,6 +34,7 @@ public class StreamerConfig extends RecoveryDeviceConfig { public StreamerConfig(OpenRocketDocument d, final RocketComponent component) { super(d, component); + Streamer streamer = (Streamer) component; JPanel primary = new JPanel(new MigLayout()); @@ -191,29 +192,30 @@ public class StreamerConfig extends RecoveryDeviceConfig { //// Deployment //// Deploys at: - panel.add(new JLabel(trans.get("StreamerCfg.lbl.Deploysat")), ""); + panel.add(new JLabel(trans.get("StreamerCfg.lbl.Deploysat") + CommonStrings.dagger), ""); - combo = new JComboBox(new EnumModel(component, "DeployEvent")); + DeploymentConfiguration deploymentConfig = streamer.getDeploymentConfiguration().getDefault(); + combo = new JComboBox(new EnumModel(deploymentConfig, "DeployEvent")); panel.add(combo, "spanx 3, growx, wrap"); // ... and delay //// plus panel.add(new JLabel(trans.get("StreamerCfg.lbl.plusdelay")), "right"); - m = new DoubleModel(component, "DeployDelay", 0); + m = new DoubleModel(deploymentConfig, "DeployDelay", 0); spin = new JSpinner(m.getSpinnerModel()); - spin.setEditor(new SpinnerEditor(spin,3)); + spin.setEditor(new SpinnerEditor(spin, 3)); panel.add(spin, "spanx, split"); //// seconds panel.add(new JLabel(trans.get("StreamerCfg.lbl.seconds")), "wrap paragraph"); // Altitude: - label = new JLabel(trans.get("StreamerCfg.lbl.Altitude")); + label = new JLabel(trans.get("StreamerCfg.lbl.Altitude") + CommonStrings.dagger); altitudeComponents.add(label); panel.add(label); - m = new DoubleModel(component, "DeployAltitude", UnitGroup.UNITS_DISTANCE, 0); + m = new DoubleModel(deploymentConfig, "DeployAltitude", UnitGroup.UNITS_DISTANCE, 0); spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); @@ -226,6 +228,7 @@ public class StreamerConfig extends RecoveryDeviceConfig { altitudeComponents.add(slider); panel.add(slider, "w 100lp, wrap"); + panel.add(new StyledLabel(CommonStrings.override_description, -1), "skip 1, spanx, wrap para"); primary.add(panel, "grow"); @@ -280,8 +283,8 @@ public class StreamerConfig extends RecoveryDeviceConfig { button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - ((MassComponent) component).setRadialDirection(0.0); - ((MassComponent) component).setRadialPosition(0.0); + ((Streamer) component).setRadialDirection(0.0); + ((Streamer) component).setRadialPosition(0.0); } }); panel.add(button, "spanx, right"); diff --git a/core/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java b/core/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java index 41358d911..20856d2c9 100644 --- a/core/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java +++ b/core/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java @@ -20,7 +20,6 @@ import javax.swing.filechooser.FileNameExtensionFilter; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.file.DatabaseMotorFinder; import net.sf.openrocket.file.GeneralRocketLoader; import net.sf.openrocket.file.RocketLoadException; import net.sf.openrocket.gui.components.UnitSelector; @@ -41,7 +40,7 @@ public class CustomExpressionPanel extends JPanel { public CustomExpressionPanel(final OpenRocketDocument doc, final JDialog parentDialog) { super(new MigLayout("fill")); this.doc = doc; - + expressionSelectorPanel = new JPanel(new MigLayout("gapy rel")); expressionSelectorPanel.setToolTipText(trans.get("customExpressionPanel.lbl.CalcNote")); @@ -82,24 +81,24 @@ public class CustomExpressionPanel extends JPanel { //Create a file chooser final JFileChooser fc = new JFileChooser(); - if (doc.getFile() != null){ + if (doc.getFile() != null) { fc.setCurrentDirectory(doc.getFile().getParentFile()); } fc.setFileFilter(new FileNameExtensionFilter("Openrocket file", "ork")); fc.setAcceptAllFileFilterUsed(false); int returnVal = fc.showOpenDialog(CustomExpressionPanel.this); - if (returnVal == JFileChooser.APPROVE_OPTION){ + if (returnVal == JFileChooser.APPROVE_OPTION) { File importFile = fc.getSelectedFile(); - log.info("User selected a file to import expressions from "+fc.getSelectedFile().toString()); + log.info("User selected a file to import expressions from " + fc.getSelectedFile().toString()); //TODO: This should probably be somewhere else and ideally we would use an alternative minimal rocket loader. Still, it doesn't seem particularly slow this way. // Load expressions from selected document - GeneralRocketLoader loader = new GeneralRocketLoader(); + GeneralRocketLoader loader = new GeneralRocketLoader(importFile); try { - OpenRocketDocument importedDocument = loader.load(importFile, new DatabaseMotorFinder()); - for (CustomExpression exp : importedDocument.getCustomExpressions()){ + OpenRocketDocument importedDocument = loader.load(); + for (CustomExpression exp : importedDocument.getCustomExpressions()) { doc.addCustomExpression(exp); } } catch (RocketLoadException e1) { @@ -128,20 +127,20 @@ public class CustomExpressionPanel extends JPanel { /* * Update the expressionSelectorPanel */ - private void updateExpressions(){ + private void updateExpressions() { expressionSelectorPanel.removeAll(); int totalExpressions = doc.getCustomExpressions().size(); - for (int i=0; i expressions = doc.getCustomExpressions(); int i = expressions.indexOf(expression); - if (i+move == expressions.size() || i+move < 0) + if (i + move == expressions.size() || i + move < 0) return; else - Collections.swap(expressions, i, i+move); + Collections.swap(expressions, i, i + move); } - + /* * A JPanel which configures a single expression @@ -166,10 +165,10 @@ public class CustomExpressionPanel extends JPanel { private class SingleExpression extends JPanel { // Convenience method to make the labels consistent - private JLabel setLabelStyle(JLabel l){ + private JLabel setLabelStyle(JLabel l) { l.setBackground(Color.WHITE); l.setOpaque(true); - l.setBorder(BorderFactory.createRaisedBevelBorder() ); + l.setBorder(BorderFactory.createRaisedBevelBorder()); l.setText(" " + l.getText() + " "); return l; } @@ -179,15 +178,15 @@ public class CustomExpressionPanel extends JPanel { // name: aName symbol: a Unit: m/s //super(new MigLayout("","[::100][:200:400][::100][:100:200][::100][:100:200]","")); - JLabel nameLabel = new JLabel( trans.get("customExpression.Name")+ " :"); - JLabel name = new JLabel ( expression.getName() ); + JLabel nameLabel = new JLabel(trans.get("customExpression.Name") + " :"); + JLabel name = new JLabel(expression.getName()); name = setLabelStyle(name); - JLabel symbolLabel = new JLabel( trans.get("customExpression.Symbol")+ " :" ); - JLabel symbol = new JLabel ( expression.getSymbol()); + JLabel symbolLabel = new JLabel(trans.get("customExpression.Symbol") + " :"); + JLabel symbol = new JLabel(expression.getSymbol()); symbol = setLabelStyle(symbol); symbol.setBackground(Color.WHITE); - JLabel unitLabel = new JLabel( trans.get("customExpression.Units")+ " :"); + JLabel unitLabel = new JLabel(trans.get("customExpression.Units") + " :"); UnitSelector unitSelector = new UnitSelector(expression.getType().getUnitGroup()); //JLabel unitSelector = new JLabel ( expression.getUnit() ); //unitSelector = setLabelStyle(unitSelector); @@ -196,9 +195,9 @@ public class CustomExpressionPanel extends JPanel { JButton editButton = new JButton(Icons.EDIT); editButton.setToolTipText(trans.get("customExpression.Units.but.ttip.Edit")); editButton.setBorderPainted(false); - editButton.addActionListener( new ActionListener() { + editButton.addActionListener(new ActionListener() { @Override - public void actionPerformed(ActionEvent e){ + public void actionPerformed(ActionEvent e) { Window parent = SwingUtilities.getWindowAncestor(CustomExpressionPanel.this); new ExpressionBuilderDialog(parent, doc, expression).setVisible(true); updateExpressions(); @@ -209,7 +208,7 @@ public class CustomExpressionPanel extends JPanel { upButton.setToolTipText(trans.get("customExpression.Units.but.ttip.MoveUp")); upButton.setBorderPainted(false); upButton.setVisible(showUp); - upButton.addActionListener( new ActionListener() { + upButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { moveExpression(expression, -1); @@ -221,7 +220,7 @@ public class CustomExpressionPanel extends JPanel { downButton.setToolTipText(trans.get("customExpression.Units.but.ttip.MoveDown")); downButton.setBorderPainted(false); downButton.setVisible(showDown); - downButton.addActionListener( new ActionListener() { + downButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { moveExpression(expression, 1); diff --git a/core/src/net/sf/openrocket/gui/customexpression/OperatorSelector.java b/core/src/net/sf/openrocket/gui/customexpression/OperatorSelector.java index 4a66d15ee..a4daa254c 100644 --- a/core/src/net/sf/openrocket/gui/customexpression/OperatorSelector.java +++ b/core/src/net/sf/openrocket/gui/customexpression/OperatorSelector.java @@ -26,13 +26,12 @@ import net.miginfocom.swing.MigLayout; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.TextUtil; public class OperatorSelector extends JDialog { private static final Translator trans = Application.getTranslator(); private static final LogHelper log = Application.getLogger(); - + @SuppressWarnings("unused") private final Window parentWindow; @@ -40,7 +39,7 @@ public class OperatorSelector extends JDialog { private final OperatorTableModel tableModel; private final ExpressionBuilderDialog parentBuilder; - public OperatorSelector(Window parent, final ExpressionBuilderDialog parentBuilder){ + public OperatorSelector(Window parent, final ExpressionBuilderDialog parentBuilder) { super(parent, trans.get("CustomOperatorSelector.title"), JDialog.ModalityType.DOCUMENT_MODAL); @@ -57,19 +56,19 @@ public class OperatorSelector extends JDialog { table.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); int width = table.getColumnModel().getTotalColumnWidth(); - table.getColumnModel().getColumn(0).setPreferredWidth( (int) (.1 * width)); - table.getColumnModel().getColumn(1).setPreferredWidth( (int) (.9 * width)); + table.getColumnModel().getColumn(0).setPreferredWidth((int) (.1 * width)); + table.getColumnModel().getColumn(1).setPreferredWidth((int) (.9 * width)); table.setAutoCreateRowSorter(true); - table.addMouseMotionListener(new MouseMotionAdapter(){ + table.addMouseMotionListener(new MouseMotionAdapter() { @Override - public void mouseMoved(MouseEvent e){ + public void mouseMoved(MouseEvent e) { Point p = e.getPoint(); int row = table.rowAtPoint(p); int col = table.columnAtPoint(p); - if (col == 1 && row > -1){ + if (col == 1 && row > -1) { String description = String.valueOf(table.getValueAt(row, 1)); - description = TextUtil.wrap(description, 60); + description = wrap(description, 60); table.setToolTipText(description); } else { table.setToolTipText(null); @@ -77,29 +76,37 @@ public class OperatorSelector extends JDialog { } }); - table.addMouseListener(new MouseListener(){ + table.addMouseListener(new MouseListener() { @Override - public void mouseClicked(MouseEvent e){ - if (e.getClickCount() == 2){ + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { log.debug("Selected operator by double clicking."); selectOperator(); } } + @Override - public void mouseEntered(MouseEvent e) {} + public void mouseEntered(MouseEvent e) { + } + @Override - public void mouseExited(MouseEvent e) {} + public void mouseExited(MouseEvent e) { + } + @Override - public void mousePressed(MouseEvent e) {} + public void mousePressed(MouseEvent e) { + } + @Override - public void mouseReleased(MouseEvent e) {} - } ); + public void mouseReleased(MouseEvent e) { + } + }); InputMap inputMap = table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); ActionMap actionMap = table.getActionMap(); KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); inputMap.put(enter, "select"); - actionMap.put("select", new AbstractAction(){ + actionMap.put("select", new AbstractAction() { @Override public void actionPerformed(ActionEvent arg0) { log.debug("Selected operator by enter key"); @@ -110,16 +117,16 @@ public class OperatorSelector extends JDialog { JScrollPane scrollPane = new JScrollPane(table); table.setFillsViewportHeight(true); table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e){ - if (table.getSelectedRowCount() == 1){ - insertButton.setEnabled(true); - } - else { - insertButton.setEnabled(false); - } + @Override + public void valueChanged(ListSelectionEvent e) { + if (table.getSelectedRowCount() == 1) { + insertButton.setEnabled(true); } - }); + else { + insertButton.setEnabled(false); + } + } + }); mainPanel.add(scrollPane, "wrap, push, grow"); @@ -142,17 +149,32 @@ public class OperatorSelector extends JDialog { }); insertButton.setEnabled(false); // disabled by default, only enable when a variable selected mainPanel.add(insertButton, "right, width :100:200, wrap"); - + this.add(mainPanel); this.validate(); this.pack(); - this.setLocationByPlatform(true); + this.setLocationByPlatform(true); } - private void selectOperator(){ + private void selectOperator() { int row = table.getSelectedRow(); String str = table.getValueAt(row, 0).toString(); parentBuilder.pasteIntoExpression(str); OperatorSelector.this.dispose(); } + + + /* + * Returns a word-wrapped version of given input string using HTML syntax, wrapped to len characters. + */ + private String wrap(String in, int len) { + in = in.trim(); + if (in.length() < len) + return in; + if (in.substring(0, len).contains("\n")) + return in.substring(0, in.indexOf("\n")).trim() + "\n\n" + wrap(in.substring(in.indexOf("\n") + 1), len); + int place = Math.max(Math.max(in.lastIndexOf(" ", len), in.lastIndexOf("\t", len)), in.lastIndexOf("-", len)); + return "" + in.substring(0, place).trim() + "
" + wrap(in.substring(place), len); + } + } diff --git a/core/src/net/sf/openrocket/gui/dialogs/AboutDialog.java b/core/src/net/sf/openrocket/gui/dialogs/AboutDialog.java index 5b7d6cc93..12e99f10d 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/AboutDialog.java +++ b/core/src/net/sf/openrocket/gui/dialogs/AboutDialog.java @@ -69,7 +69,7 @@ public class AboutDialog extends JDialog { sub.add(new StyledLabel("OpenRocket", 20), "ax 50%, growy, wrap para"); sub.add(new StyledLabel(trans.get("lbl.version").trim() + " " + version, 3), "ax 50%, growy, wrap rel"); - sub.add(new StyledLabel("Copyright " + Chars.COPY + " 2007-2012 Sampo Niskanen and others"), "ax 50%, growy, wrap para"); + sub.add(new StyledLabel("Copyright " + Chars.COPY + " 2007-2013 Sampo Niskanen and others"), "ax 50%, growy, wrap para"); sub.add(new URLLabel(OPENROCKET_URL), "ax 50%, growy, wrap para"); panel.add(sub, "grow"); diff --git a/core/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java b/core/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java index ec9896647..9a2320bf7 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java +++ b/core/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java @@ -12,6 +12,7 @@ import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; +import java.util.EventObject; import java.util.List; import java.util.Map; import java.util.Vector; @@ -44,7 +45,7 @@ import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.gui.adaptors.Column; import net.sf.openrocket.gui.adaptors.ColumnTableModel; import net.sf.openrocket.gui.adaptors.DoubleModel; -import net.sf.openrocket.gui.adaptors.MotorConfigurationModel; +import net.sf.openrocket.gui.adaptors.FlightConfigurationModel; import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.StageSelector; import net.sf.openrocket.gui.components.StyledLabel; @@ -64,13 +65,14 @@ import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.StateChangeListener; -public class ComponentAnalysisDialog extends JDialog implements ChangeListener { +public class ComponentAnalysisDialog extends JDialog implements StateChangeListener { private static ComponentAnalysisDialog singletonDialog = null; private static final Translator trans = Application.getTranslator(); - - + + private final FlightConditions conditions; private final Configuration configuration; private final DoubleModel theta, aoa, mach, roll; @@ -85,7 +87,7 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { private final JList warningList; - + private final List cpData = new ArrayList(); private final List cgData = new ArrayList(); private final List dragData = new ArrayList(); @@ -95,7 +97,7 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { public ComponentAnalysisDialog(final RocketPanel rocketPanel) { ////Component analysis - super(SwingUtilities.getWindowAncestor(rocketPanel), + super(SwingUtilities.getWindowAncestor(rocketPanel), trans.get("componentanalysisdlg.componentanalysis")); JTable table; @@ -106,7 +108,7 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { this.configuration = rocketPanel.getConfiguration(); this.aerodynamicCalculator = rocketPanel.getAerodynamicCalculator().newInstance(); - + conditions = new FlightConditions(configuration); rocketPanel.setCPAOA(0); @@ -141,7 +143,7 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { }); panel.add(worstToggle, ""); - + warningList = new JList(); JScrollPane scrollPane = new JScrollPane(warningList); ////Warnings: @@ -164,7 +166,7 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { panel.add(new BasicSlider(roll.getSliderModel(-20 * 2 * Math.PI, 20 * 2 * Math.PI)), "growx, wrap paragraph"); - + // Stage and motor selection: //// Active stages: panel.add(new JLabel(trans.get("componentanalysisdlg.lbl.activestages")), "spanx, split, gapafter rel"); @@ -174,35 +176,35 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { JLabel label = new JLabel(trans.get("componentanalysisdlg.lbl.motorconf")); label.setHorizontalAlignment(JLabel.RIGHT); panel.add(label, "growx, right"); - panel.add(new JComboBox(new MotorConfigurationModel(configuration)), "wrap"); + panel.add(new JComboBox(new FlightConfigurationModel(configuration)), "wrap"); + + - - // Tabbed pane JTabbedPane tabbedPane = new JTabbedPane(); panel.add(tabbedPane, "spanx, growx, growy"); - + // Create the CP data table cpTableModel = new ColumnTableModel( - - //// Component - new Column(trans.get("componentanalysisdlg.TabStability.Col.Component")) { - @Override - public Object getValueAt(int row) { - RocketComponent c = cpData.get(row).getComponent(); - if (c instanceof Rocket) { - return trans.get("componentanalysisdlg.TOTAL"); - } - return c.toString(); - } - - @Override - public int getDefaultWidth() { - return 200; - } - }, + + //// Component + new Column(trans.get("componentanalysisdlg.TabStability.Col.Component")) { + @Override + public Object getValueAt(int row) { + RocketComponent c = cpData.get(row).getComponent(); + if (c instanceof Rocket) { + return trans.get("componentanalysisdlg.TOTAL"); + } + return c.toString(); + } + + @Override + public int getDefaultWidth() { + return 200; + } + }, new Column(trans.get("componentanalysisdlg.TabStability.Col.CG") + " / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit()) { private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit(); @@ -233,13 +235,13 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { return NOUNIT.toString(cpData.get(row).getCP().weight); } } - - ) { - @Override - public int getRowCount() { - return cpData.size(); - } - }; + + ) { + @Override + public int getRowCount() { + return cpData.size(); + } + }; table = new JTable(cpTableModel); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); @@ -255,11 +257,11 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { scrollpane.setPreferredSize(new Dimension(600, 200)); //// Stability and Stability information - tabbedPane.addTab(trans.get("componentanalysisdlg.TabStability"), + tabbedPane.addTab(trans.get("componentanalysisdlg.TabStability"), null, scrollpane, trans.get("componentanalysisdlg.TabStability.ttip")); - - + + // Create the drag data table dragTableModel = new ColumnTableModel( //// Component @@ -313,7 +315,7 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { } }; - + table = new JTable(dragTableModel); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.setSelectionBackground(Color.LIGHT_GRAY); @@ -331,9 +333,9 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { tabbedPane.addTab(trans.get("componentanalysisdlg.dragTabchar"), null, scrollpane, trans.get("componentanalysisdlg.dragTabchar.ttip")); - - - + + + // Create the roll data table rollTableModel = new ColumnTableModel( //// Component @@ -375,7 +377,7 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { } }; - + table = new JTable(rollTableModel); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.setSelectionBackground(Color.LIGHT_GRAY); @@ -386,13 +388,13 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { scrollpane.setPreferredSize(new Dimension(600, 200)); //// Roll dynamics and Roll dynamics tooltip - tabbedPane.addTab(trans.get("componentanalysisdlg.rollTableModel"), null, scrollpane, + tabbedPane.addTab(trans.get("componentanalysisdlg.rollTableModel"), null, scrollpane, trans.get("componentanalysisdlg.rollTableModel.ttip")); - - - - + + + + // Add the data updater to listen to changes in aoa and theta mach.addChangeListener(this); theta.addChangeListener(this); @@ -401,8 +403,8 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { configuration.addChangeListener(this); this.stateChanged(null); - - + + // Remove listeners when closing window this.addWindowListener(new WindowAdapter() { @Override @@ -437,8 +439,8 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { sel.resizeFont(-1); panel.add(sel, "wrap"); - - + + // Buttons JButton button; @@ -468,7 +470,7 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { }); panel.add(button, "span, split, tag cancel"); - + this.setLocationByPlatform(true); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); pack(); @@ -477,12 +479,12 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { } - + /** * Updates the data in the table and fires a table data change event. */ @Override - public void stateChanged(ChangeEvent e) { + public void stateChanged(EventObject e) { AerodynamicForces forces; WarningSet set = new WarningSet(); conditions.setAOA(aoa.getValue()); @@ -506,7 +508,7 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { Map massData = massCalculator.getCGAnalysis(configuration, MassCalcType.LAUNCH_MASS); - + cpData.clear(); cgData.clear(); dragData.clear(); @@ -583,7 +585,7 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener { } - + private class DragCellRenderer extends JLabel implements TableCellRenderer { private final Font normalFont; private final Font boldFont; diff --git a/core/src/net/sf/openrocket/gui/dialogs/EditDecalDialog.java b/core/src/net/sf/openrocket/gui/dialogs/EditDecalDialog.java new file mode 100644 index 000000000..59ee62ce7 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/EditDecalDialog.java @@ -0,0 +1,200 @@ +package net.sf.openrocket.gui.dialogs; + +import java.awt.Desktop; +import java.awt.Dialog; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextArea; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.startup.Application; + +public class EditDecalDialog extends JDialog { + + private static final Translator trans = Application.getTranslator(); + + private JRadioButton systemRadio; + private JRadioButton commandRadio; + private JTextArea commandText; + + private JCheckBox savePref; + + private boolean isCancel = false; + private boolean editOne = true; + + public EditDecalDialog(final Window owner, boolean promptForEditor, int usageCount) { + super(owner, trans.get("EditDecalDialog.title"), Dialog.ModalityType.APPLICATION_MODAL); + + JPanel panel = new JPanel(new MigLayout("fill, ins para")); + + if (promptForEditor) { + JLabel selectLbl = new JLabel(trans.get("EditDecalDialog.lbl.select")); + panel.add(selectLbl, "gapright, wrap"); + + ButtonGroup execGroup = new ButtonGroup(); + + if (Desktop.getDesktop().isSupported(Desktop.Action.EDIT)) { + + systemRadio = new JRadioButton(trans.get("EditDecalDialog.lbl.system")); + systemRadio.setSelected(true); + panel.add(systemRadio, "wrap"); + execGroup.add(systemRadio); + + commandRadio = new JRadioButton(trans.get("EditDecalDialog.lbl.cmdline")); + commandRadio.setSelected(false); + panel.add(commandRadio, "wrap"); + execGroup.add(commandRadio); + + commandText = new JTextArea(); + commandText.setEnabled(false); + panel.add(commandText, "growx, wrap"); + + final JButton chooser = new JButton(trans.get("EditDecalDialog.btn.chooser")); + chooser.setEnabled(false); + chooser.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser fc = new JFileChooser(); + int action = fc.showOpenDialog(owner); + if (action == JFileChooser.APPROVE_OPTION) { + commandText.setText(fc.getSelectedFile().getAbsolutePath()); + } + + } + + }); + panel.add(chooser, "growx, wrap"); + + + commandRadio.addChangeListener(new ChangeListener() { + + @Override + public void stateChanged(ChangeEvent e) { + boolean enabled = commandRadio.isSelected(); + commandText.setEnabled(enabled); + chooser.setEnabled(enabled); + } + + }); + + } else { + commandText = new JTextArea(); + commandText.setEnabled(false); + panel.add(commandText, "growx, wrap"); + + final JButton chooser = new JButton(trans.get("EditDecalDialog.btn.chooser")); + chooser.setEnabled(false); + chooser.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser fc = new JFileChooser(); + int action = fc.showOpenDialog(owner); + if (action == JFileChooser.APPROVE_OPTION) { + commandText.setText(fc.getSelectedFile().getAbsolutePath()); + } + + } + + }); + panel.add(chooser, "growx, wrap"); + + } + } + + if (usageCount > 1) { + ButtonGroup bg = new ButtonGroup(); + final JRadioButton justThisOne = new JRadioButton("just this one", true); + justThisOne.addChangeListener(new ChangeListener() { + + @Override + public void stateChanged(ChangeEvent e) { + EditDecalDialog.this.editOne = justThisOne.isSelected(); + } + + }); + panel.add(justThisOne, "left"); + bg.add(justThisOne); + JRadioButton all = new JRadioButton("all", false); + panel.add(all, "gapleft para, right, wrap"); + bg.add(all); + } + + if (promptForEditor) { + savePref = new JCheckBox(trans.get("EditDecalDialog.lbl.always")); + panel.add(savePref, "wrap"); + } + + // OK / Cancel buttons + JButton okButton = new JButton(trans.get("dlg.but.ok")); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ok(); + } + }); + panel.add(okButton, "tag ok, spanx, split"); + + //// Cancel button + JButton cancelButton = new JButton(trans.get("dlg.but.cancel")); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + close(); + } + }); + panel.add(cancelButton, "tag cancel"); + + this.add(panel); + + GUIUtil.rememberWindowSize(this); + GUIUtil.setDisposableDialogOptions(this, okButton); + + } + + public boolean isCancel() { + return isCancel; + } + + public boolean isSavePreferences() { + return savePref.isSelected(); + } + + public boolean isUseSystemEditor() { + return systemRadio != null && systemRadio.isSelected(); + } + + public String getCommandLine() { + return commandText.getText(); + } + + public boolean isEditOne() { + return editOne; + } + + public void ok() { + isCancel = false; + this.setVisible(false); + } + + public void close() { + isCancel = true; + this.setVisible(false); + } + +} diff --git a/core/src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java b/core/src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java deleted file mode 100644 index 3934ff93a..000000000 --- a/core/src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java +++ /dev/null @@ -1,521 +0,0 @@ -package net.sf.openrocket.gui.dialogs; - -import java.awt.Window; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.JTextField; -import javax.swing.ListSelectionModel; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; - -import net.miginfocom.swing.MigLayout; -import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.gui.dialogs.motor.MotorChooserDialog; -import net.sf.openrocket.gui.main.BasicFrame; -import net.sf.openrocket.gui.util.GUIUtil; -import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.rocketcomponent.MotorMount; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.Chars; - -public class EditMotorConfigurationDialog extends JDialog { - - private final Rocket rocket; - - private final MotorMount[] mounts; - - private final JTable configurationTable; - private final MotorConfigurationTableModel configurationTableModel; - - - private final JButton newConfButton, removeConfButton; - private final JButton selectMotorButton, removeMotorButton; - private final JTextField configurationNameField; - - - private String currentID = null; - private MotorMount currentMount = null; - - // Positive when user is modifying configuration name - private int configurationNameModification = 0; - private static final Translator trans = Application.getTranslator(); - - public EditMotorConfigurationDialog(final Rocket rocket, Window parent) { - //// Edit motor configurations - super(parent, trans.get("edtmotorconfdlg.title.Editmotorconf")); - - if (parent != null) - this.setModalityType(ModalityType.DOCUMENT_MODAL); - else - this.setModalityType(ModalityType.APPLICATION_MODAL); - - this.rocket = rocket; - - mounts = getPotentialMotorMounts(); - - - - JPanel panel = new JPanel(new MigLayout("fill, flowy")); - - - //// Motor mount selection - //// Motor mounts: - JLabel label = new JLabel(trans.get("edtmotorconfdlg.lbl.Motormounts")); - panel.add(label, "gapbottom para"); - - //// Select which components function as motor mounts: - label = new JLabel(trans.get("edtmotorconfdlg.selectcomp")); - panel.add(label, "ay 100%, w 1px, growx"); - - - JTable table = new JTable(new MotorMountTableModel()); - table.setTableHeader(null); - table.setShowVerticalLines(false); - table.setRowSelectionAllowed(false); - table.setColumnSelectionAllowed(false); - - TableColumnModel columnModel = table.getColumnModel(); - TableColumn col0 = columnModel.getColumn(0); - int w = table.getRowHeight() + 2; - col0.setMinWidth(w); - col0.setPreferredWidth(w); - col0.setMaxWidth(w); - - table.addMouseListener(new GUIUtil.BooleanTableClickListener(table)); - - JScrollPane scroll = new JScrollPane(table); - panel.add(scroll, "w 200lp, h 150lp, grow, wrap 20lp"); - - - - - - //// Motor selection - //// Motor configurations: - label = new JLabel(trans.get("edtmotorconfdlg.lbl.Motorconfig")); - panel.add(label, "spanx, gapbottom para"); - - //// Configuration name: - label = new JLabel(trans.get("edtmotorconfdlg.lbl.Configname")); - //// Leave name empty for default. - String tip = trans.get("edtmotorconfdlg.lbl.Leavenamedefault"); - label.setToolTipText(tip); - panel.add(label, ""); - - configurationNameField = new JTextField(10); - configurationNameField.setToolTipText(tip); - configurationNameField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void changedUpdate(DocumentEvent e) { - update(); - } - - @Override - public void insertUpdate(DocumentEvent e) { - update(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - update(); - } - - private void update() { - if (configurationNameModification != 0) - return; - - String text = configurationNameField.getText(); - if (currentID != null) { - configurationNameModification++; - rocket.setMotorConfigurationName(currentID, text); - int row = configurationTable.getSelectedRow(); - configurationTableModel.fireTableCellUpdated(row, 0); - updateEnabled(); - configurationNameModification--; - } - } - }); - panel.add(configurationNameField, "cell 2 1, gapright para"); - - //// New configuration - newConfButton = new JButton(trans.get("edtmotorconfdlg.but.Newconfiguration")); - newConfButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - String id = rocket.newMotorConfigurationID(); - rocket.getDefaultConfiguration().setMotorConfigurationID(id); - configurationTableModel.fireTableDataChanged(); - updateEnabled(); - } - }); - panel.add(newConfButton, "cell 3 1"); - - //// Remove configuration - removeConfButton = new JButton(trans.get("edtmotorconfdlg.but.Removeconfiguration")); - removeConfButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (currentID == null) - return; - rocket.removeMotorConfigurationID(currentID); - rocket.getDefaultConfiguration().setMotorConfigurationID(null); - configurationTableModel.fireTableDataChanged(); - updateEnabled(); - } - }); - panel.add(removeConfButton, "cell 4 1"); - - - - - configurationTableModel = new MotorConfigurationTableModel(); - configurationTable = new JTable(configurationTableModel); - configurationTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - configurationTable.setCellSelectionEnabled(true); - configurationTable.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - int column = configurationTable.getSelectedColumn(); - System.err.println("column=" + column); - if (column == 0 && configurationTable.getColumnCount() > 1) { - configurationTable.setColumnSelectionInterval(1, 1); - } - } - }); - - configurationTable.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - - if (e.getClickCount() == 1) { - - // Single click updates selection - updateEnabled(); - - } else if (e.getClickCount() == 2) { - - // Double-click edits motor - selectMotor(); - - } - - } - }); - - - scroll = new JScrollPane(configurationTable); - panel.add(scroll, "cell 1 2, spanx, w 500lp, h 150lp, grow"); - - //// Select motor - selectMotorButton = new JButton(trans.get("edtmotorconfdlg.but.Selectmotor")); - selectMotorButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - selectMotor(); - } - }); - panel.add(selectMotorButton, "spanx, flowx, split 2, ax 50%"); - - //// Remove motor button - removeMotorButton = new JButton(trans.get("edtmotorconfdlg.but.removemotor")); - removeMotorButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - removeMotor(); - } - }); - panel.add(removeMotorButton, "ax 50%"); - - - - //// Close button - JButton close = new JButton(trans.get("dlg.but.close")); - close.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - EditMotorConfigurationDialog.this.dispose(); - } - }); - panel.add(close, "spanx, right"); - - this.add(panel); - this.validate(); - this.pack(); - - updateEnabled(); - - this.setLocationByPlatform(true); - GUIUtil.setDisposableDialogOptions(this, close); - - // Undo description - final OpenRocketDocument document = BasicFrame.findDocument(rocket); - if (document != null) { - //// Edit motor configurations - document.startUndo(trans.get("edtmotorconfdlg.title.Editmotorconf")); - this.addWindowListener(new WindowAdapter() { - @Override - public void windowClosed(WindowEvent e) { - document.stopUndo(); - } - }); - } - } - - - private MotorMount[] getPotentialMotorMounts() { - List list = new ArrayList(); - for (RocketComponent c : rocket) { - if (c instanceof MotorMount) { - list.add((MotorMount) c); - } - } - return list.toArray(new MotorMount[0]); - } - - - - - private void updateEnabled() { - int column = configurationTable.getSelectedColumn(); - int row = configurationTable.getSelectedRow(); - - if (column < 0 || row < 0) { - currentID = null; - currentMount = null; - } else { - - currentID = findID(row); - if (column == 0) { - currentMount = null; - } else { - currentMount = findMount(column); - } - rocket.getDefaultConfiguration().setMotorConfigurationID(currentID); - - } - - if (configurationNameModification == 0) { - // Don't update name field when user is modifying it - configurationNameModification++; - - configurationNameField.setEnabled(currentID != null); - if (currentID == null) { - configurationNameField.setText(""); - } else { - configurationNameField.setText(rocket.getMotorConfigurationName(currentID)); - } - - configurationNameModification--; - } - removeConfButton.setEnabled(currentID != null); - selectMotorButton.setEnabled(currentMount != null && currentID != null); - removeMotorButton.setEnabled(currentMount != null && currentID != null); - } - - - - - private void selectMotor() { - if (currentID == null || currentMount == null) - return; - - MotorChooserDialog dialog = new MotorChooserDialog(currentMount.getMotor(currentID), - currentMount.getMotorDelay(currentID), currentMount.getMotorMountDiameter(), this); - dialog.setVisible(true); - Motor m = dialog.getSelectedMotor(); - double d = dialog.getSelectedDelay(); - - if (m != null) { - currentMount.setMotor(currentID, m); - currentMount.setMotorDelay(currentID, d); - } - - int row = configurationTable.getSelectedRow(); - configurationTableModel.fireTableRowsUpdated(row, row); - updateEnabled(); - } - - - private void removeMotor() { - if (currentID == null || currentMount == null) - return; - - currentMount.setMotor(currentID, null); - - int row = configurationTable.getSelectedRow(); - configurationTableModel.fireTableRowsUpdated(row, row); - updateEnabled(); - } - - - private String findID(int row) { - return rocket.getMotorConfigurationIDs()[row + 1]; - } - - - private MotorMount findMount(int column) { - MotorMount mount = null; - - int count = column; - for (MotorMount m : mounts) { - if (m.isMotorMount()) - count--; - if (count <= 0) { - mount = m; - break; - } - } - - if (mount == null) { - throw new IndexOutOfBoundsException("motor mount not found, column=" + column); - } - return mount; - } - - - /** - * The table model for selecting whether components are motor mounts or not. - */ - private class MotorMountTableModel extends AbstractTableModel { - - @Override - public int getColumnCount() { - return 2; - } - - @Override - public int getRowCount() { - return mounts.length; - } - - @Override - public Class getColumnClass(int column) { - switch (column) { - case 0: - return Boolean.class; - - case 1: - return String.class; - - default: - throw new IndexOutOfBoundsException("column=" + column); - } - } - - @Override - public Object getValueAt(int row, int column) { - switch (column) { - case 0: - return new Boolean(mounts[row].isMotorMount()); - - case 1: - return mounts[row].toString(); - - default: - throw new IndexOutOfBoundsException("column=" + column); - } - } - - @Override - public boolean isCellEditable(int row, int column) { - return column == 0; - } - - @Override - public void setValueAt(Object value, int row, int column) { - if (column != 0 || !(value instanceof Boolean)) { - throw new IllegalArgumentException("column=" + column + ", value=" + value); - } - - mounts[row].setMotorMount((Boolean) value); - configurationTableModel.fireTableStructureChanged(); - updateEnabled(); - } - } - - - - /** - * The table model for selecting and editing the motor configurations. - */ - private class MotorConfigurationTableModel extends AbstractTableModel { - - @Override - public int getColumnCount() { - int count = 1; - for (MotorMount m : mounts) { - if (m.isMotorMount()) - count++; - } - return count; - } - - @Override - public int getRowCount() { - return rocket.getMotorConfigurationIDs().length - 1; - } - - @Override - public Object getValueAt(int row, int column) { - - String id = findID(row); - - if (column == 0) { - return rocket.getMotorConfigurationNameOrDescription(id); - } - - MotorMount mount = findMount(column); - Motor motor = mount.getMotor(id); - if (motor == null) - //// None - return "None"; - - String str = motor.getDesignation(mount.getMotorDelay(id)); - int count = mount.getMotorCount(); - if (count > 1) { - str = "" + count + Chars.TIMES + " " + str; - } - return str; - } - - - @Override - public String getColumnName(int column) { - if (column == 0) { - //// Configuration name - return trans.get("edtmotorconfdlg.lbl.Configname"); - } - - MotorMount mount = findMount(column); - String name = mount.toString(); - int count = mount.getMotorCount(); - if (count > 1) { - name = name + " (" + Chars.TIMES + count + ")"; - } - return name; - } - - } - -} diff --git a/core/src/net/sf/openrocket/gui/dialogs/ExampleDesignDialog.java b/core/src/net/sf/openrocket/gui/dialogs/ExampleDesignDialog.java deleted file mode 100644 index 95ad56dfe..000000000 --- a/core/src/net/sf/openrocket/gui/dialogs/ExampleDesignDialog.java +++ /dev/null @@ -1,266 +0,0 @@ -package net.sf.openrocket.gui.dialogs; - -import java.awt.Dialog; -import java.awt.Window; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.ListSelectionModel; - -import net.miginfocom.swing.MigLayout; -import net.sf.openrocket.gui.util.GUIUtil; -import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.BugException; -import net.sf.openrocket.util.JarUtil; - -public class ExampleDesignDialog extends JDialog { - - private static final String DIRECTORY = "datafiles/examples/"; - private static final String PATTERN = ".*\\.[oO][rR][kK]$"; - private static final Translator trans = Application.getTranslator(); - - private static final FilenameFilter FILTER = new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.matches(PATTERN); - } - }; - - - private boolean open = false; - private final JList designSelection; - - private ExampleDesignDialog(ExampleDesign[] designs, Window parent) { - //// Open example design - super(parent, trans.get("exdesigndlg.lbl.Openexampledesign"), Dialog.ModalityType.APPLICATION_MODAL); - - JPanel panel = new JPanel(new MigLayout("fill")); - - //// Select example designs to open: - panel.add(new JLabel(trans.get("exdesigndlg.lbl.Selectexample")), "wrap"); - - designSelection = new JList(designs); - designSelection.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); - designSelection.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() >= 2) { - open = true; - ExampleDesignDialog.this.setVisible(false); - } - } - }); - panel.add(new JScrollPane(designSelection), "grow, wmin 300lp, wrap para"); - - //// Open button - JButton openButton = new JButton(trans.get("exdesigndlg.but.open")); - openButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - open = true; - ExampleDesignDialog.this.setVisible(false); - } - }); - panel.add(openButton, "split 2, sizegroup buttons, growx"); - - //// Cancel button - JButton cancelButton = new JButton(trans.get("dlg.but.cancel")); - cancelButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - open = false; - ExampleDesignDialog.this.setVisible(false); - } - }); - panel.add(cancelButton, "sizegroup buttons, growx"); - - this.add(panel); - this.pack(); - this.setLocationByPlatform(true); - - GUIUtil.setDisposableDialogOptions(this, openButton); - } - - - /** - * Open a dialog to allow opening the example designs. - * - * @param parent the parent window of the dialog. - * @return an array of URL's to open, or null if the operation - * was cancelled. - */ - public static URL[] selectExampleDesigns(Window parent) { - - ExampleDesign[] designs; - - designs = getJarFileNames(); - if (designs == null || designs.length == 0) { - designs = getDirFileNames(); - } - if (designs == null || designs.length == 0) { - //// Example designs could not be found. - JOptionPane.showMessageDialog(parent, trans.get("exdesigndlg.lbl.Exampledesignsnotfound"), - //// Examples not found - trans.get("exdesigndlg.lbl.Examplesnotfound"), JOptionPane.ERROR_MESSAGE); - return null; - } - - Arrays.sort(designs); - - ExampleDesignDialog dialog = new ExampleDesignDialog(designs, parent); - dialog.setVisible(true); - - if (!dialog.open) { - return null; - } - - Object[] selected = dialog.designSelection.getSelectedValues(); - URL[] urls = new URL[selected.length]; - for (int i=0; i list = new ArrayList(); - int dirLength = DIRECTORY.length(); - - // Find and open the jar file this class is contained in - File file = JarUtil.getCurrentJarFile(); - if (file == null) - return null; - - - // Generate URL pointing to JAR file - URL fileUrl; - try { - fileUrl = file.toURI().toURL(); - } catch (MalformedURLException e1) { - e1.printStackTrace(); - throw new BugException(e1); - } - - // Iterate over JAR entries searching for designs - JarFile jarFile = null; - try { - jarFile = new JarFile(file); - - // Loop through JAR entries searching for files to load - Enumeration entries = jarFile.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - String name = entry.getName(); - if (name.startsWith(DIRECTORY) && FILTER.accept(null, name)) { - String urlName = "jar:" + fileUrl + "!/" + name; - URL url = new URL(urlName); - list.add(new ExampleDesign(url, - name.substring(dirLength, name.length()-4))); - } - } - - } catch (IOException e) { - // Could be normal condition if not package in JAR - return null; - } finally { - if (jarFile != null) { - try { - jarFile.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - return list.toArray(new ExampleDesign[0]); - } - - - - /** - * Data holder class. - */ - private static class ExampleDesign implements Comparable { - - private final URL url; - private final String name; - - public ExampleDesign(URL url, String name) { - this.url = url; - this.name = name; - } - - @Override - public String toString() { - return name; - } - - public URL getURL() { - return url; - } - - @Override - public int compareTo(ExampleDesign o) { - return this.name.compareTo(o.name); - } - } - -} diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/DeploymentSelectionDialog.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/DeploymentSelectionDialog.java new file mode 100644 index 000000000..87bfe2a76 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/DeploymentSelectionDialog.java @@ -0,0 +1,155 @@ +package net.sf.openrocket.gui.dialogs.flightconfiguration; + +import java.awt.Dialog; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JSlider; +import javax.swing.JSpinner; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.formatting.RocketDescriptor; +import net.sf.openrocket.gui.SpinnerEditor; +import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.adaptors.EnumModel; +import net.sf.openrocket.gui.components.BasicSlider; +import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; + +public class DeploymentSelectionDialog extends JDialog { + + private static final Translator trans = Application.getTranslator(); + + private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); + + private final DeploymentConfiguration newConfiguration; + + private final JLabel altText; + private final JSpinner altSpinner; + private final UnitSelector altUnit; + private final JSlider altSlider; + + DeploymentSelectionDialog(JDialog parent, final Rocket rocket, final RecoveryDevice component) { + super(parent, trans.get("edtmotorconfdlg.title.Selectdeploymentconf"), Dialog.ModalityType.APPLICATION_MODAL); + + final String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + + newConfiguration = component.getDeploymentConfiguration().get(id).clone(); + + JPanel panel = new JPanel(new MigLayout("fill")); + + panel.add(new JLabel(trans.get("DeploymentSelectionDialog.opt.title")), "span, wrap rel"); + final JRadioButton defaultButton = new JRadioButton(trans.get("DeploymentSelectionDialog.opt.default"), true); + panel.add(defaultButton, "span, gapleft para, wrap rel"); + String str = trans.get("DeploymentSelectionDialog.opt.override"); + str = str.replace("{0}", descriptor.format(rocket, id)); + final JRadioButton overrideButton = new JRadioButton(str, false); + panel.add(overrideButton, "span, gapleft para, wrap para"); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(defaultButton); + buttonGroup.add(overrideButton); + + // Select the button based on current configuration. If the configuration is overridden + // The the overrideButton is selected. + boolean isOverridden = !component.getDeploymentConfiguration().isDefault(id); + if (isOverridden) { + overrideButton.setSelected(true); + } + + //// Deployment + //// Deploys at: + panel.add(new JLabel(trans.get("ParachuteCfg.lbl.Deploysat")), ""); + + final JComboBox event = new JComboBox(new EnumModel(newConfiguration, "DeployEvent")); + panel.add(event, "spanx 3, growx, wrap"); + + // ... and delay + //// plus + panel.add(new JLabel(trans.get("ParachuteCfg.lbl.plusdelay")), "right"); + + final DoubleModel delay = new DoubleModel(newConfiguration, "DeployDelay", UnitGroup.UNITS_SHORT_TIME, 0); + final JSpinner delaySpinner = new JSpinner(delay.getSpinnerModel()); + delaySpinner.setEditor(new SpinnerEditor(delaySpinner, 3)); + panel.add(delaySpinner, "spanx, split"); + + //// seconds + panel.add(new JLabel(trans.get("ParachuteCfg.lbl.seconds")), "wrap paragraph"); + + // Altitude: + altText = new JLabel(trans.get("ParachuteCfg.lbl.Altitude")); + panel.add(altText); + + final DoubleModel alt = new DoubleModel(newConfiguration, "DeployAltitude", UnitGroup.UNITS_DISTANCE, 0); + + altSpinner = new JSpinner(alt.getSpinnerModel()); + altSpinner.setEditor(new SpinnerEditor(altSpinner)); + panel.add(altSpinner, "growx"); + altUnit = new UnitSelector(alt); + panel.add(altUnit, "growx"); + altSlider = new BasicSlider(alt.getSliderModel(100, 1000)); + panel.add(altSlider, "w 100lp, wrap"); + + event.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + updateState(); + } + }); + updateState(); + + panel.add(new JPanel(), "span, split, growx"); + + JButton okButton = new JButton(trans.get("button.ok")); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (defaultButton.isSelected()) { + component.getDeploymentConfiguration().setDefault(newConfiguration); + } else { + component.getDeploymentConfiguration().set(id, newConfiguration); + } + DeploymentSelectionDialog.this.setVisible(false); + } + }); + + panel.add(okButton, "sizegroup btn"); + + JButton cancel = new JButton(trans.get("button.cancel")); + cancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + DeploymentSelectionDialog.this.setVisible(false); + } + }); + + panel.add(cancel, "sizegroup btn"); + + this.setContentPane(panel); + GUIUtil.setDisposableDialogOptions(this, okButton); + } + + private void updateState() { + boolean enabled = (newConfiguration.getDeployEvent() == DeployEvent.ALTITUDE); + altText.setEnabled(enabled); + altSpinner.setEnabled(enabled); + altUnit.setEnabled(enabled); + altSlider.setEnabled(enabled); + } + + +} diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationDialog.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationDialog.java new file mode 100644 index 000000000..9a4d98d69 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationDialog.java @@ -0,0 +1,213 @@ +package net.sf.openrocket.gui.dialogs.flightconfiguration; + +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.gui.adaptors.FlightConfigurationModel; +import net.sf.openrocket.gui.main.BasicFrame; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.FlightConfigurableComponent; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; + +/** + * Dialog for configuring all flight-configuration specific properties. + * Content of individual tabs are in separate classes. + */ +public class FlightConfigurationDialog extends JDialog { + + private static final Translator trans = Application.getTranslator(); + + private final Rocket rocket; + + private FlightConfigurationModel flightConfigurationModel; + + private final JButton renameConfButton, removeConfButton, copyConfButton; + + private final MotorConfigurationPanel motorConfigurationPanel; + private final RecoveryConfigurationPanel recoveryConfigurationPanel; + private final SeparationConfigurationPanel separationConfigurationPanel; + + + public FlightConfigurationDialog(final Rocket rocket, Window parent) { + //// Edit motor configurations + super(parent, trans.get("edtmotorconfdlg.title.Editmotorconf"), ModalityType.APPLICATION_MODAL); + + if (parent != null) + this.setModalityType(ModalityType.DOCUMENT_MODAL); + else + this.setModalityType(ModalityType.APPLICATION_MODAL); + + this.rocket = rocket; + + JPanel panel = new JPanel(new MigLayout("fill")); + + JLabel label = new JLabel(trans.get("edtmotorconfdlg.lbl.Selectedconf")); + panel.add(label, "span, split"); + + flightConfigurationModel = new FlightConfigurationModel(rocket.getDefaultConfiguration(), false); + JComboBox configSelector = new JComboBox(flightConfigurationModel); + configSelector.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + configurationChanged(); + } + }); + + panel.add(configSelector, "growx, gapright para"); + + JButton newConfButton = new JButton(trans.get("edtmotorconfdlg.but.Newconfiguration")); + newConfButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + addConfiguration(); + } + + }); + + panel.add(newConfButton); + + renameConfButton = new JButton(trans.get("edtmotorconfdlg.but.Renameconfiguration")); + renameConfButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + renameConfiguration(); + } + }); + panel.add(renameConfButton); + + removeConfButton = new JButton(trans.get("edtmotorconfdlg.but.Removeconfiguration")); + removeConfButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + removeConfiguration(); + } + }); + panel.add(removeConfButton); + + copyConfButton = new JButton(trans.get("edtmotorconfdlg.but.Copyconfiguration")); + copyConfButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + copyConfiguration(); + } + }); + panel.add(copyConfButton, "wrap para"); + + + //// Tabs for advanced view. + JTabbedPane tabs = new JTabbedPane(); + panel.add(tabs, "grow, spanx, w 700lp, h 500lp, wrap"); + + //// Motor tabs + motorConfigurationPanel = new MotorConfigurationPanel(this, rocket); + tabs.add(trans.get("edtmotorconfdlg.lbl.Motortab"), motorConfigurationPanel); + //// Recovery tab + recoveryConfigurationPanel = new RecoveryConfigurationPanel(this, rocket); + tabs.add(trans.get("edtmotorconfdlg.lbl.Recoverytab"), recoveryConfigurationPanel); + + //// Stage tab + separationConfigurationPanel = new SeparationConfigurationPanel(this, rocket); + if (rocket.getStageCount() > 1) { + tabs.add(trans.get("edtmotorconfdlg.lbl.Stagetab"), separationConfigurationPanel); + } + + + //// Close button + JButton close = new JButton(trans.get("dlg.but.close")); + close.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + FlightConfigurationDialog.this.dispose(); + } + }); + panel.add(close, "spanx, right"); + + this.add(panel); + this.validate(); + this.pack(); + + updateButtonState(); + + this.setLocationByPlatform(true); + GUIUtil.setDisposableDialogOptions(this, close); + + // Undo description + final OpenRocketDocument document = BasicFrame.findDocument(rocket); + if (document != null) { + //// Edit motor configurations + document.startUndo(trans.get("edtmotorconfdlg.title.Editmotorconf")); + this.addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent e) { + document.stopUndo(); + } + }); + } + } + + private void configurationChanged() { + motorConfigurationPanel.fireTableDataChanged(); + recoveryConfigurationPanel.fireTableDataChanged(); + separationConfigurationPanel.fireTableDataChanged(); + updateButtonState(); + } + + private void addConfiguration() { + String newId = rocket.newFlightConfigurationID(); + rocket.getDefaultConfiguration().setFlightConfigurationID(newId); + configurationChanged(); + } + + private void copyConfiguration() { + String currentId = rocket.getDefaultConfiguration().getFlightConfigurationID(); + + // currentID is the currently selected configuration. + String newConfigId = rocket.newFlightConfigurationID(); + String oldName = rocket.getFlightConfigurationName(currentId); + + for (RocketComponent c : rocket) { + if (c instanceof FlightConfigurableComponent) { + ((FlightConfigurableComponent) c).cloneFlightConfiguration(currentId, newConfigId); + } + } + rocket.setFlightConfigurationName(currentId, oldName); + rocket.getDefaultConfiguration().setFlightConfigurationID(newConfigId); + + configurationChanged(); + } + + private void renameConfiguration() { + new RenameConfigDialog(this, rocket).setVisible(true); + } + + private void removeConfiguration() { + String currentId = rocket.getDefaultConfiguration().getFlightConfigurationID(); + if (currentId == null) + return; + rocket.removeFlightConfigurationID(currentId); + rocket.getDefaultConfiguration().setFlightConfigurationID(null); + configurationChanged(); + } + + private void updateButtonState() { + String currentId = rocket.getDefaultConfiguration().getFlightConfigurationID(); + removeConfButton.setEnabled(currentId != null); + renameConfButton.setEnabled(currentId != null); + } + +} diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/IgnitionSelectionDialog.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/IgnitionSelectionDialog.java new file mode 100644 index 000000000..f4e8cd4f6 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/IgnitionSelectionDialog.java @@ -0,0 +1,120 @@ +package net.sf.openrocket.gui.dialogs.flightconfiguration; + +import java.awt.Dialog; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JSpinner; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.formatting.RocketDescriptor; +import net.sf.openrocket.gui.SpinnerEditor; +import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.adaptors.EnumModel; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration.IgnitionEvent; +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; + +public class IgnitionSelectionDialog extends JDialog { + + private static final Translator trans = Application.getTranslator(); + + private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); + + + private IgnitionConfiguration newConfiguration; + + public IgnitionSelectionDialog(JDialog parent, final Rocket rocket, final MotorMount component) { + super(parent, trans.get("edtmotorconfdlg.title.Selectignitionconf"), Dialog.ModalityType.APPLICATION_MODAL); + final String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + + newConfiguration = component.getIgnitionConfiguration().get(id).clone(); + + JPanel panel = new JPanel(new MigLayout("fill")); + + // Edit default or override option + boolean isDefault = component.getIgnitionConfiguration().isDefault(id); + panel.add(new JLabel(trans.get("IgnitionSelectionDialog.opt.title")), "span, wrap rel"); + final JRadioButton defaultButton = new JRadioButton(trans.get("IgnitionSelectionDialog.opt.default"), isDefault); + panel.add(defaultButton, "span, gapleft para, wrap rel"); + String str = trans.get("IgnitionSelectionDialog.opt.override"); + str = str.replace("{0}", descriptor.format(rocket, id)); + final JRadioButton overrideButton = new JRadioButton(str, !isDefault); + panel.add(overrideButton, "span, gapleft para, wrap para"); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(defaultButton); + buttonGroup.add(overrideButton); + + // Select the button based on current configuration. If the configuration is overridden + // The the overrideButton is selected. + boolean isOverridden = !component.getIgnitionConfiguration().isDefault(id); + if (isOverridden) { + overrideButton.setSelected(true); + } + + // Select ignition event + //// Ignition at: + panel.add(new JLabel(trans.get("MotorCfg.lbl.Ignitionat")), ""); + + final JComboBox event = new JComboBox(new EnumModel(newConfiguration, "IgnitionEvent")); + panel.add(event, "growx, wrap"); + + // ... and delay + //// plus + panel.add(new JLabel(trans.get("MotorCfg.lbl.plus")), "gap indent, skip 1, span, split"); + + DoubleModel delay = new DoubleModel(newConfiguration, "IgnitionDelay", UnitGroup.UNITS_SHORT_TIME, 0); + JSpinner spin = new JSpinner(delay.getSpinnerModel()); + spin.setEditor(new SpinnerEditor(spin, 3)); + panel.add(spin, "gap rel rel"); + + //// seconds + panel.add(new JLabel(trans.get("MotorCfg.lbl.seconds")), "wrap unrel"); + + + panel.add(new JPanel(), "span, split, growx"); + + JButton okButton = new JButton(trans.get("button.ok")); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (defaultButton.isSelected()) { + component.getIgnitionConfiguration().setDefault(newConfiguration); + } else { + component.getIgnitionConfiguration().set(id, newConfiguration); + } + IgnitionSelectionDialog.this.setVisible(false); + } + }); + + panel.add(okButton, "sizegroup btn"); + + + JButton cancel = new JButton(trans.get("button.cancel")); + cancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + IgnitionSelectionDialog.this.setVisible(false); + } + }); + + panel.add(cancel, "sizegroup btn"); + + this.setContentPane(panel); + + GUIUtil.setDisposableDialogOptions(this, okButton); + } +} diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorConfigurationPanel.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorConfigurationPanel.java new file mode 100644 index 000000000..811169dd0 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorConfigurationPanel.java @@ -0,0 +1,318 @@ +package net.sf.openrocket.gui.dialogs.flightconfiguration; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.gui.components.DescriptionArea; +import net.sf.openrocket.gui.components.StyledLabel; +import net.sf.openrocket.gui.components.StyledLabel.Style; +import net.sf.openrocket.gui.dialogs.motor.MotorChooserDialog; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.MotorConfiguration; +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; + +public class MotorConfigurationPanel extends JPanel { + + private static final Translator trans = Application.getTranslator(); + + private final FlightConfigurationDialog flightConfigurationDialog; + private final Rocket rocket; + + private final JTable configurationTable; + private final MotorConfigurationTableModel configurationTableModel; + private final JButton selectMotorButton, removeMotorButton, selectIgnitionButton, resetIgnitionButton; + + + MotorConfigurationPanel(FlightConfigurationDialog flightConfigurationDialog, Rocket rocket) { + super(new MigLayout("fill")); + this.flightConfigurationDialog = flightConfigurationDialog; + this.rocket = rocket; + + DescriptionArea desc = new DescriptionArea(trans.get("description"), 3, -1); + this.add(desc, "spanx, growx, wrap para"); + + + //// Motor mount selection + JLabel label = new StyledLabel(trans.get("lbl.motorMounts"), Style.BOLD); + this.add(label, ""); + + //// Motor selection + label = new StyledLabel(trans.get("lbl.motorConfiguration"), Style.BOLD); + this.add(label, "wrap rel"); + + + //// Motor Mount selection + JTable table = new JTable(new MotorMountTableModel(this, rocket)); + table.setTableHeader(null); + table.setShowVerticalLines(false); + table.setRowSelectionAllowed(false); + table.setColumnSelectionAllowed(false); + + TableColumnModel columnModel = table.getColumnModel(); + TableColumn col0 = columnModel.getColumn(0); + int w = table.getRowHeight() + 2; + col0.setMinWidth(w); + col0.setPreferredWidth(w); + col0.setMaxWidth(w); + + table.addMouseListener(new GUIUtil.BooleanTableClickListener(table)); + JScrollPane scroll = new JScrollPane(table); + this.add(scroll, "w 200lp, h 150lp, grow"); + + + //// Motor selection table. + configurationTableModel = new MotorConfigurationTableModel(rocket); + configurationTable = new JTable(configurationTableModel); + configurationTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + configurationTable.setRowSelectionAllowed(true); + configurationTable.setDefaultRenderer(Object.class, new MotorTableCellRenderer()); + + configurationTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + updateButtonState(); + int selectedColumn = configurationTable.getSelectedColumn(); + if (e.getClickCount() == 2) { + if (selectedColumn == 2) { + // user double clicked in ignition column + selectIgnition(); + } else { + // Double-click edits motor + selectMotor(); + } + } + } + }); + + scroll = new JScrollPane(configurationTable); + this.add(scroll, "w 500lp, h 150lp, grow, wrap"); + + //// Select motor + selectMotorButton = new JButton(trans.get("MotorConfigurationPanel.btn.selectMotor")); + selectMotorButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectMotor(); + } + }); + this.add(selectMotorButton, "skip, split, sizegroup button"); + + //// Remove motor button + removeMotorButton = new JButton(trans.get("MotorConfigurationPanel.btn.removeMotor")); + removeMotorButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + removeMotor(); + } + }); + this.add(removeMotorButton, "sizegroup button"); + + //// Select Ignition button + selectIgnitionButton = new JButton(trans.get("MotorConfigurationPanel.btn.selectIgnition")); + selectIgnitionButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectIgnition(); + } + }); + this.add(selectIgnitionButton, "sizegroup button"); + + //// Reset Ignition button + resetIgnitionButton = new JButton(trans.get("MotorConfigurationPanel.btn.resetIgnition")); + resetIgnitionButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + resetIgnition(); + } + }); + this.add(resetIgnitionButton, "sizegroup button, wrap"); + + } + + public void fireTableDataChanged() { + int selected = configurationTable.getSelectedRow(); + configurationTableModel.fireTableDataChanged(); + if (selected >= 0) { + selected = Math.min(selected, configurationTable.getRowCount() - 1); + configurationTable.getSelectionModel().setSelectionInterval(selected, selected); + } + updateButtonState(); + } + + private void updateButtonState() { + String currentID = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount currentMount = getCurrentMount(); + selectMotorButton.setEnabled(currentMount != null && currentID != null); + removeMotorButton.setEnabled(currentMount != null && currentID != null); + selectIgnitionButton.setEnabled(currentMount != null && currentID != null); + resetIgnitionButton.setEnabled(currentMount != null && currentID != null); + } + + + private MotorMount getCurrentMount() { + int row = configurationTable.getSelectedRow(); + if (row < 0) { + return null; + } + + return getMount(row); + } + + + private MotorMount getMount(int row) { + int count = 0; + for (RocketComponent c : rocket) { + if (c instanceof MotorMount) { + MotorMount mount = (MotorMount) c; + if (mount.isMotorMount()) { + count++; + } + if (count > row) { + return mount; + } + } + } + + throw new IndexOutOfBoundsException("Invalid row, row=" + row + " count=" + count); + } + + + + private void selectMotor() { + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount mount = getCurrentMount(); + if (id == null || mount == null) + return; + + MotorConfiguration config = mount.getMotorConfiguration().get(id); + + MotorChooserDialog dialog = new MotorChooserDialog( + config.getMotor(), + config.getEjectionDelay(), + mount.getMotorMountDiameter(), + flightConfigurationDialog); + dialog.setVisible(true); + Motor m = dialog.getSelectedMotor(); + double d = dialog.getSelectedDelay(); + + if (m != null) { + config = new MotorConfiguration(); + config.setMotor(m); + config.setEjectionDelay(d); + mount.getMotorConfiguration().set(id, config); + } + + fireTableDataChanged(); + } + + private void removeMotor() { + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount mount = getCurrentMount(); + if (id == null || mount == null) + return; + + mount.getMotorConfiguration().resetDefault(id); + + fireTableDataChanged(); + } + + private void selectIgnition() { + String currentID = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount currentMount = getCurrentMount(); + if (currentID == null || currentMount == null) + return; + + IgnitionSelectionDialog dialog = new IgnitionSelectionDialog( + this.flightConfigurationDialog, + rocket, + currentMount); + dialog.setVisible(true); + + fireTableDataChanged(); + } + + + private void resetIgnition() { + String currentID = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount currentMount = getCurrentMount(); + if (currentID == null || currentMount == null) + return; + + currentMount.getIgnitionConfiguration().resetDefault(currentID); + + fireTableDataChanged(); + } + + + private class MotorTableCellRenderer extends DefaultTableCellRenderer { + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (!(c instanceof JLabel)) { + return c; + } + JLabel label = (JLabel) c; + + MotorMount mount = getMount(row); + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + + switch (column) { + case 0: + regular(label); + break; + + case 1: + if (mount.getMotorConfiguration().get(id).getMotor() != null) { + regular(label); + } else { + shaded(label); + } + break; + + case 2: + if (mount.getIgnitionConfiguration().isDefault(id)) { + shaded(label); + } else { + regular(label); + } + break; + } + + return label; + } + + private void shaded(JLabel label) { + GUIUtil.changeFontStyle(label, Font.ITALIC); + label.setForeground(Color.GRAY); + } + + private void regular(JLabel label) { + GUIUtil.changeFontStyle(label, Font.PLAIN); + label.setForeground(Color.BLACK); + } + + } + +} diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorConfigurationTableModel.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorConfigurationTableModel.java new file mode 100644 index 000000000..ce900e72f --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorConfigurationTableModel.java @@ -0,0 +1,146 @@ +package net.sf.openrocket.gui.dialogs.flightconfiguration; + +import javax.swing.table.AbstractTableModel; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; +import net.sf.openrocket.rocketcomponent.MotorConfiguration; +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.Chars; +import net.sf.openrocket.util.Coordinate; + +/** + * The table model for selecting and editing the motor configurations. + */ +class MotorConfigurationTableModel extends AbstractTableModel { + + private static final Translator trans = Application.getTranslator(); + + private static final String NONE = trans.get("edtmotorconfdlg.tbl.None"); + private static final String MOTOR_MOUNT = trans.get("edtmotorconfdlg.tbl.Mountheader"); + private static final String MOTOR = trans.get("edtmotorconfdlg.tbl.Motorheader"); + private static final String IGNITION = trans.get("edtmotorconfdlg.tbl.Ignitionheader"); + + private final Rocket rocket; + + + public MotorConfigurationTableModel(Rocket rocket) { + this.rocket = rocket; + } + + @Override + public int getColumnCount() { + return 3; + } + + @Override + public int getRowCount() { + int count = 0; + for (RocketComponent c : rocket) { + if (c instanceof MotorMount && ((MotorMount) c).isMotorMount()) { + count++; + } + } + return count; + } + + @Override + public Object getValueAt(int row, int column) { + switch (column) { + case 0: { + MotorMount mount = findMount(row); + String name = mount.toString(); + int count = getMountMultiplicity(mount); + if (count > 1) { + name = name + " (" + Chars.TIMES + count + ")"; + } + return name; + } + case 1: { + MotorMount mount = findMount(row); + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorConfiguration config = mount.getMotorConfiguration().get(id); + Motor motor = config.getMotor(); + + if (motor == null) + return NONE; + + String str = motor.getDesignation(config.getEjectionDelay()); + int count = getMountMultiplicity(mount); + if (count > 1) { + str = "" + count + Chars.TIMES + " " + str; + } + return str; + } + case 2: { + return getIgnitionEventString(row); + + } + default: + throw new IndexOutOfBoundsException("column=" + column); + } + } + + + + @Override + public String getColumnName(int column) { + switch (column) { + case 0: + return MOTOR_MOUNT; + case 1: + return MOTOR; + case 2: + return IGNITION; + default: + throw new IndexOutOfBoundsException("column=" + column); + } + } + + + private MotorMount findMount(int row) { + int count = row; + for (RocketComponent c : rocket) { + if (c instanceof MotorMount && ((MotorMount) c).isMotorMount()) { + count--; + if (count < 0) { + return (MotorMount) c; + } + } + } + throw new IndexOutOfBoundsException("Requesting row=" + row + " but only " + getRowCount() + " rows exist"); + } + + + private int getMountMultiplicity(MotorMount mount) { + RocketComponent c = (RocketComponent) mount; + return c.toAbsolute(Coordinate.NUL).length; + } + + + + private String getIgnitionEventString(int row) { + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount mount = findMount(row); + IgnitionConfiguration ignitionConfig = mount.getIgnitionConfiguration().get(id); + + IgnitionConfiguration.IgnitionEvent ignitionEvent = ignitionConfig.getIgnitionEvent(); + Double ignitionDelay = ignitionConfig.getIgnitionDelay(); + boolean isDefault = mount.getIgnitionConfiguration().isDefault(id); + + String str = trans.get("MotorMount.IgnitionEvent.short." + ignitionEvent.name()); + if (ignitionDelay > 0.001) { + str = str + " + " + UnitGroup.UNITS_SHORT_TIME.toStringUnit(ignitionDelay); + } + if (isDefault) { + String def = trans.get("table.ignition.default"); + str = def.replace("{0}", str); + } + return str; + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorMountTableModel.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorMountTableModel.java new file mode 100644 index 000000000..29b1fab64 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorMountTableModel.java @@ -0,0 +1,87 @@ +package net.sf.openrocket.gui.dialogs.flightconfiguration; + +import java.util.List; + +import javax.swing.table.AbstractTableModel; + +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.ArrayList; + +/** + * The table model for selecting whether components are motor mounts or not. + */ +class MotorMountTableModel extends AbstractTableModel { + + private final MotorConfigurationPanel motorConfigurationPanel; + + private final List potentialMounts = new ArrayList(); + + /** + * @param motorConfigurationPanel + */ + MotorMountTableModel(MotorConfigurationPanel motorConfigurationPanel, Rocket rocket) { + this.motorConfigurationPanel = motorConfigurationPanel; + + for (RocketComponent c : rocket) { + if (c instanceof MotorMount) { + potentialMounts.add((MotorMount) c); + } + } + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public int getRowCount() { + return potentialMounts.size(); + } + + @Override + public Class getColumnClass(int column) { + switch (column) { + case 0: + return Boolean.class; + + case 1: + return String.class; + + default: + throw new IndexOutOfBoundsException("column=" + column); + } + } + + @Override + public Object getValueAt(int row, int column) { + switch (column) { + case 0: + return new Boolean(potentialMounts.get(row).isMotorMount()); + + case 1: + return potentialMounts.get(row).toString(); + + default: + throw new IndexOutOfBoundsException("column=" + column); + } + } + + @Override + public boolean isCellEditable(int row, int column) { + return column == 0; + } + + @Override + public void setValueAt(Object value, int row, int column) { + if (column != 0 || !(value instanceof Boolean)) { + throw new IllegalArgumentException("column=" + column + ", value=" + value); + } + + MotorMount mount = potentialMounts.get(row); + mount.setMotorMount((Boolean) value); + this.motorConfigurationPanel.fireTableDataChanged(); + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RecoveryConfigurationPanel.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RecoveryConfigurationPanel.java new file mode 100644 index 000000000..42b974c1c --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RecoveryConfigurationPanel.java @@ -0,0 +1,264 @@ +package net.sf.openrocket.gui.dialogs.flightconfiguration; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Iterator; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; + +public class RecoveryConfigurationPanel extends JPanel { + + private Translator trans = Application.getTranslator(); + + + private final FlightConfigurationDialog flightConfigurationDialog; + private final Rocket rocket; + + private final RecoveryTableModel recoveryTableModel; + private final JTable recoveryTable; + private final JButton selectDeploymentButton; + private final JButton resetDeploymentButton; + + + RecoveryConfigurationPanel(FlightConfigurationDialog flightConfigurationDialog, Rocket rocket) { + super(new MigLayout("fill")); + this.flightConfigurationDialog = flightConfigurationDialog; + this.rocket = rocket; + + //// Recovery selection + recoveryTableModel = new RecoveryTableModel(); + recoveryTable = new JTable(recoveryTableModel); + recoveryTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + recoveryTable.setRowSelectionAllowed(true); + recoveryTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + updateButtonState(); + + if (e.getClickCount() == 2) { + // Double-click edits + selectDeployment(); + } + } + }); + recoveryTable.setDefaultRenderer(Object.class, new RecoveryTableCellRenderer()); + + JScrollPane scroll = new JScrollPane(recoveryTable); + this.add(scroll, "span, grow, wrap"); + + //// Select deployment + selectDeploymentButton = new JButton(trans.get("edtmotorconfdlg.but.Selectdeployment")); + selectDeploymentButton.setEnabled(false); + selectDeploymentButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectDeployment(); + } + }); + this.add(selectDeploymentButton, "skip, split, sizegroup button"); + + //// Reset deployment + resetDeploymentButton = new JButton(trans.get("edtmotorconfdlg.but.Resetdeployment")); + resetDeploymentButton.setEnabled(false); + resetDeploymentButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + resetDeployment(); + } + }); + this.add(resetDeploymentButton, "sizegroup button, wrap"); + } + + public void fireTableDataChanged() { + int selected = recoveryTable.getSelectedRow(); + recoveryTableModel.fireTableDataChanged(); + if (selected >= 0) { + selected = Math.min(selected, recoveryTable.getRowCount() - 1); + recoveryTable.getSelectionModel().setSelectionInterval(selected, selected); + } + updateButtonState(); + } + + private void selectDeployment() { + RecoveryDevice c = getSelectedComponent(); + if (c == null) { + return; + } + JDialog d = new DeploymentSelectionDialog(flightConfigurationDialog, rocket, c); + d.setVisible(true); + fireTableDataChanged(); + } + + private void resetDeployment() { + RecoveryDevice c = getSelectedComponent(); + if (c == null) { + return; + } + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + c.getDeploymentConfiguration().resetDefault(id); + fireTableDataChanged(); + } + + public void updateButtonState() { + boolean componentSelected = getSelectedComponent() != null; + selectDeploymentButton.setEnabled(componentSelected); + resetDeploymentButton.setEnabled(componentSelected); + } + + + private RecoveryDevice getSelectedComponent() { + int row = recoveryTable.getSelectedRow(); + return findRecoveryDevice(row); + } + + private RecoveryDevice findRecoveryDevice(int count) { + RecoveryDevice d = null; + Iterator it = rocket.iterator(); + while (it.hasNext() && count >= 0) { + RocketComponent c = it.next(); + if (c instanceof RecoveryDevice) { + d = (RecoveryDevice) c; + count--; + } + } + return d; + } + + + + private class RecoveryTableModel extends AbstractTableModel { + + @Override + public int getRowCount() { + int count = 0; + Iterator it = rocket.iterator(); + while (it.hasNext()) { + RocketComponent c = it.next(); + if (c instanceof RecoveryDevice) { + count++; + } + } + return count; + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + RecoveryDevice d = findRecoveryDevice(rowIndex); + switch (columnIndex) { + case 0: + return d.getName(); + case 1: + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + DeploymentConfiguration config = d.getDeploymentConfiguration().get(id); + boolean isDefault = d.getDeploymentConfiguration().isDefault(id); + + String str; + + str = trans.get("RecoveryDevice.DeployEvent.short." + config.getDeployEvent().name()); + if (config.getDeployEvent() == DeployEvent.ALTITUDE) { + str += " " + UnitGroup.UNITS_DISTANCE.toStringUnit(config.getDeployAltitude()); + } + if (config.getDeployDelay() > 0.001) { + str += " + " + UnitGroup.UNITS_SHORT_TIME.toStringUnit(config.getDeployDelay()); + } + + + if (isDefault) { + String def = trans.get("table.deployment.default"); + str = def.replace("{0}", str); + } + return str; + + default: + throw new IndexOutOfBoundsException("columnIndex=" + columnIndex); + } + + } + + @Override + public String getColumnName(int column) { + switch (column) { + case 0: + return trans.get("edtmotorconfdlg.tbl.Recoveryheader"); + case 1: + return trans.get("edtmotorconfdlg.tbl.Deploymentheader"); + default: + return ""; + } + } + + } + + + private class RecoveryTableCellRenderer extends DefaultTableCellRenderer { + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (!(c instanceof JLabel)) { + return c; + } + JLabel label = (JLabel) c; + + RecoveryDevice recoveryDevice = findRecoveryDevice(row); + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + + switch (column) { + case 0: + regular(label); + break; + + case 1: + if (recoveryDevice.getDeploymentConfiguration().isDefault(id)) { + shaded(label); + } else { + regular(label); + } + break; + } + + return label; + } + + private void shaded(JLabel label) { + GUIUtil.changeFontStyle(label, Font.ITALIC); + label.setForeground(Color.GRAY); + } + + private void regular(JLabel label) { + GUIUtil.changeFontStyle(label, Font.PLAIN); + label.setForeground(Color.BLACK); + } + + } + +} diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java new file mode 100644 index 000000000..bd5d5afeb --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java @@ -0,0 +1,70 @@ +package net.sf.openrocket.gui.dialogs.flightconfiguration; + +import java.awt.Dialog; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.startup.Application; + +public class RenameConfigDialog extends JDialog { + + private static final Translator trans = Application.getTranslator(); + + RenameConfigDialog(final FlightConfigurationDialog parent, final Rocket rocket) { + super(parent, trans.get("RenameConfigDialog.title"), Dialog.ModalityType.APPLICATION_MODAL); + final String configId = rocket.getDefaultConfiguration().getFlightConfigurationID(); + + JPanel panel = new JPanel(new MigLayout("fill")); + + panel.add(new JLabel(trans.get("RenameConfigDialog.lbl.name")), "span, wrap rel"); + + final JTextField textbox = new JTextField(rocket.getFlightConfigurationName(configId)); + panel.add(textbox, "span, w 200lp, growx, wrap para"); + + panel.add(new JPanel(), "growx"); + + JButton okButton = new JButton(trans.get("button.ok")); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String newName = textbox.getText(); + rocket.setFlightConfigurationName(configId, newName); + RenameConfigDialog.this.setVisible(false); + } + }); + panel.add(okButton); + + JButton defaultButton = new JButton(trans.get("RenameConfigDialog.but.reset")); + defaultButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + rocket.setFlightConfigurationName(configId, null); + RenameConfigDialog.this.setVisible(false); + } + }); + panel.add(defaultButton); + + JButton cancel = new JButton(trans.get("button.cancel")); + cancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + RenameConfigDialog.this.setVisible(false); + } + }); + panel.add(cancel); + + this.add(panel); + + GUIUtil.setDisposableDialogOptions(this, okButton); + } +} diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationConfigurationPanel.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationConfigurationPanel.java new file mode 100644 index 000000000..8094a35d0 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationConfigurationPanel.java @@ -0,0 +1,259 @@ +package net.sf.openrocket.gui.dialogs.flightconfiguration; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Iterator; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; + +public class SeparationConfigurationPanel extends JPanel { + + private static final Translator trans = Application.getTranslator(); + + private final FlightConfigurationDialog flightConfigurationDialog; + private final Rocket rocket; + private final Stage[] stages; + + private final JTable separationTable; + private final SeparationTableModel separationTableModel; + private final JButton selectSeparationButton; + private final JButton resetDeploymentButton; + + + SeparationConfigurationPanel(FlightConfigurationDialog flightConfigurationDialog, Rocket rocket) { + super(new MigLayout("fill")); + this.flightConfigurationDialog = flightConfigurationDialog; + this.rocket = rocket; + + + int stageCount = rocket.getStageCount() - 1; + stages = new Stage[stageCount]; + Iterator it = rocket.iterator(); + { + int stageIndex = -1; + while (it.hasNext()) { + RocketComponent c = it.next(); + if (c instanceof Stage) { + if (stageIndex >= 0) { + stages[stageIndex] = (Stage) c; + } + stageIndex++; + } + } + } + + //// Recovery selection + separationTableModel = new SeparationTableModel(); + separationTable = new JTable(separationTableModel); + separationTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + separationTable.setRowSelectionAllowed(true); + separationTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + updateButtonState(); + if (e.getClickCount() == 2) { + // Double-click edits + selectDeployment(); + } + } + }); + separationTable.setDefaultRenderer(Object.class, new SeparationTableCellRenderer()); + + JScrollPane scroll = new JScrollPane(separationTable); + this.add(scroll, "span, grow, wrap"); + + //// Select deployment + selectSeparationButton = new JButton(trans.get("edtmotorconfdlg.but.Selectseparation")); + selectSeparationButton.setEnabled(false); + selectSeparationButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectDeployment(); + } + }); + this.add(selectSeparationButton, "skip, split, sizegroup button"); + + //// Reset deployment + resetDeploymentButton = new JButton(trans.get("edtmotorconfdlg.but.Resetseparation")); + resetDeploymentButton.setEnabled(false); + resetDeploymentButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + resetDeployment(); + } + }); + this.add(resetDeploymentButton, "sizegroup button, wrap"); + + } + + public void fireTableDataChanged() { + int selected = separationTable.getSelectedRow(); + separationTableModel.fireTableDataChanged(); + if (selected >= 0) { + selected = Math.min(selected, separationTable.getRowCount() - 1); + separationTable.getSelectionModel().setSelectionInterval(selected, selected); + } + updateButtonState(); + } + + private Stage getSelectedStage() { + int row = separationTable.getSelectedRow(); + return getStage(row); + } + + private Stage getStage(int row) { + if (row >= 0 && row < stages.length) { + return stages[row]; + } + return null; + } + + private void selectDeployment() { + Stage stage = getSelectedStage(); + if (stage == null) { + return; + } + JDialog d = new SeparationSelectionDialog(flightConfigurationDialog, rocket, stage); + d.setVisible(true); + fireTableDataChanged(); + } + + private void resetDeployment() { + Stage stage = getSelectedStage(); + if (stage == null) { + return; + } + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + stage.getStageSeparationConfiguration().resetDefault(id); + fireTableDataChanged(); + } + + public void updateButtonState() { + boolean componentSelected = getSelectedStage() != null; + selectSeparationButton.setEnabled(componentSelected); + resetDeploymentButton.setEnabled(componentSelected); + } + + private class SeparationTableModel extends AbstractTableModel { + + @Override + public int getRowCount() { + return stages.length; + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + Stage d = SeparationConfigurationPanel.this.stages[rowIndex]; + switch (columnIndex) { + case 0: + return d.getName(); + case 1: + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + StageSeparationConfiguration config = d.getStageSeparationConfiguration().get(id); + + String str; + + str = config.getSeparationEvent().toString(); + if (config.getSeparationDelay() > 0.001) { + str += " + " + UnitGroup.UNITS_SHORT_TIME.toStringUnit(config.getSeparationDelay()); + } + + if (d.getStageSeparationConfiguration().isDefault(id)) { + String def = trans.get("SeparationConfigurationPanel.table.separation.default"); + str = def.replace("{0}", str); + } + + return str; + + default: + throw new IndexOutOfBoundsException("column=" + columnIndex); + } + + } + + @Override + public String getColumnName(int column) { + switch (column) { + case 0: + return trans.get("edtmotorconfdlg.tbl.Stageheader"); + case 1: + return trans.get("edtmotorconfdlg.tbl.Separationheader"); + default: + return ""; + } + } + } + + + private class SeparationTableCellRenderer extends DefaultTableCellRenderer { + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (!(c instanceof JLabel)) { + return c; + } + JLabel label = (JLabel) c; + + Stage stage = getStage(row); + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + + switch (column) { + case 0: + regular(label); + break; + + case 1: + if (stage.getStageSeparationConfiguration().isDefault(id)) { + shaded(label); + } else { + regular(label); + } + break; + } + + return label; + } + + private void shaded(JLabel label) { + GUIUtil.changeFontStyle(label, Font.ITALIC); + label.setForeground(Color.GRAY); + } + + private void regular(JLabel label) { + GUIUtil.changeFontStyle(label, Font.PLAIN); + label.setForeground(Color.BLACK); + } + + } + + +} diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationSelectionDialog.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationSelectionDialog.java new file mode 100644 index 000000000..58a76f19f --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationSelectionDialog.java @@ -0,0 +1,116 @@ +package net.sf.openrocket.gui.dialogs.flightconfiguration; + +import java.awt.Dialog; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JSpinner; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.formatting.RocketDescriptor; +import net.sf.openrocket.gui.SpinnerEditor; +import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.adaptors.EnumModel; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration.SeparationEvent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; + +public class SeparationSelectionDialog extends JDialog { + + private static final Translator trans = Application.getTranslator(); + + private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); + + private StageSeparationConfiguration newConfiguration; + + SeparationSelectionDialog(JDialog parent, final Rocket rocket, final Stage component) { + super(parent, trans.get("edtmotorconfdlg.title.Selectseparationconf"), Dialog.ModalityType.APPLICATION_MODAL); + final String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + + newConfiguration = component.getStageSeparationConfiguration().get(id).clone(); + + JPanel panel = new JPanel(new MigLayout("fill")); + + + // Select separation event + panel.add(new JLabel(trans.get("SeparationSelectionDialog.opt.title")), "span, wrap rel"); + + boolean isDefault = component.getStageSeparationConfiguration().isDefault(id); + final JRadioButton defaultButton = new JRadioButton(trans.get("SeparationSelectionDialog.opt.default"), isDefault); + panel.add(defaultButton, "span, gapleft para, wrap rel"); + String str = trans.get("SeparationSelectionDialog.opt.override"); + str = str.replace("{0}", descriptor.format(rocket, id)); + final JRadioButton overrideButton = new JRadioButton(str, false); + panel.add(overrideButton, "span, gapleft para, wrap para"); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(defaultButton); + buttonGroup.add(overrideButton); + + // Select the button based on current configuration. If the configuration is overridden + // The the overrideButton is selected. + boolean isOverridden = !component.getStageSeparationConfiguration().isDefault(id); + if (isOverridden) { + overrideButton.setSelected(true); + } + + final JComboBox event = new JComboBox(new EnumModel(newConfiguration, "SeparationEvent")); + event.setSelectedItem(newConfiguration.getSeparationEvent()); + panel.add(event, "wrap rel"); + + // ... and delay + panel.add(new JLabel(trans.get("StageConfig.separation.lbl.plus")), "alignx 100%"); + + final DoubleModel delay = new DoubleModel(newConfiguration, "SeparationDelay", UnitGroup.UNITS_SHORT_TIME, 0); + JSpinner spin = new JSpinner(delay.getSpinnerModel()); + spin.setEditor(new SpinnerEditor(spin, 3)); + panel.add(spin, "span, split"); + + //// seconds + panel.add(new JLabel(trans.get("StageConfig.separation.lbl.seconds")), "wrap para"); + + + panel.add(new JPanel(), "span, split, growx"); + + JButton okButton = new JButton(trans.get("button.ok")); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (defaultButton.isSelected()) { + component.getStageSeparationConfiguration().setDefault(newConfiguration); + } else { + component.getStageSeparationConfiguration().set(id, newConfiguration); + } + SeparationSelectionDialog.this.setVisible(false); + } + }); + + panel.add(okButton, "sizegroup btn"); + + JButton cancel = new JButton(trans.get("button.cancel")); + cancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + SeparationSelectionDialog.this.setVisible(false); + } + }); + + panel.add(cancel, "sizegroup btn"); + + this.setContentPane(panel); + + GUIUtil.setDisposableDialogOptions(this, okButton); + } +} diff --git a/core/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorPlotDialog.java b/core/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorPlotDialog.java index c1ecb7cfb..e5099a8ea 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorPlotDialog.java +++ b/core/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorPlotDialog.java @@ -79,7 +79,7 @@ public class ThrustCurveMotorPlotDialog extends JDialog { // Selected thrust curve int n = 0; if (selected >= 0) { - dataset.addSeries(generateSeries(motors.get(selected))); + dataset.addSeries(generateSeries(motors.get(selected),0)); renderer.setSeriesStroke(n, new BasicStroke(1.5f)); renderer.setSeriesPaint(n, ThrustCurveMotorSelectionPanel.getColor(selected)); } @@ -91,7 +91,7 @@ public class ThrustCurveMotorPlotDialog extends JDialog { continue; ThrustCurveMotor m = motors.get(i); - dataset.addSeries(generateSeries(m)); + dataset.addSeries(generateSeries(m, i)); renderer.setSeriesStroke(n, new BasicStroke(1.5f)); renderer.setSeriesPaint(n, ThrustCurveMotorSelectionPanel.getColor(i)); renderer.setSeriesShape(n, new Rectangle()); @@ -121,8 +121,12 @@ public class ThrustCurveMotorPlotDialog extends JDialog { } - private XYSeries generateSeries(ThrustCurveMotor motor) { - XYSeries series = new XYSeries(motor.getManufacturer() + " " + motor.getDesignation()); + private XYSeries generateSeries(ThrustCurveMotor motor, int i) { + String label = motor.getManufacturer() + " " + motor.getDesignation(); + if ( i> 0 ) { + label += " ("+i+")"; + } + XYSeries series = new XYSeries(label); double[] time = motor.getTimePoints(); double[] thrust = motor.getThrustPoints(); diff --git a/core/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java b/core/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java index b2f7ecb07..aaa19b633 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java +++ b/core/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java @@ -713,7 +713,7 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec ThrustCurveMotor m = motors.get(i); //// Thrust - XYSeries series = new XYSeries(trans.get("TCMotorSelPan.title.Thrust")); + XYSeries series = new XYSeries(trans.get("TCMotorSelPan.title.Thrust") + " (" + i + ")"); double[] time = m.getTimePoints(); double[] thrust = m.getThrustPoints(); diff --git a/core/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java b/core/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java index 7c597e9ec..767c50f38 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java +++ b/core/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java @@ -54,6 +54,7 @@ import javax.swing.tree.TreePath; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.formatting.RocketDescriptor; import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.adaptors.DoubleModel; import net.sf.openrocket.gui.components.CsvOptionPanel; @@ -96,9 +97,8 @@ import net.sf.openrocket.util.TextUtil; import com.itextpdf.text.Font; - /** - * General rocket optimization dialog. + * General rocket optimization dialog. * * @author Sampo Niskanen */ @@ -108,7 +108,6 @@ public class GeneralOptimizationDialog extends JDialog { private static final Collator collator = Collator.getInstance(); - private static final String GOAL_MAXIMIZE = trans.get("goal.maximize"); private static final String GOAL_MINIMIZE = trans.get("goal.minimize"); private static final String GOAL_SEEK = trans.get("goal.seek"); @@ -116,6 +115,7 @@ public class GeneralOptimizationDialog extends JDialog { private static final String START_TEXT = trans.get("btn.start"); private static final String STOP_TEXT = trans.get("btn.stop"); + private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); private final List optimizationParameters = new ArrayList(); @@ -126,7 +126,6 @@ public class GeneralOptimizationDialog extends JDialog { private final OpenRocketDocument baseDocument; private OpenRocketDocument documentCopy; - private final JButton addButton; private final JButton removeButton; private final JButton removeAllButton; @@ -177,7 +176,6 @@ public class GeneralOptimizationDialog extends JDialog { /** The optimization worker that is running */ private OptimizationWorker worker = null; - private double bestValue = Double.NaN; private Unit bestValueUnit = Unit.NOUNIT; private int stepCount = 0; @@ -187,15 +185,13 @@ public class GeneralOptimizationDialog extends JDialog { private final Map evaluationHistory = new LinkedHashMap(); private final List optimizationPath = new LinkedList(); - private boolean updating = false; - /** * Sole constructor. * - * @param document the document - * @param parent the parent window + * @param document the document + * @param parent the parent window */ public GeneralOptimizationDialog(OpenRocketDocument document, Window parent) { super(parent, trans.get("title")); @@ -213,7 +209,6 @@ public class GeneralOptimizationDialog extends JDialog { JPanel panel = new JPanel(new MigLayout("fill")); - ChangeListener clearHistoryChangeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { @@ -227,9 +222,7 @@ public class GeneralOptimizationDialog extends JDialog { } }; - - - //// Selected modifiers table + // // Selected modifiers table selectedModifierTableModel = new ParameterSelectionTableModel(); selectedModifierTable = new JTable(selectedModifierTableModel); @@ -275,9 +268,7 @@ public class GeneralOptimizationDialog extends JDialog { disableComponents.add(selectedModifierDescription); panel.add(selectedModifierDescription, "growx"); - - - //// Add/remove buttons + // // Add/remove buttons sub = new JPanel(new MigLayout("fill")); addButton = new JButton(Chars.LEFT_ARROW + " " + trans.get("btn.add") + " "); @@ -331,9 +322,7 @@ public class GeneralOptimizationDialog extends JDialog { panel.add(sub); - - - //// Available modifier tree + // // Available modifier tree availableModifierTree = new SimulationModifierTree(documentCopy.getRocket(), simulationModifiers, selectedModifiers); availableModifierTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { @Override @@ -365,10 +354,7 @@ public class GeneralOptimizationDialog extends JDialog { panel.add(label, "split 2, flowy"); panel.add(scroll, "width 300lp, height 200lp, grow, wrap para*2"); - - - - //// Optimization options sub-panel + // // Optimization options sub-panel sub = new JPanel(new MigLayout("fill")); TitledBorder border = BorderFactory.createTitledBorder(trans.get("lbl.optimizationOpts")); @@ -376,8 +362,7 @@ public class GeneralOptimizationDialog extends JDialog { sub.setBorder(border); disableComponents.add(sub); - - //// Simulation to optimize + // // Simulation to optimize label = new JLabel(trans.get("lbl.optimizeSim")); tip = trans.get("lbl.optimizeSim.ttip"); @@ -392,9 +377,7 @@ public class GeneralOptimizationDialog extends JDialog { disableComponents.add(simulationSelectionCombo); sub.add(simulationSelectionCombo, "growx, wrap unrel"); - - - //// Value to optimize + // // Value to optimize label = new JLabel(trans.get("lbl.optimizeValue")); tip = trans.get("lbl.optimizeValue.ttip"); label.setToolTipText(tip); @@ -408,9 +391,7 @@ public class GeneralOptimizationDialog extends JDialog { disableComponents.add(optimizationParameterCombo); sub.add(optimizationParameterCombo, "growx, wrap unrel"); - - - //// Optimization goal + // // Optimization goal label = new JLabel(trans.get("lbl.optimizeGoal")); tip = trans.get("lbl.optimizeGoal"); label.setToolTipText(tip); @@ -424,8 +405,7 @@ public class GeneralOptimizationDialog extends JDialog { disableComponents.add(optimizationGoalCombo); sub.add(optimizationGoalCombo, "growx"); - - //// Optimization custom value + // // Optimization custom value optimizationSeekValue = new DoubleModel(0, UnitGroup.UNITS_NONE); optimizationSeekValue.addChangeListener(clearHistoryChangeListener); @@ -441,12 +421,9 @@ public class GeneralOptimizationDialog extends JDialog { disableComponents.add(optimizationGoalUnitSelector); sub.add(optimizationGoalUnitSelector, "width 20lp, wrap unrel"); - panel.add(sub, "grow"); - - - //// Required stability sub-panel + // // Required stability sub-panel sub = new JPanel(new MigLayout("fill")); border = BorderFactory.createTitledBorder(trans.get("lbl.requireStability")); @@ -454,16 +431,13 @@ public class GeneralOptimizationDialog extends JDialog { sub.setBorder(border); disableComponents.add(sub); - - double ref = CaliberUnit.calculateCaliber(baseDocument.getRocket()); minimumStability = new DoubleModel(ref, UnitGroup.stabilityUnits(ref)); maximumStability = new DoubleModel(5 * ref, UnitGroup.stabilityUnits(ref)); minimumStability.addChangeListener(clearHistoryChangeListener); maximumStability.addChangeListener(clearHistoryChangeListener); - - //// Minimum stability + // // Minimum stability tip = trans.get("lbl.requireMinStability.ttip"); minimumStabilitySelected = new JCheckBox(trans.get("lbl.requireMinStability")); minimumStabilitySelected.setSelected(true); @@ -488,8 +462,7 @@ public class GeneralOptimizationDialog extends JDialog { disableComponents.add(minimumStabilityUnitSelector); sub.add(minimumStabilityUnitSelector, "growx, wrap unrel"); - - //// Maximum stability + // // Maximum stability tip = trans.get("lbl.requireMaxStability.ttip"); maximumStabilitySelected = new JCheckBox(trans.get("lbl.requireMaxStability")); maximumStabilitySelected.setToolTipText(tip); @@ -516,28 +489,22 @@ public class GeneralOptimizationDialog extends JDialog { // DescriptionArea desc = new DescriptionArea("Stability requirements are verified during each time step of the simulation.", - // 2, -2, false); - // desc.setViewportBorder(null); - // disableComponents.add(desc); - // sub.add(desc, "span, growx"); - + // 2, -2, false); + // desc.setViewportBorder(null); + // disableComponents.add(desc); + // sub.add(desc, "span, growx"); panel.add(sub, "span 2, grow, wrap para*2"); - - - - //// Rocket figure + // // Rocket figure figure = new RocketFigure(getSelectedSimulation().getConfiguration()); figure.setBorderPixels(1, 1); ScaleScrollPane figureScrollPane = new ScaleScrollPane(figure); figureScrollPane.setFitting(true); panel.add(figureScrollPane, "span, split, height 200lp, grow"); - sub = new JPanel(new MigLayout("fill")); - label = new JLabel(trans.get("status.bestValue")); tip = trans.get("status.bestValue.ttip"); label.setToolTipText(tip); @@ -547,7 +514,6 @@ public class GeneralOptimizationDialog extends JDialog { bestValueLabel.setToolTipText(tip); sub.add(bestValueLabel, "wmin 60lp, wrap rel"); - label = new JLabel(trans.get("status.stepCount")); tip = trans.get("status.stepCount.ttip"); label.setToolTipText(tip); @@ -557,7 +523,6 @@ public class GeneralOptimizationDialog extends JDialog { stepCountLabel.setToolTipText(tip); sub.add(stepCountLabel, "wrap rel"); - label = new JLabel(trans.get("status.evalCount")); tip = trans.get("status.evalCount.ttip"); label.setToolTipText(tip); @@ -567,7 +532,6 @@ public class GeneralOptimizationDialog extends JDialog { evaluationCountLabel.setToolTipText(tip); sub.add(evaluationCountLabel, "wrap rel"); - label = new JLabel(trans.get("status.stepSize")); tip = trans.get("status.stepSize.ttip"); label.setToolTipText(tip); @@ -577,8 +541,7 @@ public class GeneralOptimizationDialog extends JDialog { stepSizeLabel.setToolTipText(tip); sub.add(stepSizeLabel, "wrap para"); - - //// Start/Stop button + // // Start/Stop button startButton = new JToggleButton(START_TEXT); startButton.addActionListener(new ActionListener() { @@ -599,7 +562,6 @@ public class GeneralOptimizationDialog extends JDialog { }); sub.add(startButton, "span, growx, wrap para*2"); - plotButton = new JButton(trans.get("btn.plotPath")); plotButton.setToolTipText(trans.get("btn.plotPath.ttip")); plotButton.addActionListener(new ActionListener() { @@ -619,7 +581,6 @@ public class GeneralOptimizationDialog extends JDialog { disableComponents.add(plotButton); sub.add(plotButton, "span, growx, wrap"); - saveButton = new JButton(trans.get("btn.save")); saveButton.setToolTipText(trans.get("btn.save.ttip")); saveButton.addActionListener(new ActionListener() { @@ -632,14 +593,9 @@ public class GeneralOptimizationDialog extends JDialog { disableComponents.add(saveButton); sub.add(saveButton, "span, growx"); - - panel.add(sub, "wrap para*2"); - - - - //// Bottom buttons + // // Bottom buttons applyButton = new JButton(trans.get("btn.apply")); applyButton.setToolTipText(trans.get("btn.apply.ttip")); @@ -677,21 +633,18 @@ public class GeneralOptimizationDialog extends JDialog { }); panel.add(closeButton, "right"); - this.add(panel); clearHistory(); updateComponents(); GUIUtil.setDisposableDialogOptions(this, null); } - private void startOptimization() { if (running) { log.info("Optimization already running"); return; } - if (selectedModifiers.isEmpty()) { JOptionPane.showMessageDialog(this, trans.get("error.selectParams.text"), trans.get("error.selectParams.title"), JOptionPane.ERROR_MESSAGE); @@ -702,7 +655,6 @@ public class GeneralOptimizationDialog extends JDialog { return; } - running = true; // Update the button status @@ -761,7 +713,6 @@ public class GeneralOptimizationDialog extends JDialog { maxAbsolute = true; } - if (!minimumStabilitySelected.isSelected()) { min = Double.NaN; minAbsolute = maxAbsolute; @@ -778,6 +729,22 @@ public class GeneralOptimizationDialog extends JDialog { SimulationModifier[] modifiers = selectedModifiers.toArray(new SimulationModifier[0]); + // Check for DeploymentAltitude modifier, if it's there, we want to make certain the DeploymentEvent + // is ALTITUDE: + for (SimulationModifier mod : modifiers) { + + try { + mod.initialize(simulation); + } catch (OptimizationException ex) { + updating = true; + startButton.setSelected(false); + startButton.setText(START_TEXT); + updating = false; + throw new BugException(ex); + } + + } + // Create and start the background worker worker = new OptimizationWorker(simulation, parameter, goal, domain, modifiers) { @Override @@ -862,7 +829,6 @@ public class GeneralOptimizationDialog extends JDialog { }; worker.start(); - clearHistory(); updateComponents(); @@ -889,13 +855,9 @@ public class GeneralOptimizationDialog extends JDialog { startButton.setText(START_TEXT); updating = false; - updateComponents(); } - - - /** * Reset the current optimization history and values. This does not reset the design. */ @@ -911,7 +873,6 @@ public class GeneralOptimizationDialog extends JDialog { updateComponents(); } - private void applyDesign() { // TODO: MEDIUM: Apply also potential changes to simulations Rocket src = getSelectedSimulation().getRocket().copyWithOriginalID(); @@ -938,7 +899,6 @@ public class GeneralOptimizationDialog extends JDialog { } } - private void resetDesign() { clearHistory(); @@ -969,7 +929,6 @@ public class GeneralOptimizationDialog extends JDialog { availableModifierTree.populateTree(documentCopy.getRocket(), simulationModifiers); availableModifierTree.expandComponents(); - // Update selectable simulations populateSimulations(); @@ -978,7 +937,6 @@ public class GeneralOptimizationDialog extends JDialog { } - private void populateSimulations() { String current = null; Object selection = simulationSelectionCombo.getSelectedItem(); @@ -986,30 +944,28 @@ public class GeneralOptimizationDialog extends JDialog { current = selection.toString(); } - List> simulations = new ArrayList>(); Rocket rocket = documentCopy.getRocket(); for (Simulation s : documentCopy.getSimulations()) { - String id = s.getConfiguration().getMotorConfigurationID(); - String name = createSimulationName(s.getName(), rocket.getMotorConfigurationNameOrDescription(id)); + String id = s.getConfiguration().getFlightConfigurationID(); + String name = createSimulationName(s.getName(), descriptor.format(rocket, id)); simulations.add(new Named(s, name)); } - for (String id : rocket.getMotorConfigurationIDs()) { + for (String id : rocket.getFlightConfigurationIDs()) { if (id == null) { continue; } Simulation sim = new Simulation(rocket); - sim.getConfiguration().setMotorConfigurationID(id); - String name = createSimulationName(trans.get("basicSimulationName"), rocket.getMotorConfigurationNameOrDescription(id)); + sim.getConfiguration().setFlightConfigurationID(id); + String name = createSimulationName(trans.get("basicSimulationName"), descriptor.format(rocket, id)); simulations.add(new Named(sim, name)); } - Simulation sim = new Simulation(rocket); - sim.getConfiguration().setMotorConfigurationID(null); - String name = createSimulationName(trans.get("noSimulationName"), rocket.getMotorConfigurationNameOrDescription(null)); + sim.getConfiguration().setFlightConfigurationID(null); + String name = createSimulationName(trans.get("noSimulationName"), descriptor.format(rocket, null)); simulations.add(new Named(sim, name)); @@ -1025,7 +981,6 @@ public class GeneralOptimizationDialog extends JDialog { } } - private void populateParameters() { String current = null; Object selection = optimizationParameterCombo.getSelectedItem(); @@ -1058,7 +1013,6 @@ public class GeneralOptimizationDialog extends JDialog { stepSizeLabel.setText(UnitGroup.UNITS_RELATIVE.toStringUnit(stepSize)); } - private void loadOptimizationParameters() { optimizationParameters.clear(); optimizationParameters.addAll(OptimizationServiceHelper.getOptimizableParameters(documentCopy)); @@ -1075,7 +1029,6 @@ public class GeneralOptimizationDialog extends JDialog { }); } - private void loadSimulationModifiers() { simulationModifiers.clear(); @@ -1101,13 +1054,10 @@ public class GeneralOptimizationDialog extends JDialog { } - - private void addModifier(SimulationModifier mod) { if (!selectedModifiers.contains(mod)) { log.user(1, "Adding simulation modifier " + mod); selectedModifiers.add(mod); - Collections.sort(selectedModifiers, new SimulationModifierComparator()); selectedModifierTableModel.fireTableDataChanged(); availableModifierTree.repaint(); } else { @@ -1115,7 +1065,6 @@ public class GeneralOptimizationDialog extends JDialog { } } - private void removeModifier(SimulationModifier mod) { log.user(1, "Removing simulation modifier " + mod); selectedModifiers.remove(mod); @@ -1123,8 +1072,6 @@ public class GeneralOptimizationDialog extends JDialog { availableModifierTree.repaint(); } - - /** * Update the enabled status of all components in the dialog. */ @@ -1140,7 +1087,6 @@ public class GeneralOptimizationDialog extends JDialog { updating = true; - // First enable all components if optimization not running if (!running) { log.debug("Initially enabling all components"); @@ -1149,7 +1095,6 @@ public class GeneralOptimizationDialog extends JDialog { } } - // "Add" button SimulationModifier mod = getSelectedAvailableModifier(); state = (mod != null && !selectedModifiers.contains(mod)); @@ -1166,7 +1111,6 @@ public class GeneralOptimizationDialog extends JDialog { log.debug("removeAllButton enabled: " + state); removeAllButton.setEnabled(state); - // Optimization goal String selected = (String) optimizationGoalCombo.getSelectedItem(); state = GOAL_SEEK.equals(selected); @@ -1174,7 +1118,6 @@ public class GeneralOptimizationDialog extends JDialog { optimizationGoalSpinner.setVisible(state); optimizationGoalUnitSelector.setVisible(state); - // Minimum/maximum stability options state = minimumStabilitySelected.isSelected(); log.debug("minimumStabilitySpinner & UnitSelector enabled: " + state); @@ -1186,7 +1129,6 @@ public class GeneralOptimizationDialog extends JDialog { maximumStabilitySpinner.setEnabled(state); maximumStabilityUnitSelector.setEnabled(state); - // Plot button (enabled if path exists and dimensionality is 1 or 2) state = (!optimizationPath.isEmpty() && (selectedModifiers.size() == 1 || selectedModifiers.size() == 2)); log.debug("plotButton enabled: " + state + " optimizationPath.isEmpty=" + optimizationPath.isEmpty() + @@ -1198,7 +1140,6 @@ public class GeneralOptimizationDialog extends JDialog { log.debug("saveButton enabled: " + state); saveButton.setEnabled(state); - // Last disable all components if optimization is running if (running) { log.debug("Disabling all components because optimization is running"); @@ -1207,7 +1148,6 @@ public class GeneralOptimizationDialog extends JDialog { } } - // Update description text mod = getSelectedModifier(); if (mod != null) { @@ -1216,14 +1156,12 @@ public class GeneralOptimizationDialog extends JDialog { selectedModifierDescription.setText(""); } - // Update the figure figure.setConfiguration(getSelectedSimulation().getConfiguration()); updating = false; } - private void savePath() { if (evaluationHistory.isEmpty()) { @@ -1282,7 +1220,6 @@ public class GeneralOptimizationDialog extends JDialog { writer.write("\n"); } - for (FunctionEvaluationData data : evaluationHistory.values()) { Value[] state = data.getState(); @@ -1330,7 +1267,7 @@ public class GeneralOptimizationDialog extends JDialog { /** * Return the currently selected simulation. - * @return the selected simulation. + * @return the selected simulation. */ @SuppressWarnings("unchecked") private Simulation getSelectedSimulation() { @@ -1347,11 +1284,10 @@ public class GeneralOptimizationDialog extends JDialog { return ((Named) item).get(); } - /** * Return the currently selected simulation modifier from the table, * or null if none selected. - * @return the selected modifier or null. + * @return the selected modifier or null. */ private SimulationModifier getSelectedModifier() { int row = selectedModifierTable.getSelectedRow(); @@ -1362,17 +1298,15 @@ public class GeneralOptimizationDialog extends JDialog { return selectedModifiers.get(row); } - /** * Return the currently selected optimization parameter. - * @return the selected optimization parameter. + * @return the selected optimization parameter. */ @SuppressWarnings("unchecked") private OptimizableParameter getSelectedParameter() { return ((Named) optimizationParameterCombo.getSelectedItem()).get(); } - private Unit getModifierUnit(int index) { return selectedModifiers.get(index).getUnitGroup().getDefaultUnit(); } @@ -1394,7 +1328,7 @@ public class GeneralOptimizationDialog extends JDialog { /** * The table model for the parameter selection. * - * [Body tube: Length] [min] [max] [unit] + * [Body tube: Length] [min] [max] [unit] */ private class ParameterSelectionTableModel extends AbstractTableModel { @@ -1524,7 +1458,6 @@ public class GeneralOptimizationDialog extends JDialog { } - private class DoubleCellRenderer extends DefaultTableCellRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, @@ -1590,6 +1523,4 @@ public class GeneralOptimizationDialog extends JDialog { } } - - } diff --git a/core/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java b/core/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java index 663c79b83..c18c98060 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java +++ b/core/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java @@ -119,8 +119,8 @@ public class SimulationModifierTree extends BasicTree { } - - + + public class ComponentModifierTreeRenderer extends DefaultTreeCellRenderer { private Font componentFont; private Font stringFont; @@ -142,7 +142,7 @@ public class SimulationModifierTree extends BasicTree { makeFonts(); } - + // Customize based on line type Object object = ((DefaultMutableTreeNode) value).getUserObject(); @@ -150,7 +150,7 @@ public class SimulationModifierTree extends BasicTree { // Set icon (for rocket components, null for others) setIcon(ComponentIcons.getSmallIcon(object.getClass())); - + // Set text color/style if (object instanceof RocketComponent) { setForeground(Color.GRAY); @@ -160,7 +160,7 @@ public class SimulationModifierTree extends BasicTree { RocketComponent c = (RocketComponent) object; String comment = c.getComment().trim(); if (comment.length() > 0) { - comment = TextUtil.htmlEncode(comment); + comment = TextUtil.escapeXML(comment); comment = "" + comment.replace("\n", "
"); this.setToolTipText(comment); } else { diff --git a/core/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java b/core/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java index 8e793fe50..d699abbe9 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java +++ b/core/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java @@ -1,9 +1,12 @@ package net.sf.openrocket.gui.dialogs.preferences; +import java.awt.Desktop; import java.awt.Dialog; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; @@ -13,6 +16,7 @@ import java.util.List; import java.util.Locale; import javax.swing.AbstractListModel; +import javax.swing.ButtonGroup; import javax.swing.ComboBoxModel; import javax.swing.JButton; import javax.swing.JCheckBox; @@ -23,9 +27,14 @@ import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; +import javax.swing.JRadioButton; import javax.swing.JTabbedPane; +import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.SwingUtilities; import javax.swing.Timer; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; @@ -37,8 +46,8 @@ import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.StyledLabel.Style; import net.sf.openrocket.gui.dialogs.UpdateInfoDialog; import net.sf.openrocket.gui.util.GUIUtil; -import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.gui.util.SimpleFileFilter; +import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.L10N; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.LogHelper; @@ -53,21 +62,23 @@ import net.sf.openrocket.util.Utils; public class PreferencesDialog extends JDialog { private static final LogHelper log = Application.getLogger(); - + private final List unitSelectors = new ArrayList(); - + private File defaultDirectory = null; private static final Translator trans = Application.getTranslator(); - + + private final SwingPreferences preferences = (SwingPreferences) Application.getPreferences(); + private PreferencesDialog(Window parent) { //// Preferences super(parent, trans.get("pref.dlg.title.Preferences"), Dialog.ModalityType.APPLICATION_MODAL); - + JPanel panel = new JPanel(new MigLayout("fill, gap unrel", "[grow]", "[grow][]")); - + JTabbedPane tabbedPane = new JTabbedPane(); panel.add(tabbedPane, "grow, wrap"); - + //// Units and Default units tabbedPane.addTab(trans.get("pref.dlg.tab.Units"), null, unitsPane(), trans.get("pref.dlg.tab.Defaultunits")); @@ -77,7 +88,9 @@ public class PreferencesDialog extends JDialog { //// Options and Miscellaneous options tabbedPane.addTab(trans.get("pref.dlg.tab.Options"), null, optionsPane(), trans.get("pref.dlg.tab.Miscellaneousoptions")); - + //// Decal Editor selection + tabbedPane.addTab(trans.get("pref.dlg.tab.DecalEditor"), decalEditorPane()); + //// Close button JButton close = new JButton(trans.get("dlg.but.close")); close.addActionListener(new ActionListener() { @@ -88,39 +101,39 @@ public class PreferencesDialog extends JDialog { } }); panel.add(close, "span, right, tag close"); - + this.setContentPane(panel); pack(); this.setLocationRelativeTo(null); - + this.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { - ((SwingPreferences) Application.getPreferences()).storeDefaultUnits(); + preferences.storeDefaultUnits(); } }); - + GUIUtil.setDisposableDialogOptions(this, close); } - - + + private JPanel optionsPane() { JPanel panel = new JPanel(new MigLayout("fillx, ins 30lp n n n")); - - + + //// Language selector Locale userLocale = null; { - String locale = Application.getPreferences().getString("locale", null); + String locale = preferences.getString("locale", null); userLocale = L10N.toLocale(locale); } List> locales = new ArrayList>(); for (Locale l : SwingPreferences.getSupportedLocales()) { - locales.add(new Named(l, l.getDisplayLanguage())); + locales.add(new Named(l, l.getDisplayLanguage(l) + "/" + l.getDisplayLanguage())); } Collections.sort(locales); locales.add(0, new Named(null, trans.get("languages.default"))); - + final JComboBox languageCombo = new JComboBox(locales.toArray()); for (int i = 0; i < locales.size(); i++) { if (Utils.equals(userLocale, locales.get(i).get())) { @@ -133,15 +146,15 @@ public class PreferencesDialog extends JDialog { public void actionPerformed(ActionEvent e) { Named selection = (Named) languageCombo.getSelectedItem(); Locale l = selection.get(); - Application.getPreferences().putString(Preferences.USER_LOCAL, l == null ? null : l.toString()); + preferences.putString(Preferences.USER_LOCAL, l == null ? null : l.toString()); } }); panel.add(new JLabel(trans.get("lbl.language")), "gapright para"); panel.add(languageCombo, "wrap rel, growx, sg combos"); - + panel.add(new StyledLabel(trans.get("PreferencesDialog.lbl.languageEffect"), -3, Style.ITALIC), "span, wrap para*2"); - - + + //// Position to insert new body components: panel.add(new JLabel(trans.get("pref.dlg.lbl.Positiontoinsert")), "gapright para"); panel.add(new JComboBox(new PrefChoiseSelector(Preferences.BODY_COMPONENT_INSERT_POSITION_KEY, @@ -151,7 +164,7 @@ public class PreferencesDialog extends JDialog { trans.get("pref.dlg.PrefChoiseSelector1"), trans.get("pref.dlg.PrefChoiseSelector2"), trans.get("pref.dlg.PrefChoiseSelector3"))), "wrap para, growx, sg combos"); - + //// Confirm deletion of simulations: panel.add(new JLabel(trans.get("pref.dlg.lbl.Confirmdeletion"))); panel.add(new JComboBox(new PrefBooleanSelector(Preferences.CONFIRM_DELETE_SIMULATION, @@ -159,11 +172,11 @@ public class PreferencesDialog extends JDialog { //// Confirm trans.get("pref.dlg.PrefBooleanSelector1"), trans.get("pref.dlg.PrefBooleanSelector2"), true)), "wrap 40lp, growx, sg combos"); - + //// User-defined thrust curves: panel.add(new JLabel(trans.get("pref.dlg.lbl.User-definedthrust")), "spanx, wrap"); final JTextField field = new JTextField(); - List files = ((SwingPreferences) Application.getPreferences()).getUserThrustCurveFiles(); + List files = preferences.getUserThrustCurveFiles(); String str = ""; for (File file : files) { if (str.length() > 0) { @@ -177,17 +190,17 @@ public class PreferencesDialog extends JDialog { public void removeUpdate(DocumentEvent e) { changed(); } - + @Override public void insertUpdate(DocumentEvent e) { changed(); } - + @Override public void changedUpdate(DocumentEvent e) { changed(); } - + private void changed() { String text = field.getText(); List list = new ArrayList(); @@ -197,11 +210,11 @@ public class PreferencesDialog extends JDialog { list.add(new File(s)); } } - ((SwingPreferences) Application.getPreferences()).setUserThrustCurveFiles(list); + preferences.setUserThrustCurveFiles(list); } }); panel.add(field, "w 100px, gapright unrel, spanx, growx, split"); - + //// Add button JButton button = new JButton(trans.get("pref.dlg.but.add")); button.addActionListener(new ActionListener() { @@ -228,7 +241,7 @@ public class PreferencesDialog extends JDialog { if (defaultDirectory != null) { chooser.setCurrentDirectory(defaultDirectory); } - + //// Add int returnVal = chooser.showDialog(PreferencesDialog.this, trans.get("pref.dlg.Add")); if (returnVal == JFileChooser.APPROVE_OPTION) { @@ -244,39 +257,39 @@ public class PreferencesDialog extends JDialog { } }); panel.add(button, "gapright unrel"); - + //// Reset button button = new JButton(trans.get("pref.dlg.but.reset")); - + button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // First one sets to the default, but does not un-set the pref - field.setText(((SwingPreferences)Application.getPreferences()).getDefaultUserThrustCurveFile().getAbsolutePath()); - ((SwingPreferences) Application.getPreferences()).setUserThrustCurveFiles(null); + field.setText(preferences.getDefaultUserThrustCurveFile().getAbsolutePath()); + preferences.setUserThrustCurveFiles(null); } }); panel.add(button, "wrap"); - + //// Add directories, RASP motor files (*.eng), RockSim engine files (*.rse) or ZIP archives separated by a semicolon (;) to load external thrust curves. Changes will take effect the next time you start OpenRocket. DescriptionArea desc = new DescriptionArea(trans.get("pref.dlg.DescriptionArea.Adddirectories"), 3, -3, false); desc.setBackground(getBackground()); panel.add(desc, "spanx, growx, wrap 40lp"); - - - + + + //// Check for software updates at startup final JCheckBox softwareUpdateBox = new JCheckBox(trans.get("pref.dlg.checkbox.Checkupdates")); - softwareUpdateBox.setSelected( Application.getPreferences().getCheckUpdates()); + softwareUpdateBox.setSelected(preferences.getCheckUpdates()); softwareUpdateBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - Application.getPreferences().setCheckUpdates(softwareUpdateBox.isSelected()); + preferences.setCheckUpdates(softwareUpdateBox.isSelected()); } }); panel.add(softwareUpdateBox); - + //// Check now button button = new JButton(trans.get("pref.dlg.but.checknow")); //// Check for software updates now @@ -288,140 +301,139 @@ public class PreferencesDialog extends JDialog { } }); panel.add(button, "right, wrap"); - - - final JCheckBox autoOpenDesignFile = new JCheckBox(trans.get("pref.dlg.but.openlast")); - autoOpenDesignFile.setSelected(Application.getPreferences().isAutoOpenLastDesignOnStartupEnabled()); - autoOpenDesignFile.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Application.getPreferences().setAutoOpenLastDesignOnStartup(autoOpenDesignFile.isSelected()); - } - }); - panel.add(autoOpenDesignFile); - - return panel; + + final JCheckBox autoOpenDesignFile = new JCheckBox(trans.get("pref.dlg.but.openlast")); + autoOpenDesignFile.setSelected(preferences.isAutoOpenLastDesignOnStartupEnabled()); + autoOpenDesignFile.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + preferences.setAutoOpenLastDesignOnStartup(autoOpenDesignFile.isSelected()); + } + }); + panel.add(autoOpenDesignFile); + + return panel; } - + private JPanel unitsPane() { JPanel panel = new JPanel(new MigLayout("", "[][]40lp[][]")); JComboBox combo; - + //// Select your preferred units: panel.add(new JLabel(trans.get("pref.dlg.lbl.Selectprefunits")), "span, wrap paragraph"); - - + + //// Rocket dimensions: panel.add(new JLabel(trans.get("pref.dlg.lbl.Rocketdimensions"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_LENGTH)); panel.add(combo, "sizegroup boxes"); - + //// Line density: panel.add(new JLabel(trans.get("pref.dlg.lbl.Linedensity"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_LINE)); panel.add(combo, "sizegroup boxes, wrap"); - - + + //// Motor dimensions: panel.add(new JLabel(trans.get("pref.dlg.lbl.Motordimensions"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MOTOR_DIMENSIONS)); panel.add(combo, "sizegroup boxes"); - + //// Surface density: panel.add(new JLabel(trans.get("pref.dlg.lbl.Surfacedensity"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_SURFACE)); panel.add(combo, "sizegroup boxes, wrap"); - - + + //// Distance: panel.add(new JLabel(trans.get("pref.dlg.lbl.Distance"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DISTANCE)); panel.add(combo, "sizegroup boxes"); - + //// Bulk density:: panel.add(new JLabel(trans.get("pref.dlg.lbl.Bulkdensity"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_BULK)); panel.add(combo, "sizegroup boxes, wrap"); - - + + //// Velocity: panel.add(new JLabel(trans.get("pref.dlg.lbl.Velocity"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_VELOCITY)); panel.add(combo, "sizegroup boxes"); - + //// Surface roughness: panel.add(new JLabel(trans.get("pref.dlg.lbl.Surfaceroughness"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROUGHNESS)); panel.add(combo, "sizegroup boxes, wrap"); - - + + //// Acceleration: panel.add(new JLabel(trans.get("pref.dlg.lbl.Acceleration"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ACCELERATION)); panel.add(combo, "sizegroup boxes"); - + //// Area: panel.add(new JLabel(trans.get("pref.dlg.lbl.Area"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_AREA)); panel.add(combo, "sizegroup boxes, wrap"); - - + + //// Mass: panel.add(new JLabel(trans.get("pref.dlg.lbl.Mass"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MASS)); panel.add(combo, "sizegroup boxes"); - + //// Angle: panel.add(new JLabel(trans.get("pref.dlg.lbl.Angle"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ANGLE)); panel.add(combo, "sizegroup boxes, wrap"); - - + + //// Force: panel.add(new JLabel(trans.get("pref.dlg.lbl.Force"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_FORCE)); panel.add(combo, "sizegroup boxes"); - + //// Roll rate: panel.add(new JLabel(trans.get("pref.dlg.lbl.Rollrate"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROLL)); panel.add(combo, "sizegroup boxes, wrap"); - - + + //// Total impulse: panel.add(new JLabel(trans.get("pref.dlg.lbl.Totalimpulse"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_IMPULSE)); panel.add(combo, "sizegroup boxes"); - + //// Temperature: panel.add(new JLabel(trans.get("pref.dlg.lbl.Temperature"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_TEMPERATURE)); panel.add(combo, "sizegroup boxes, wrap"); - + //// Moment of inertia: panel.add(new JLabel(trans.get("pref.dlg.lbl.Momentofinertia"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_INERTIA)); panel.add(combo, "sizegroup boxes"); - + //// Pressure: panel.add(new JLabel(trans.get("pref.dlg.lbl.Pressure"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_PRESSURE)); panel.add(combo, "sizegroup boxes, wrap"); - - + + //// Stability: panel.add(new JLabel(trans.get("pref.dlg.lbl.Stability"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_STABILITY)); panel.add(combo, "sizegroup boxes"); - + //// Windspeed: panel.add(new JLabel(trans.get("pref.dlg.lbl.Windspeed"))); combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_WINDSPEED)); panel.add(combo, "sizegroup boxes, wrap para"); - - - - + + + + //// Default metric button JButton button = new JButton(trans.get("pref.dlg.but.defaultmetric")); button.addActionListener(new ActionListener() { @@ -433,7 +445,7 @@ public class PreferencesDialog extends JDialog { } }); panel.add(button, "spanx, split 2, grow"); - + //// Default imperial button button = new JButton(trans.get("pref.dlg.but.defaultimperial")); button.addActionListener(new ActionListener() { @@ -445,34 +457,133 @@ public class PreferencesDialog extends JDialog { } }); panel.add(button, "grow, wrap para"); - + //// The effects will take place the next time you open a window. panel.add(new StyledLabel( trans.get("pref.dlg.lbl.effect1"), -2, Style.ITALIC), "spanx, wrap"); - - + + return panel; } - - - - - + + + private JPanel decalEditorPane() { + + JPanel panel = new JPanel(new MigLayout("fillx, ins 30lp n n n")); + + ButtonGroup execGroup = new ButtonGroup(); + + JRadioButton showPrompt = new JRadioButton(trans.get("EditDecalDialog.lbl.prompt")); + showPrompt.setSelected(!preferences.isDecalEditorPreferenceSet()); + showPrompt.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (((JRadioButton) e.getItem()).isSelected()) { + preferences.clearDecalEditorPreference(); + } + } + }); + panel.add(showPrompt, "wrap"); + execGroup.add(showPrompt); + + if (Desktop.getDesktop().isSupported(Desktop.Action.EDIT)) { + + JRadioButton systemRadio = new JRadioButton(trans.get("EditDecalDialog.lbl.system")); + systemRadio.setSelected(preferences.isDecalEditorPreferenceSystem()); + systemRadio.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (((JRadioButton) e.getItem()).isSelected()) { + preferences.setDecalEditorPreference(true, null); + } + } + }); + panel.add(systemRadio, "wrap"); + execGroup.add(systemRadio); + + } + + boolean commandLineIsSelected = preferences.isDecalEditorPreferenceSet() && !preferences.isDecalEditorPreferenceSystem(); + final JRadioButton commandRadio = new JRadioButton(trans.get("EditDecalDialog.lbl.cmdline")); + commandRadio.setSelected(commandLineIsSelected); + panel.add(commandRadio, "wrap"); + execGroup.add(commandRadio); + + final JTextArea commandText = new JTextArea(); + commandText.setEnabled(commandLineIsSelected); + commandText.setText(commandLineIsSelected ? preferences.getDecalEditorCommandLine() : ""); + commandText.getDocument().addDocumentListener(new DocumentListener() { + + @Override + public void insertUpdate(DocumentEvent e) { + preferences.setDecalEditorPreference(false, commandText.getText()); + } + + @Override + public void removeUpdate(DocumentEvent e) { + preferences.setDecalEditorPreference(false, commandText.getText()); + } + + @Override + public void changedUpdate(DocumentEvent e) { + preferences.setDecalEditorPreference(false, commandText.getText()); + } + + }); + panel.add(commandText, "growx, wrap"); + + final JButton chooser = new JButton(trans.get("EditDecalDialog.btn.chooser")); + chooser.setEnabled(commandLineIsSelected); + chooser.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser fc = new JFileChooser(); + int action = fc.showOpenDialog(SwingUtilities.windowForComponent(PreferencesDialog.this)); + if (action == JFileChooser.APPROVE_OPTION) { + String commandLine = fc.getSelectedFile().getAbsolutePath(); + commandText.setText(commandLine); + preferences.setDecalEditorPreference(false, commandLine); + } + + } + + }); + panel.add(chooser, "growx, wrap"); + + + commandRadio.addChangeListener(new ChangeListener() { + + @Override + public void stateChanged(ChangeEvent e) { + boolean enabled = commandRadio.isSelected(); + commandText.setEnabled(enabled); + chooser.setEnabled(enabled); + } + + }); + + return panel; + } + + + + private class DefaultUnitSelector extends AbstractListModel implements ComboBoxModel { - + private final UnitGroup group; - + public DefaultUnitSelector(UnitGroup group) { this.group = group; unitSelectors.add(this); } - + @Override public Object getSelectedItem() { return group.getDefaultUnit(); } - + @Override public void setSelectedItem(Object item) { if (item == null) { @@ -484,39 +595,39 @@ public class PreferencesDialog extends JDialog { } group.setDefaultUnit(group.getUnitIndex((Unit) item)); } - + @Override public Object getElementAt(int index) { return group.getUnit(index); } - + @Override public int getSize() { return group.getUnitCount(); } - - + + public void fireChange() { this.fireContentsChanged(this, 0, this.getSize()); } } - - - + + + private class PrefChoiseSelector extends AbstractListModel implements ComboBoxModel { private final String preference; private final String[] descriptions; - + public PrefChoiseSelector(String preference, String... descriptions) { this.preference = preference; this.descriptions = descriptions; } - + @Override public Object getSelectedItem() { - return descriptions[Application.getPreferences().getChoice(preference, descriptions.length, 0)]; + return descriptions[preferences.getChoice(preference, descriptions.length, 0)]; } - + @Override public void setSelectedItem(Object item) { if (item == null) { @@ -534,27 +645,27 @@ public class PreferencesDialog extends JDialog { if (index >= descriptions.length) { throw new IllegalArgumentException("Illegal argument " + item); } - - Application.getPreferences().putChoice(preference, index); + + preferences.putChoice(preference, index); } - + @Override public Object getElementAt(int index) { return descriptions[index]; } - + @Override public int getSize() { return descriptions.length; } } - - + + private class PrefBooleanSelector extends AbstractListModel implements ComboBoxModel { private final String preference; private final String trueDesc, falseDesc; private final boolean def; - + public PrefBooleanSelector(String preference, String falseDescription, String trueDescription, boolean defaultState) { this.preference = preference; @@ -562,16 +673,16 @@ public class PreferencesDialog extends JDialog { this.falseDesc = falseDescription; this.def = defaultState; } - + @Override public Object getSelectedItem() { - if (Application.getPreferences().getBoolean(preference, def)) { + if (preferences.getBoolean(preference, def)) { return trueDesc; } else { return falseDesc; } } - + @Override public void setSelectedItem(Object item) { if (item == null) { @@ -581,53 +692,53 @@ public class PreferencesDialog extends JDialog { if (!(item instanceof String)) { throw new IllegalArgumentException("Illegal argument " + item); } - + if (trueDesc.equals(item)) { - Application.getPreferences().putBoolean(preference, true); + preferences.putBoolean(preference, true); } else if (falseDesc.equals(item)) { - Application.getPreferences().putBoolean(preference, false); + preferences.putBoolean(preference, false); } else { throw new IllegalArgumentException("Illegal argument " + item); } } - + @Override public Object getElementAt(int index) { switch (index) { case 0: return def ? trueDesc : falseDesc; - + case 1: return def ? falseDesc : trueDesc; - + default: throw new IndexOutOfBoundsException("Boolean asked for index=" + index); } } - + @Override public int getSize() { return 2; } } - - + + private void checkForUpdates() { final UpdateInfoRetriever retriever = new UpdateInfoRetriever(); retriever.start(); - - + + // Progress dialog final JDialog dialog1 = new JDialog(this, ModalityType.APPLICATION_MODAL); JPanel panel = new JPanel(new MigLayout()); - + //// Checking for updates... panel.add(new JLabel(trans.get("pref.dlg.lbl.Checkingupdates")), "wrap"); - + JProgressBar bar = new JProgressBar(); bar.setIndeterminate(true); panel.add(bar, "growx, wrap para"); - + //// Cancel button JButton cancel = new JButton(trans.get("dlg.but.cancel")); cancel.addActionListener(new ActionListener() { @@ -638,14 +749,14 @@ public class PreferencesDialog extends JDialog { }); panel.add(cancel, "right"); dialog1.add(panel); - + GUIUtil.setDisposableDialogOptions(dialog1, cancel); - - + + // Timer to monitor progress final Timer timer = new Timer(100, null); final long startTime = System.currentTimeMillis(); - + ActionListener listener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -657,12 +768,12 @@ public class PreferencesDialog extends JDialog { }; timer.addActionListener(listener); timer.start(); - - + + // Wait for action dialog1.setVisible(true); - - + + // Check result UpdateInfo info = retriever.getUpdateInfo(); if (info == null) { @@ -683,19 +794,19 @@ public class PreferencesDialog extends JDialog { UpdateInfoDialog infoDialog = new UpdateInfoDialog(info); infoDialog.setVisible(true); if (infoDialog.isReminderSelected()) { - Application.getPreferences().putString(SwingPreferences.LAST_UPDATE, ""); + preferences.putString(SwingPreferences.LAST_UPDATE, ""); } else { - Application.getPreferences().putString(SwingPreferences.LAST_UPDATE, info.getLatestVersion()); + preferences.putString(SwingPreferences.LAST_UPDATE, info.getLatestVersion()); } } - + } - - + + //////// Singleton implementation //////// - + private static PreferencesDialog dialog = null; - + public static void showPreferences(Window parent) { if (dialog != null) { dialog.dispose(); @@ -703,6 +814,6 @@ public class PreferencesDialog extends JDialog { dialog = new PreferencesDialog(parent); dialog.setVisible(true); } - - + + } diff --git a/core/src/net/sf/openrocket/gui/figure3d/ComponentRenderer.java b/core/src/net/sf/openrocket/gui/figure3d/ComponentRenderer.java deleted file mode 100644 index 80cdbfc02..000000000 --- a/core/src/net/sf/openrocket/gui/figure3d/ComponentRenderer.java +++ /dev/null @@ -1,340 +0,0 @@ -package net.sf.openrocket.gui.figure3d; - -import java.util.HashMap; -import java.util.Map; - -import javax.media.opengl.GL; -import javax.media.opengl.GL2; -import javax.media.opengl.GLAutoDrawable; -import javax.media.opengl.fixedfunc.GLLightingFunc; -import javax.media.opengl.fixedfunc.GLMatrixFunc; -import javax.media.opengl.glu.GLU; -import javax.media.opengl.glu.GLUquadric; -import javax.media.opengl.glu.GLUtessellator; -import javax.media.opengl.glu.GLUtessellatorCallback; -import javax.media.opengl.glu.GLUtessellatorCallbackAdapter; - -import net.sf.openrocket.logging.LogHelper; -import net.sf.openrocket.rocketcomponent.BodyTube; -import net.sf.openrocket.rocketcomponent.EllipticalFinSet; -import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.LaunchLug; -import net.sf.openrocket.rocketcomponent.MassObject; -import net.sf.openrocket.rocketcomponent.RingComponent; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.Transition; -import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.Coordinate; - -/* - * @author Bill Kuker - */ -public class ComponentRenderer { - private static final LogHelper log = Application.getLogger(); - - private int LOD = 80; - - GLU glu; - GLUquadric q; - GLUtessellator tobj; - - public ComponentRenderer() { - - } - - public void init(GLAutoDrawable drawable) { - glu = new GLU(); - q = glu.gluNewQuadric(); - tobj = GLU.gluNewTess(); - glu.gluQuadricTexture(q, true); - } - - private Map lists = new HashMap(); - private boolean clearDisplayLists = false; - public void updateFigure() { - clearDisplayLists = true; - } - - public void renderGeometry(GL2 gl, RocketComponent c) { - if (glu == null) - throw new IllegalStateException(this + " Not Initialized"); - - glu.gluQuadricNormals(q, GLU.GLU_SMOOTH); - - if ( clearDisplayLists ){ - log.debug("Clearing Display Lists"); - for ( int i : lists.values() ){ - gl.glDeleteLists(i,1); - } - lists.clear(); - clearDisplayLists = false; - } - if ( lists.containsKey(c) ){ - gl.glCallList(lists.get(c)); - } else { - int list = gl.glGenLists(1); - gl.glNewList(list, GL2.GL_COMPILE_AND_EXECUTE); - - Coordinate[] oo = c.toAbsolute(new Coordinate(0, 0, 0)); - - for (Coordinate o : oo) { - gl.glPushMatrix(); - - gl.glTranslated(o.x, o.y, o.z); - - if (c instanceof BodyTube) { - renderTube(gl, (BodyTube) c); - } else if (c instanceof LaunchLug) { - renderLug(gl, (LaunchLug) c); - } else if (c instanceof RingComponent) { - renderRing(gl, (RingComponent) c); - } else if (c instanceof Transition) { - renderTransition(gl, (Transition) c); - } else if (c instanceof MassObject) { - renderMassObject(gl, (MassObject) c); - } else if (c instanceof FinSet) { - renderFinSet(gl, (FinSet) c); - } else { - renderOther(gl, c); - } - gl.glPopMatrix(); - } - - gl.glEndList(); - lists.put(c, list); - } - } - - private void renderOther(GL2 gl, RocketComponent c) { - gl.glBegin(GL.GL_LINES); - for (Coordinate cc : c.getComponentBounds()) { - for (Coordinate ccc : c.getComponentBounds()) { - gl.glVertex3d(cc.x, cc.y, cc.z); - gl.glVertex3d(ccc.x, ccc.y, ccc.z); - } - } - gl.glEnd(); - } - - private void renderTransition(GL2 gl, Transition t) { - gl.glRotated(90, 0, 1.0, 0); - - if (t.getType() == Transition.Shape.CONICAL) { - glu.gluCylinder(q, t.getForeRadius(), t.getAftRadius(), - t.getLength(), LOD, 1); - } else { - TransitionRenderer.drawTransition(gl, t, LOD, LOD); - } - - // Render AFT shoulder - gl.glPushMatrix(); - gl.glTranslated(0, 0, t.getLength()); - - glu.gluCylinder(q, t.getAftShoulderRadius(), t.getAftShoulderRadius(), - t.getAftShoulderLength(), LOD, 1); - - gl.glRotated(180, 0, 1.0, 0); - - glu.gluDisk(q, t.getAftRadius(), t.getAftShoulderRadius(), LOD, 2); - - gl.glTranslated(0, 0, -t.getAftShoulderLength()); - - if (t.isFilled() || t.isAftShoulderCapped()) { - glu.gluDisk(q, t.getAftShoulderRadius(), 0, LOD, 2); - } - gl.glPopMatrix(); - - // Render Fore Shoulder - gl.glPushMatrix(); - gl.glRotated(180, 0, 1.0, 0); - - glu.gluCylinder(q, t.getForeShoulderRadius(), - t.getForeShoulderRadius(), t.getForeShoulderLength(), LOD, 1); - - gl.glRotated(180, 0, 1.0, 0); - - glu.gluDisk(q, t.getForeRadius(), t.getForeShoulderRadius(), LOD, 2); - - gl.glTranslated(0, 0, -t.getForeShoulderLength()); - - if (t.isFilled() || t.isForeShoulderCapped()) { - glu.gluDisk(q, t.getForeShoulderRadius(), 0, LOD, 2); - } - gl.glPopMatrix(); - - } - - private void renderTube(GL2 gl, BodyTube t) { - gl.glRotated(90, 0, 1.0, 0); - glu.gluCylinder(q, t.getOuterRadius(), t.getOuterRadius(), - t.getLength(), LOD, 1); - } - - private void renderRing(GL2 gl, RingComponent r) { - gl.glRotated(90, 0, 1.0, 0); - glu.gluCylinder(q, r.getOuterRadius(), r.getOuterRadius(), - r.getLength(), LOD, 1); - - gl.glRotated(180, 0, 1.0, 0); - glu.gluDisk(q, r.getInnerRadius(), r.getOuterRadius(), LOD, 2); - - gl.glRotated(180, 0, 1.0, 0); - gl.glTranslated(0, 0, r.getLength()); - glu.gluDisk(q, r.getInnerRadius(), r.getOuterRadius(), LOD, 2); - - gl.glTranslated(0, 0, -r.getLength()); - glu.gluCylinder(q, r.getInnerRadius(), r.getInnerRadius(), - r.getLength(), LOD, 1); - - } - - private void renderLug(GL2 gl, LaunchLug t) { - - gl.glRotated(90, 0, 1.0, 0); - glu.gluCylinder(q, t.getOuterRadius(), t.getOuterRadius(), - t.getLength(), LOD, 1); - } - - private void renderMassObject(GL2 gl, MassObject o) { - gl.glRotated(90, 0, 1.0, 0); - - MassObjectRenderer.drawMassObject(gl, o, LOD, LOD); - } - - private void renderFinSet(final GL2 gl, FinSet fs) { - - Coordinate finPoints[] = fs.getFinPointsWithTab(); - - double minX = Double.MAX_VALUE; - double minY = Double.MAX_VALUE; - double maxX = Double.MIN_VALUE; - double maxY = Double.MIN_VALUE; - - for (int i = 0; i < finPoints.length; i++) { - Coordinate c = finPoints[i]; - minX = Math.min(c.x, minX); - minY = Math.min(c.y, minY); - maxX = Math.max(c.x, maxX); - maxY = Math.max(c.y, maxY); - } - - gl.glMatrixMode(GL.GL_TEXTURE); - gl.glPushMatrix(); - gl.glScaled(1/(maxX-minX), 1/(maxY-minY), 0); - gl.glTranslated(-minX, -minY - fs.getBodyRadius(), 0); - gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - - gl.glRotated(fs.getBaseRotation() * (180.0 / Math.PI), 1, 0, 0); - - for (int fin = 0; fin < fs.getFinCount(); fin++) { - - gl.glPushMatrix(); - - gl.glTranslated(fs.getLength() / 2, 0, 0); - gl.glRotated(fs.getCantAngle() * (180.0 / Math.PI), 0, 1, 0); - gl.glTranslated(-fs.getLength() / 2, 0, 0); - - GLUtessellatorCallback cb = new GLUtessellatorCallbackAdapter() { - @Override - public void vertex(Object vertexData) { - double d[] = (double[]) vertexData; - gl.glTexCoord2d(d[0], d[1]); - gl.glVertex3dv(d, 0); - } - - @Override - public void begin(int type) { - gl.glBegin(type); - } - - @Override - public void end() { - gl.glEnd(); - } - }; - - GLU.gluTessCallback(tobj, GLU.GLU_TESS_VERTEX, cb); - GLU.gluTessCallback(tobj, GLU.GLU_TESS_BEGIN, cb); - GLU.gluTessCallback(tobj, GLU.GLU_TESS_END, cb); - - GLU.gluTessBeginPolygon(tobj, null); - GLU.gluTessBeginContour(tobj); - gl.glNormal3f(0, 0, 1); - for (int i = finPoints.length - 1; i >= 0; i--) { - Coordinate c = finPoints[i]; - double[] p = new double[] { c.x, c.y + fs.getBodyRadius(), - c.z + fs.getThickness() / 2.0 }; - GLU.gluTessVertex(tobj, p, 0, p); - - } - GLU.gluTessEndContour(tobj); - GLU.gluTessEndPolygon(tobj); - - GLU.gluTessBeginPolygon(tobj, null); - GLU.gluTessBeginContour(tobj); - gl.glNormal3f(0, 0, -1); - for (int i = 0; i < finPoints.length; i++) { - Coordinate c = finPoints[i]; - double[] p = new double[] { c.x, c.y + fs.getBodyRadius(), - c.z - fs.getThickness() / 2.0 }; - GLU.gluTessVertex(tobj, p, 0, p); - - } - GLU.gluTessEndContour(tobj); - GLU.gluTessEndPolygon(tobj); - - // Strip around the edge - if (!(fs instanceof EllipticalFinSet)) - gl.glShadeModel(GLLightingFunc.GL_FLAT); - gl.glBegin(GL.GL_TRIANGLE_STRIP); - for (int i = 0; i <= finPoints.length; i++) { - Coordinate c = finPoints[i % finPoints.length]; - // if ( i > 1 ){ - Coordinate c2 = finPoints[(i - 1 + finPoints.length) - % finPoints.length]; - gl.glNormal3d(c2.y - c.y, c.x - c2.x, 0); - // } - gl.glTexCoord2d(c.x, c.y + fs.getBodyRadius()); - gl.glVertex3d(c.x, c.y + fs.getBodyRadius(), - c.z - fs.getThickness() / 2.0); - gl.glVertex3d(c.x, c.y + fs.getBodyRadius(), - c.z + fs.getThickness() / 2.0); - } - gl.glEnd(); - if (!(fs instanceof EllipticalFinSet)) - gl.glShadeModel(GLLightingFunc.GL_SMOOTH); - - gl.glPopMatrix(); - - gl.glRotated(360.0 / fs.getFinCount(), 1, 0, 0); - } - - gl.glMatrixMode(GL.GL_TEXTURE); - gl.glPopMatrix(); - gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - - } - - public void renderMotor(final GL2 gl, final Coordinate c, double l, double r) { - final float outside[] = { 0.2f, 0.2f, 0.2f, 1.0f }; - gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, outside, 0); - gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, outside, 0); - - gl.glPushMatrix(); - - gl.glTranslated(c.x, c.y, c.z); - - gl.glRotated(90, 0, 1.0, 0); - - glu.gluCylinder(q, r, r, l, LOD, 1); - - glu.gluDisk(q, r, 0, LOD, 2); - - gl.glTranslated(0, 0, l); - gl.glRotated(180, 0, 1.0, 0); - - glu.gluDisk(q, r, 0, LOD, 2); - - gl.glPopMatrix(); - } -} diff --git a/core/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java b/core/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java new file mode 100644 index 000000000..61e0dc993 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java @@ -0,0 +1,176 @@ +package net.sf.openrocket.gui.figure3d; + +import java.util.HashMap; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.fixedfunc.GLLightingFunc; + +import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.ExternalComponent; +import net.sf.openrocket.rocketcomponent.NoseCone; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.Color; + +public class FigureRenderer extends RocketRenderer { + private final float[] color = new float[4]; + + public FigureRenderer() { + } + + @Override + public void init(GLAutoDrawable drawable) { + super.init(drawable); + + GL2 gl = drawable.getGL().getGL2(); + + gl.glLightModelfv(GL2ES1.GL_LIGHT_MODEL_AMBIENT, + new float[] { 0, 0, 0 }, 0); + + float amb = 0.3f; + float dif = 1.0f - amb; + float spc = 1.0f; + gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_AMBIENT, + new float[] { amb, amb, amb, 1 }, 0); + gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_DIFFUSE, + new float[] { dif, dif, dif, 1 }, 0); + gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_SPECULAR, + new float[] { spc, spc, spc, 1 }, 0); + + gl.glEnable(GLLightingFunc.GL_LIGHT1); + gl.glEnable(GLLightingFunc.GL_LIGHTING); + gl.glShadeModel(GLLightingFunc.GL_SMOOTH); + + gl.glEnable(GLLightingFunc.GL_NORMALIZE); + } + + + + @Override + public boolean isDrawn(RocketComponent c) { + return true; + } + + @Override + public boolean isDrawnTransparent(RocketComponent c) { + if (c instanceof BodyTube) + return true; + if (c instanceof NoseCone) + return false; + if (c instanceof SymmetricComponent) { + if (((SymmetricComponent) c).isFilled()) + return false; + } + if (c instanceof Transition) { + Transition t = (Transition) c; + return !t.isAftShoulderCapped() && !t.isForeShoulderCapped(); + } + return false; + } + + private static final HashMap, Color> defaultColorCache = new HashMap, Color>(); + + @Override + public void renderComponent(GL2 gl, RocketComponent c, float alpha) { + + gl.glLightModeli(GL2ES1.GL_LIGHT_MODEL_TWO_SIDE, 1); + Color figureColor = c.getColor(); + if (figureColor == null) { + if (defaultColorCache.containsKey(c.getClass())) { + figureColor = defaultColorCache.get(c.getClass()); + } else { + figureColor = Application.getPreferences().getDefaultColor(c.getClass()); + defaultColorCache.put(c.getClass(), figureColor); + } + } + + //Inside + convertColor(figureColor, color); + color[0] = color[0] * 0.7f; + color[1] = color[1] * 0.7f; + color[2] = color[2] * 0.7f; + color[3] = 1.0f; + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, color, 0); + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, color, 0); + + cr.getGeometry(c, Surface.INSIDE).render(gl); + + //OUtside + // Set up the front A&D color + convertColor(figureColor, color); + color[3] = alpha; + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, color, 0); + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, color, 0); + + // Set up the Specular color & Shine + convertColor(figureColor, color); + float d = 0.9f; + float m = (float) getShine(c) / 128.0f; + color[0] = Math.max(color[0], d) * m; + color[1] = Math.max(color[1], d) * m; + color[2] = Math.max(color[2], d) * m; + + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, color, 0); + gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, getShine(c)); + + cr.getGeometry(c, Surface.OUTSIDE).render(gl); + cr.getGeometry(c, Surface.EDGES).render(gl); + + color[0] = color[1] = color[2] = 0; + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, color, 0); + + + } + + @Override + public void flushTextureCache(GLAutoDrawable drawable) { + } + + private static int getShine(RocketComponent c) { + if (c instanceof ExternalComponent) { + switch (((ExternalComponent) c).getFinish()) { + case ROUGH: + return 10; + case UNFINISHED: + return 30; + case NORMAL: + return 40; + case SMOOTH: + return 80; + case POLISHED: + return 128; + default: + return 100; + } + } + return 20; + } + + protected static void convertColor(Color color, float[] out) { + if (color == null) { + out[0] = 1; + out[1] = 1; + out[2] = 0; + } else { + out[0] = Math.max(0.2f, (float) color.getRed() / 255f) * 2; + out[1] = Math.max(0.2f, (float) color.getGreen() / 255f) * 2; + out[2] = Math.max(0.2f, (float) color.getBlue() / 255f) * 2; + } + } + + + @Override + protected void renderMotor(GL2 gl, Motor motor) { + final float outside[] = { 0.3f, 0.3f, 0.3f, 1.0f }; + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, outside, 0); + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, outside, 0); + super.renderMotor(gl, motor); + } +} diff --git a/core/src/net/sf/openrocket/gui/figure3d/JoglDebugAdaptor.java b/core/src/net/sf/openrocket/gui/figure3d/JoglDebugAdaptor.java deleted file mode 100644 index bd8120a14..000000000 --- a/core/src/net/sf/openrocket/gui/figure3d/JoglDebugAdaptor.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.sf.openrocket.gui.figure3d; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; - -import net.sf.openrocket.logging.LogHelper; -import net.sf.openrocket.startup.Application; - -/** - * Redirects system err into OpenRocket's log.debug() if openrocket.debug is enabled. - * This lets me capture JOGL debug as log events, which can be included in error reports etc. - * I wish I could get the JOGL debug log on a separate printstream, so I do not have to take - * over all of ERR. - * - * @author bkuker - * - */ -final class JoglDebugAdaptor { - private static final LogHelper log = Application.getLogger(); - - final static void plumbJoglDebug() { - if (RocketFigure3d.is3dEnabled() && System.getProperty("openrocket.debug") != null) { - System.setProperty("jogl.debug", "all"); - - System.setErr(new PrintStream(new OutputStream() { - StringBuilder sb = new StringBuilder(); - - @Override - public synchronized void write(int b) throws IOException { - if (b == '\r' || b == '\n') { - if (sb.toString().trim().length() > 0){ - String s = sb.toString(); - if ( Character.isWhitespace(s.charAt(0))){ - log.verbose(sb.toString()); - } else { - log.debug(sb.toString()); - } - } - sb = new StringBuilder(); - } else { - sb.append((char) b); - } - } - })); - } - } -} diff --git a/core/src/net/sf/openrocket/gui/figure3d/Quick3dMain.java b/core/src/net/sf/openrocket/gui/figure3d/Quick3dMain.java deleted file mode 100644 index cb119ea7f..000000000 --- a/core/src/net/sf/openrocket/gui/figure3d/Quick3dMain.java +++ /dev/null @@ -1,77 +0,0 @@ -package net.sf.openrocket.gui.figure3d; - -import java.awt.BorderLayout; - -import javax.swing.JFrame; -import javax.swing.JPanel; - -import net.sf.openrocket.database.ComponentPresetDatabase; -import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.file.DatabaseMotorFinder; -import net.sf.openrocket.file.openrocket.importt.OpenRocketLoader; -import net.sf.openrocket.gui.main.componenttree.ComponentTree; -import net.sf.openrocket.gui.scalefigure.RocketPanel; -import net.sf.openrocket.gui.util.SwingPreferences; -import net.sf.openrocket.l10n.ResourceBundleTranslator; -import net.sf.openrocket.startup.Application; - -/** - * An application for quickly testing 3d figure witout all the OpenRocket user interface - * - * @author bkuker - * - */ -public class Quick3dMain { - - /** - * @param args - */ - public static void main(String[] args) throws Exception { - Application.setBaseTranslator(new ResourceBundleTranslator( - "l10n.messages")); - // TODO: broken code due to motor db refactoring - now using Guice injectors - // Application.setMotorSetDatabase(new ThrustCurveMotorSetDatabase(false) { - // { - // startLoading(); - // } - // - // @Override - // protected void loadMotors() { - // } - // }); - Application.setPreferences(new SwingPreferences()); - - // Must be done after localization is initialized - ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase() { - - @Override - protected void load() { - // This test app doesn't need any presets loaded - just an empty database. - } - - }; - Application.setComponentPresetDao(componentPresetDao); - - OpenRocketDocument doc = new OpenRocketLoader().loadFromStream( - Quick3dMain.class.getResourceAsStream("/datafiles/examples/Clustered rocket design.ork"), - new DatabaseMotorFinder()); - - JFrame ff = new JFrame(); - ff.setSize(1200, 400); - ff.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - RocketPanel panel; - - panel = new RocketPanel(doc); - - ComponentTree ct = new ComponentTree(doc); - panel.setSelectionModel(ct.getSelectionModel()); - - JPanel p = new JPanel(); - p.setLayout(new BorderLayout()); - p.add(ct, BorderLayout.WEST); - p.add(panel, BorderLayout.CENTER); - ff.setContentPane(p); - ff.setVisible(true); - } -} diff --git a/core/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java b/core/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java new file mode 100644 index 000000000..a08c8ccb1 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java @@ -0,0 +1,263 @@ +package net.sf.openrocket.gui.figure3d; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLProfile; +import javax.media.opengl.fixedfunc.GLLightingFunc; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; +import net.sf.openrocket.appearance.defaults.DefaultAppearance; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.gui.figure3d.geometry.Geometry; +import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Color; + +import com.jogamp.opengl.util.texture.Texture; +import com.jogamp.opengl.util.texture.TextureData; +import com.jogamp.opengl.util.texture.TextureIO; + +public class RealisticRenderer extends RocketRenderer { + private final float[] colorClear = { 0, 0, 0, 0 }; + private final float[] colorWhite = { 1, 1, 1, 1 }; + private final float[] color = new float[4]; + + private Map oldTexCache = new HashMap(); + private Map texCache = new HashMap(); + private float anisotrophy = 0; + + public RealisticRenderer(OpenRocketDocument document) { + + } + + @Override + public void init(GLAutoDrawable drawable) { + super.init(drawable); + + oldTexCache = new HashMap(); + texCache = new HashMap(); + + GL2 gl = drawable.getGL().getGL2(); + + gl.glLightModelfv(GL2ES1.GL_LIGHT_MODEL_AMBIENT, new float[] { 0, 0, 0 }, 0); + + float amb = 0.3f; + float dif = 1.0f - amb; + float spc = 1.0f; + gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_AMBIENT, new float[] { amb, amb, amb, 1 }, 0); + gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_DIFFUSE, new float[] { dif, dif, dif, 1 }, 0); + gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_SPECULAR, new float[] { spc, spc, spc, 1 }, 0); + + gl.glEnable(GLLightingFunc.GL_LIGHT1); + gl.glEnable(GLLightingFunc.GL_LIGHTING); + gl.glShadeModel(GLLightingFunc.GL_SMOOTH); + + gl.glEnable(GLLightingFunc.GL_NORMALIZE); + + if (gl.isExtensionAvailable("GL_EXT_texture_filter_anisotropic")) { + float a[] = new float[1]; + gl.glGetFloatv(GL.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, a, 0); + anisotrophy = a[0]; + } + } + + @Override + public void updateFigure(GLAutoDrawable drawable) { + super.updateFigure(drawable); + clearCaches(drawable.getGL().getGL2()); + } + + @Override + public void dispose(GLAutoDrawable drawable) { + flushTextureCache(drawable); + super.dispose(drawable); + oldTexCache = null; + texCache = null; + } + + @Override + public boolean isDrawn(RocketComponent c) { + return true; + } + + @Override + public boolean isDrawnTransparent(RocketComponent c) { + return false; + } + + @Override + protected void renderMotor(final GL2 gl, final Motor motor) { + render(gl, cr.getGeometry(motor, Surface.OUTSIDE), DefaultAppearance.getDefaultAppearance(motor), true, 1); + } + + @Override + public void renderComponent(final GL2 gl, final RocketComponent c, final float alpha) { + render(gl, cr.getGeometry(c, Surface.INSIDE), DefaultAppearance.getDefaultAppearance(c), true, 1.0f); + render(gl, cr.getGeometry(c, Surface.OUTSIDE), getAppearance(c), true, alpha); + render(gl, cr.getGeometry(c, Surface.EDGES), getAppearance(c), false, alpha); + } + + private void render(GL2 gl, Geometry g, Appearance a, boolean decals, float alpha) { + final Decal t = a.getTexture(); + final Texture tex = getTexture(t); + + gl.glLightModeli(GL2.GL_LIGHT_MODEL_COLOR_CONTROL, GL2.GL_SEPARATE_SPECULAR_COLOR); + + + convertColor(a.getPaint(), color); + color[3] = alpha; + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, color, 0); + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, color, 0); + + color[0] = color[1] = color[2] = (float) a.getShine(); + color[3] = alpha; + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, color, 0); + gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, (int) (100 * a.getShine())); + + + g.render(gl); + + if (decals && t != null && tex != null) { + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); + + tex.enable(gl); + tex.bind(gl); + + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPushMatrix(); + + gl.glTranslated(-t.getCenter().x, -t.getCenter().y, 0); + gl.glRotated(57.2957795 * t.getRotation(), 0, 0, 1); + gl.glTranslated(t.getCenter().x, t.getCenter().y, 0); + + gl.glScaled(t.getScale().x, t.getScale().y, 0); + gl.glTranslated(t.getOffset().x, t.getOffset().y, 0); + + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, toEdgeMode(t.getEdgeMode())); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, toEdgeMode(t.getEdgeMode())); + + + gl.glTexParameterfv(GL.GL_TEXTURE_2D, GL2.GL_TEXTURE_BORDER_COLOR, colorClear, 0); + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, colorWhite, 0); + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, colorWhite, 0); + + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); + gl.glEnable(GL.GL_BLEND); + gl.glDepthFunc(GL.GL_LEQUAL); + + gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + + if (anisotrophy > 0) { + gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotrophy); + } + + g.render(gl); + + if (t.getEdgeMode() == Decal.EdgeMode.STICKER) { + gl.glDepthFunc(GL.GL_LESS); + } + + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPopMatrix(); + gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + tex.disable(gl); + } + + } + + @Override + public void flushTextureCache(GLAutoDrawable drawable) { + // Flush the cache twice to get rid of old images. + clearCaches(drawable.getGL().getGL2()); + clearCaches(drawable.getGL().getGL2()); + } + + private void clearCaches(GL2 gl) { + log.debug("ClearCaches"); + for (Map.Entry e : oldTexCache.entrySet()) { + log.debug("Destroying Texture for " + e.getKey()); + if (e.getValue() != null) + e.getValue().destroy(gl); + } + oldTexCache = texCache; + texCache = new HashMap(); + } + + private Texture getTexture(Decal t) { + if (t == null) + return null; + + String imageName = t.getImage().getName(); + + // Return the Cached value if available + if (texCache.containsKey(imageName)) + return texCache.get(imageName); + + // If the texture is in the Old Cache, save it. + if (oldTexCache.containsKey(imageName)) { + texCache.put(imageName, oldTexCache.get(imageName)); + oldTexCache.remove(imageName); + return texCache.get(imageName); + } + + // Otherwise load it. + Texture tex = null; + try { + log.debug("Loading texture " + t); + InputStream is = t.getImage().getBytes(); + TextureData data = TextureIO.newTextureData(GLProfile.getDefault(), is, GL.GL_RGBA, GL.GL_RGBA, true, null); + tex = TextureIO.newTexture(data); + } catch (Throwable e) { + log.error("Error loading Texture", e); + } + texCache.put(imageName, tex); + + return tex; + + } + + protected Appearance getAppearance(RocketComponent c) { + Appearance ret = c.getAppearance(); + if (ret == null) { + ret = DefaultAppearance.getDefaultAppearance(c); + } + return ret; + } + + private int toEdgeMode(Decal.EdgeMode m) { + switch (m) { + case REPEAT: + return GL.GL_REPEAT; + case MIRROR: + return GL.GL_MIRRORED_REPEAT; + case CLAMP: + return GL.GL_CLAMP_TO_EDGE; + case STICKER: + return GL2.GL_CLAMP_TO_BORDER; + default: + return GL.GL_CLAMP_TO_EDGE; + } + } + + protected static void convertColor(Color color, float[] out) { + if (color == null) { + out[0] = 1; + out[1] = 1; + out[2] = 0; + } else { + out[0] = (float) color.getRed() / 255f; + out[1] = (float) color.getGreen() / 255f; + out[2] = (float) color.getBlue() / 255f; + } + } +} diff --git a/core/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java b/core/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java index 0b15e19cb..108dd6708 100644 --- a/core/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java +++ b/core/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java @@ -21,6 +21,7 @@ import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLProfile; +import javax.media.opengl.GLRunnable; import javax.media.opengl.awt.GLCanvas; import javax.media.opengl.fixedfunc.GLLightingFunc; import javax.media.opengl.fixedfunc.GLMatrixFunc; @@ -31,6 +32,7 @@ import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; import javax.swing.event.MouseInputAdapter; +import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.figureelements.CGCaret; import net.sf.openrocket.gui.figureelements.CPCaret; import net.sf.openrocket.gui.figureelements.FigureElement; @@ -48,12 +50,15 @@ import com.jogamp.opengl.util.awt.Overlay; * @author Bill Kuker */ public class RocketFigure3d extends JPanel implements GLEventListener { + + public static final int TYPE_FIGURE = 0; + public static final int TYPE_UNFINISHED = 1; + public static final int TYPE_FINISHED = 2; + private static final long serialVersionUID = 1L; private static final LogHelper log = Application.getLogger(); static { - JoglDebugAdaptor.plumbJoglDebug(); - //this allows the GL canvas and things like the motor selection //drop down to z-order themselves. JPopupMenu.setDefaultLightWeightPopupEnabled(false); @@ -63,7 +68,8 @@ public class RocketFigure3d extends JPanel implements GLEventListener { private static double fovX = Double.NaN; private static final int CARET_SIZE = 20; - private Configuration configuration; + private final OpenRocketDocument document; + private final Configuration configuration; private GLCanvas canvas; @@ -83,9 +89,10 @@ public class RocketFigure3d extends JPanel implements GLEventListener { float[] lightPosition = new float[] { 1, 4, 1, 0 }; - RocketRenderer rr = new RocketRenderer(); + RocketRenderer rr = new FigureRenderer(); - public RocketFigure3d(Configuration config) { + public RocketFigure3d(final OpenRocketDocument document, final Configuration config) { + this.document = document; this.configuration = config; this.setLayout(new BorderLayout()); @@ -100,6 +107,16 @@ public class RocketFigure3d extends JPanel implements GLEventListener { } } + public void flushTextureCaches() { + canvas.invoke(true, new GLRunnable() { + @Override + public boolean run(GLAutoDrawable drawable) { + rr.flushTextureCache(drawable); + return false; + } + }); + } + /** * Return true if 3d view is enabled. This may be toggled by the user at * launch time. @@ -115,10 +132,10 @@ public class RocketFigure3d extends JPanel implements GLEventListener { log.debug("Setting up GL capabilities..."); log.verbose("GL - Getting Default Profile"); - GLProfile glp = GLProfile.get(GLProfile.GL2); + final GLProfile glp = GLProfile.get(GLProfile.GL2); log.verbose("GL - creating GLCapabilities"); - GLCapabilities caps = new GLCapabilities(glp); + final GLCapabilities caps = new GLCapabilities(glp); log.verbose("GL - setSampleBuffers"); caps.setSampleBuffers(true); @@ -201,21 +218,21 @@ public class RocketFigure3d extends JPanel implements GLEventListener { MouseEvent pressEvent; @Override - public void mousePressed(MouseEvent e) { + public void mousePressed(final MouseEvent e) { lastX = e.getX(); lastY = e.getY(); pressEvent = e; } @Override - public void mouseClicked(MouseEvent e) { + public void mouseClicked(final MouseEvent e) { pickPoint = new Point(lastX, canvas.getHeight() - lastY); pickEvent = e; internalRepaint(); } @Override - public void mouseDragged(MouseEvent e) { + public void mouseDragged(final MouseEvent e) { int dx = lastX - e.getX(); int dy = lastY - e.getY(); lastX = e.getX(); @@ -241,13 +258,9 @@ public class RocketFigure3d extends JPanel implements GLEventListener { canvas.addMouseListener(a); } - public void setConfiguration(Configuration configuration) { - this.configuration = configuration; - updateFigure(); - } @Override - public void display(GLAutoDrawable drawable) { + public void display(final GLAutoDrawable drawable) { GL2 gl = drawable.getGL().getGL2(); GLU glu = new GLU(); @@ -262,13 +275,17 @@ public class RocketFigure3d extends JPanel implements GLEventListener { gl.glDisable(GLLightingFunc.GL_LIGHTING); final RocketComponent picked = rr.pick(drawable, configuration, pickPoint, pickEvent.isShiftDown() ? selection : null); - if (csl != null && picked != null) { + if (csl != null) { final MouseEvent e = pickEvent; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - csl.componentClicked(new RocketComponent[] { picked }, - e); + if (picked == null) { + log.debug("unselecting"); + csl.componentClicked(new RocketComponent[] {}, e); + } else { + csl.componentClicked(new RocketComponent[] { picked }, e); + } } }); @@ -287,7 +304,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener { } - private void drawCarets(GL2 gl, GLU glu) { + private void drawCarets(final GL2 gl, final GLU glu) { final Graphics2D og2d = caretOverlay.createGraphics(); setRenderingHints(og2d); @@ -335,7 +352,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener { * Re-blits the overlay every frame. Only re-renders the overlay * when needed. */ - private void drawExtras(GL2 gl, GLU glu) { + private void drawExtras(final GL2 gl, final GLU glu) { //Only re-render if needed // redrawExtras: Some external change (new simulation data) means // the data is out of date. @@ -371,19 +388,19 @@ public class RocketFigure3d extends JPanel implements GLEventListener { } @Override - public void dispose(GLAutoDrawable drawable) { - log.verbose("GL - dispose()"); + public void dispose(final GLAutoDrawable drawable) { + log.verbose("GL - dispose() called"); + rr.dispose(drawable); } @Override - public void init(GLAutoDrawable drawable) { + public void init(final GLAutoDrawable drawable) { log.verbose("GL - init()"); - rr.init(drawable); - GL2 gl = drawable.getGL().getGL2(); + final GL2 gl = drawable.getGL().getGL2(); gl.glClearDepth(1.0f); // clear z-buffer to the farthest - gl.glDepthFunc(GL.GL_LEQUAL); // the type of depth test to do + gl.glDepthFunc(GL.GL_LESS); // the type of depth test to do float amb = 0.5f; float dif = 1.0f; @@ -400,22 +417,24 @@ public class RocketFigure3d extends JPanel implements GLEventListener { gl.glEnable(GLLightingFunc.GL_NORMALIZE); + rr.init(drawable); + extrasOverlay = new Overlay(drawable); caretOverlay = new Overlay(drawable); } @Override - public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) { + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int w, final int h) { log.verbose("GL - reshape()"); - GL2 gl = drawable.getGL().getGL2(); - GLU glu = new GLU(); + final GL2 gl = drawable.getGL().getGL2(); + final GLU glu = new GLU(); - double ratio = (double) w / (double) h; + final double ratio = (double) w / (double) h; fovX = fovY * ratio; gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION); gl.glLoadIdentity(); - glu.gluPerspective(fovY, ratio, 0.05f, 100f); + glu.gluPerspective(fovY, ratio, 0.1f, 50f); gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); redrawExtras = true; @@ -429,48 +448,55 @@ public class RocketFigure3d extends JPanel implements GLEventListener { double rMax; } + private Bounds cachedBounds = null; + /** * Calculates the bounds for the current configuration * * @return */ private Bounds calculateBounds() { - Bounds ret = new Bounds(); - Collection bounds = configuration.getBounds(); - for (Coordinate c : bounds) { - ret.xMax = Math.max(ret.xMax, c.x); - ret.xMin = Math.min(ret.xMin, c.x); - - ret.yMax = Math.max(ret.yMax, c.y); - ret.yMin = Math.min(ret.yMin, c.y); - - ret.zMax = Math.max(ret.zMax, c.z); - ret.zMin = Math.min(ret.zMin, c.z); - - double r = MathUtil.hypot(c.y, c.z); - ret.rMax = Math.max(ret.rMax, r); + if (cachedBounds != null) { + return cachedBounds; + } else { + final Bounds b = new Bounds(); + final Collection bounds = configuration.getBounds(); + for (Coordinate c : bounds) { + b.xMax = Math.max(b.xMax, c.x); + b.xMin = Math.min(b.xMin, c.x); + + b.yMax = Math.max(b.yMax, c.y); + b.yMin = Math.min(b.yMin, c.y); + + b.zMax = Math.max(b.zMax, c.z); + b.zMin = Math.min(b.zMin, c.z); + + double r = MathUtil.hypot(c.y, c.z); + b.rMax = Math.max(b.rMax, r); + } + b.xSize = b.xMax - b.xMin; + b.ySize = b.yMax - b.yMin; + b.zSize = b.zMax - b.zMin; + cachedBounds = b; + return b; } - ret.xSize = ret.xMax - ret.xMin; - ret.ySize = ret.yMax - ret.yMin; - ret.zSize = ret.zMax - ret.zMin; - return ret; } - private void setupView(GL2 gl, GLU glu) { + private void setupView(final GL2 gl, final GLU glu) { gl.glLoadIdentity(); gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_POSITION, lightPosition, 0); // Get the bounds - Bounds b = calculateBounds(); + final Bounds b = calculateBounds(); // Calculate the distance needed to fit the bounds in both the X and Y // direction // Add 10% for space around it. - double dX = (b.xSize * 1.2 / 2.0) + final double dX = (b.xSize * 1.2 / 2.0) / Math.tan(Math.toRadians(fovX / 2.0)); - double dY = (b.rMax * 2.0 * 1.2 / 2.0) + final double dY = (b.rMax * 2.0 * 1.2 / 2.0) / Math.tan(Math.toRadians(fovY / 2.0)); // Move back the greater of the 2 distances @@ -499,8 +525,14 @@ public class RocketFigure3d extends JPanel implements GLEventListener { */ public void updateFigure() { log.debug("3D Figure Updated"); - rr.updateFigure(); - internalRepaint(); + cachedBounds = null; + canvas.invoke(true, new GLRunnable() { + @Override + public boolean run(GLAutoDrawable drawable) { + rr.updateFigure(drawable); + return false; + } + }); } private void internalRepaint() { @@ -517,7 +549,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener { private Set selection = new HashSet(); - public void setSelection(RocketComponent[] selection) { + public void setSelection(final RocketComponent[] selection) { this.selection.clear(); if (selection != null) { for (RocketComponent c : selection) @@ -526,14 +558,14 @@ public class RocketFigure3d extends JPanel implements GLEventListener { internalRepaint(); } - private void setRoll(double rot) { + private void setRoll(final double rot) { if (MathUtil.equals(roll, rot)) return; this.roll = MathUtil.reduce360(rot); internalRepaint(); } - private void setYaw(double rot) { + private void setYaw(final double rot) { if (MathUtil.equals(yaw, rot)) return; this.yaw = MathUtil.reduce360(rot); @@ -542,16 +574,16 @@ public class RocketFigure3d extends JPanel implements GLEventListener { // ///////////// Extra methods - private Coordinate project(Coordinate c, GL2 gl, GLU glu) { - double[] mvmatrix = new double[16]; - double[] projmatrix = new double[16]; - int[] viewport = new int[4]; + private Coordinate project(final Coordinate c, final GL2 gl, final GLU glu) { + final double[] mvmatrix = new double[16]; + final double[] projmatrix = new double[16]; + final int[] viewport = new int[4]; gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0); gl.glGetDoublev(GLMatrixFunc.GL_MODELVIEW_MATRIX, mvmatrix, 0); gl.glGetDoublev(GLMatrixFunc.GL_PROJECTION_MATRIX, projmatrix, 0); - double out[] = new double[4]; + final double out[] = new double[4]; glu.gluProject(c.x, c.y, c.z, mvmatrix, 0, projmatrix, 0, viewport, 0, out, 0); @@ -562,22 +594,22 @@ public class RocketFigure3d extends JPanel implements GLEventListener { private Coordinate cp = new Coordinate(0, 0, 0); private Coordinate cg = new Coordinate(0, 0, 0); - public void setCG(Coordinate cg) { + public void setCG(final Coordinate cg) { this.cg = cg; redrawExtras = true; } - public void setCP(Coordinate cp) { + public void setCP(final Coordinate cp) { this.cp = cp; redrawExtras = true; } - public void addRelativeExtra(FigureElement p) { + public void addRelativeExtra(final FigureElement p) { relativeExtra.add(p); redrawExtras = true; } - public void removeRelativeExtra(FigureElement p) { + public void removeRelativeExtra(final FigureElement p) { relativeExtra.remove(p); redrawExtras = true; } @@ -587,12 +619,12 @@ public class RocketFigure3d extends JPanel implements GLEventListener { redrawExtras = true; } - public void addAbsoluteExtra(FigureElement p) { + public void addAbsoluteExtra(final FigureElement p) { absoluteExtra.add(p); redrawExtras = true; } - public void removeAbsoluteExtra(FigureElement p) { + public void removeAbsoluteExtra(final FigureElement p) { absoluteExtra.remove(p); redrawExtras = true; } @@ -613,4 +645,39 @@ public class RocketFigure3d extends JPanel implements GLEventListener { this.csl = newListener; } + public void setType(final int t) { + // The first time the user selects any 3d figure types, the canvas' internal _drawable + // has not been realized. Unfortunately, there is a test in canvas.invoke which doesn't + // execute the runnable if the drawable isn't realized. + // In order to trump this, we test if the canvas has not been realized and initialize + // the renderer accordingly. There is certainly a better way to do this. + + final RocketRenderer newRR; + + switch (t) { + case TYPE_FINISHED: + newRR = new RealisticRenderer(document); + break; + case TYPE_UNFINISHED: + newRR = new UnfinishedRenderer(document); + break; + default: + newRR = new FigureRenderer(); + } + + if (!canvas.isRealized()) { + rr = newRR; + } else { + canvas.invoke(true, new GLRunnable() { + @Override + public boolean run(GLAutoDrawable drawable) { + rr.dispose(drawable); + rr = newRR; + newRR.init(drawable); + return false; + } + }); + } + } + } diff --git a/core/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java b/core/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java index 55499332c..937d2facd 100644 --- a/core/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java +++ b/core/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java @@ -2,154 +2,138 @@ package net.sf.openrocket.gui.figure3d; import java.awt.Point; import java.nio.ByteBuffer; -import java.util.HashMap; import java.util.Iterator; import java.util.Set; import java.util.Vector; import javax.media.opengl.GL; import javax.media.opengl.GL2; -import javax.media.opengl.GL2ES1; import javax.media.opengl.GL2GL3; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.fixedfunc.GLLightingFunc; +import net.sf.openrocket.gui.figure3d.geometry.ComponentRenderer; +import net.sf.openrocket.gui.figure3d.geometry.DisplayListComponentRenderer; +import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface; +import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.rocketcomponent.ExternalComponent; import net.sf.openrocket.rocketcomponent.MotorMount; -import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.SymmetricComponent; -import net.sf.openrocket.rocketcomponent.Transition; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.Color; import net.sf.openrocket.util.Coordinate; /* * @author Bill Kuker */ -public class RocketRenderer { - ComponentRenderer cr; - +public abstract class RocketRenderer { + protected static final LogHelper log = Application.getLogger(); + + final ComponentRenderer cr = new DisplayListComponentRenderer(); + private final float[] selectedEmissive = { 1, 0, 0, 1 }; private final float[] colorBlack = { 0, 0, 0, 1 }; - private final float[] color = new float[4]; - + public void init(GLAutoDrawable drawable) { - cr = new ComponentRenderer(); cr.init(drawable); } - public void updateFigure() { - cr.updateFigure(); + public void dispose(GLAutoDrawable drawable) { } - - private boolean isDrawn(RocketComponent c) { - return true; + + public void updateFigure(GLAutoDrawable drawable) { + cr.updateFigure(drawable); } - - private boolean isDrawnTransparent(RocketComponent c) { - if (c instanceof BodyTube) - return true; - if (c instanceof NoseCone) - return false; - if (c instanceof SymmetricComponent) { - if (((SymmetricComponent) c).isFilled()) - return false; - } - if (c instanceof Transition) { - Transition t = (Transition) c; - return !t.isAftShoulderCapped() && !t.isForeShoulderCapped(); - } - return false; - } - - public RocketComponent pick(GLAutoDrawable drawable, - Configuration configuration, Point p, Set ignore) { + + public abstract void renderComponent(GL2 gl, RocketComponent c, float alpha); + + public abstract boolean isDrawn(RocketComponent c); + + public abstract boolean isDrawnTransparent(RocketComponent c); + + public abstract void flushTextureCache(GLAutoDrawable drawable); + + public RocketComponent pick(GLAutoDrawable drawable, Configuration configuration, Point p, + Set ignore) { final GL2 gl = drawable.getGL().getGL2(); gl.glEnable(GL.GL_DEPTH_TEST); - //Store a vector of pickable parts. + // Store a vector of pickable parts. final Vector pickParts = new Vector(); for (RocketComponent c : configuration) { - if ( ignore != null && ignore.contains(c) ) + if (ignore != null && ignore.contains(c)) continue; - - //Encode the index of the part as a color - //if index is 0x0ABC the color ends up as - //0xA0B0C000 with each nibble in the coresponding - //high bits of the RG and B channels. - gl.glColor4ub((byte) ((pickParts.size() >> 4) & 0xF0), - (byte) ((pickParts.size() << 0) & 0xF0), + + // Encode the index of the part as a color + // if index is 0x0ABC the color ends up as + // 0xA0B0C000 with each nibble in the coresponding + // high bits of the RG and B channels. + gl.glColor4ub((byte) ((pickParts.size() >> 4) & 0xF0), (byte) ((pickParts.size() << 0) & 0xF0), (byte) ((pickParts.size() << 4) & 0xF0), (byte) 1); pickParts.add(c); if (isDrawnTransparent(c)) { - gl.glEnable(GL.GL_CULL_FACE); - gl.glCullFace(GL.GL_FRONT); - cr.renderGeometry(gl, c); - gl.glDisable(GL.GL_CULL_FACE); + cr.getGeometry(c, Surface.INSIDE).render(gl); } else { - cr.renderGeometry(gl, c); + cr.getGeometry(c, Surface.ALL).render(gl); } } - + ByteBuffer bb = ByteBuffer.allocateDirect(4); - + gl.glReadPixels(p.x, p.y, 1, 1, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, bb); - + final int pickColor = bb.getInt(); - final int pickIndex = ((pickColor >> 20) & 0xF00) | ((pickColor >> 16) & 0x0F0) - | ((pickColor >> 12) & 0x00F); - - if ( pickIndex < 0 || pickIndex > pickParts.size() - 1 ) + final int pickIndex = ((pickColor >> 20) & 0xF00) | ((pickColor >> 16) & 0x0F0) | ((pickColor >> 12) & 0x00F); + + if (pickIndex < 0 || pickIndex > pickParts.size() - 1) return null; return pickParts.get(pickIndex); } - - public void render(GLAutoDrawable drawable, Configuration configuration, - Set selection) { + + public void render(GLAutoDrawable drawable, Configuration configuration, Set selection) { + if (cr == null) throw new IllegalStateException(this + " Not Initialized"); - + GL2 gl = drawable.getGL().getGL2(); - + gl.glEnable(GL.GL_DEPTH_TEST); // enables depth testing gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); - - { //Draw selection outline at nearest Z - gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_EMISSION, - selectedEmissive, 0); + { // Draw selection outline at nearest Z + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_EMISSION, selectedEmissive, 0); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_DIFFUSE, colorBlack, 0); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_AMBIENT, colorBlack, 0); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SPECULAR, colorBlack, 0); gl.glLineWidth(5.0f); - for (RocketComponent c : selection) { - //Draw as lines, set Z to nearest - gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE); - gl.glDepthRange(0, 0); - cr.renderGeometry(gl, c); + for (RocketComponent c : configuration) { + if (selection.contains(c)) { + // Draw as lines, set Z to nearest + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE); + gl.glDepthRange(0, 0); + cr.getGeometry(c, Surface.ALL).render(gl); - //Draw polygons, always passing depth test, - //setting Z to farthest - gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_FILL); - gl.glDepthRange(1, 1); - gl.glDepthFunc(GL.GL_ALWAYS); - cr.renderGeometry(gl, c); - gl.glDepthFunc(GL.GL_LESS); - gl.glDepthRange(0, 1); + // Draw polygons, always passing depth test, + // setting Z to farthest + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_FILL); + gl.glDepthRange(1, 1); + gl.glDepthFunc(GL.GL_ALWAYS); + cr.getGeometry(c, Surface.ALL).render(gl); + gl.glDepthFunc(GL.GL_LESS); + gl.glDepthRange(0, 1); + } } gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_FILL); - gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_EMISSION, - colorBlack, 0); - } //done with selection outline - + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_EMISSION, colorBlack, 0); + } // done with selection outline + + gl.glEnable(GL.GL_CULL_FACE); + gl.glCullFace(GL.GL_BACK); + // Draw all inner components for (RocketComponent c : configuration) { if (isDrawn(c)) { @@ -158,26 +142,11 @@ public class RocketRenderer { } } } - + renderMotors(gl, configuration); - - // Draw Tube and Transition back faces, blended with depth test - // so that they show up behind. - gl.glEnable(GL.GL_CULL_FACE); - gl.glCullFace(GL.GL_FRONT); - for (RocketComponent c : configuration) { - if (isDrawn(c)) { - if (isDrawnTransparent(c)) { - renderComponent(gl, c, 1.0f); - } - } - } - gl.glDisable(GL.GL_CULL_FACE); - + // Draw T&T front faces blended, without depth test gl.glEnable(GL.GL_BLEND); - gl.glEnable(GL.GL_CULL_FACE); - gl.glCullFace(GL.GL_BACK); for (RocketComponent c : configuration) { if (isDrawn(c)) { if (isDrawnTransparent(c)) { @@ -186,103 +155,32 @@ public class RocketRenderer { } } gl.glDisable(GL.GL_BLEND); - gl.glDisable(GL.GL_CULL_FACE); + } - + private void renderMotors(GL2 gl, Configuration configuration) { - String motorID = configuration.getMotorConfigurationID(); + String motorID = configuration.getFlightConfigurationID(); Iterator iterator = configuration.motorIterator(); while (iterator.hasNext()) { MotorMount mount = iterator.next(); - Motor motor = mount.getMotor(motorID); + Motor motor = mount.getMotorConfiguration().get(motorID).getMotor(); double length = motor.getLength(); - double radius = motor.getDiameter() / 2; - - Coordinate[] position = ((RocketComponent) mount) - .toAbsolute(new Coordinate(((RocketComponent) mount) - .getLength() + mount.getMotorOverhang() - length)); - - for (int i = 0; i < position.length; i++) { - cr.renderMotor(gl, position[i], length, radius); - } - } - - } - - - public void renderComponent(GL2 gl, RocketComponent c, float alpha) { - gl.glLightModeli(GL2ES1.GL_LIGHT_MODEL_TWO_SIDE, 1); - - getOutsideColor(c, alpha, color); - gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, color, 0); - gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, color, 0); - - getSpecularColor(c, alpha, color); - gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, color, 0); - gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, - getShininess(c)); - - getInsideColor(c, alpha, color); - gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_DIFFUSE, color, 0); - gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_AMBIENT, color, 0); - - cr.renderGeometry(gl, c); - } - - private int getShininess(RocketComponent c) { - if (c instanceof ExternalComponent) { - switch (((ExternalComponent) c).getFinish()) { - case ROUGH: - return 10; - case UNFINISHED: - return 30; - case NORMAL: - return 40; - case SMOOTH: - return 80; - case POLISHED: - return 128; - } - return 100; - } else { - return 20; - } - } - - private void getSpecularColor(RocketComponent c, float alpha, float[] out) { - int shine = getShininess(c); - float m = (float) shine / 128.0f; - float d = 0.9f; - getOutsideColor(c, alpha, out); - out[0] = Math.max(out[0], d) * m; - out[1] = Math.max(out[1], d) * m; - out[2] = Math.max(out[2], d) * m; - } - - private void getInsideColor(RocketComponent c, float alpha, float[] out) { - float d = 0.4f; - getOutsideColor(c, alpha, out); - out[0] *= d; - out[1] *= d; - out[2] *= d; - } - - private HashMap, Color> defaultColorCache = new HashMap, Color>(); - private void getOutsideColor(RocketComponent c, float alpha, float[] out) { - Color col; - col = c.getColor(); - if (col == null){ - if ( defaultColorCache.containsKey(c.getClass()) ){ - col = defaultColorCache.get(c.getClass()); - } else { - col = Application.getPreferences().getDefaultColor(c.getClass()); - defaultColorCache.put(c.getClass(), col); - } - } - out[0] = Math.max(0.2f, (float) col.getRed() / 255f); - out[1] = Math.max(0.2f, (float) col.getGreen() / 255f); - out[2] = Math.max(0.2f, (float) col.getBlue() / 255f); - out[3] = alpha; + Coordinate[] position = ((RocketComponent) mount).toAbsolute(new Coordinate(((RocketComponent) mount) + .getLength() + mount.getMotorOverhang() - length)); + + for (int i = 0; i < position.length; i++) { + gl.glPushMatrix(); + gl.glTranslated(position[i].x, position[i].y, position[i].z); + renderMotor(gl, motor); + gl.glPopMatrix(); + } + } + } + + protected void renderMotor(GL2 gl, Motor motor) { + cr.getGeometry(motor, Surface.ALL).render(gl); + } + } diff --git a/core/src/net/sf/openrocket/gui/figure3d/UnfinishedRenderer.java b/core/src/net/sf/openrocket/gui/figure3d/UnfinishedRenderer.java new file mode 100644 index 000000000..5788380f8 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/figure3d/UnfinishedRenderer.java @@ -0,0 +1,24 @@ +package net.sf.openrocket.gui.figure3d; + +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.defaults.DefaultAppearance; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.RocketComponent; + +public class UnfinishedRenderer extends RealisticRenderer { + + public UnfinishedRenderer(OpenRocketDocument document) { + super(document); + } + + @Override + public boolean isDrawnTransparent(RocketComponent c) { + return c instanceof BodyTube; + } + + @Override + protected Appearance getAppearance(RocketComponent c) { + return DefaultAppearance.getDefaultAppearance(c); + } +} diff --git a/core/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java b/core/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java new file mode 100644 index 000000000..3ca25910e --- /dev/null +++ b/core/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java @@ -0,0 +1,335 @@ +package net.sf.openrocket.gui.figure3d.geometry; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.fixedfunc.GLMatrixFunc; +import javax.media.opengl.glu.GLU; +import javax.media.opengl.glu.GLUquadric; + +import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface; +import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.MassObject; +import net.sf.openrocket.rocketcomponent.RingComponent; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.rocketcomponent.Transition.Shape; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.Coordinate; + +/* + * @author Bill Kuker + */ +public class ComponentRenderer { + @SuppressWarnings("unused") + private static final LogHelper log = Application.getLogger(); + + private int LOD = 80; + + GLU glu; + GLUquadric q; + FinRenderer fr = new FinRenderer(); + + public ComponentRenderer() { + + } + + public void init(GLAutoDrawable drawable) { + glu = new GLU(); + q = glu.gluNewQuadric(); + glu.gluQuadricTexture(q, true); + } + + + public void updateFigure(GLAutoDrawable drawable) { + + } + + public Geometry getGeometry(final RocketComponent c, final Surface which) { + return new Geometry() { + @Override + public void render(GL2 gl) { + if (which == Surface.ALL) { + renderGeometry(gl, c, Surface.INSIDE); + renderGeometry(gl, c, Surface.EDGES); + renderGeometry(gl, c, Surface.OUTSIDE); + } else { + renderGeometry(gl, c, which); + } + } + }; + } + + public Geometry getGeometry(final Motor motor, Surface which) { + return new Geometry() { + @Override + public void render(GL2 gl) { + renderMotor(gl, motor); + } + }; + } + + protected void renderGeometry(GL2 gl, RocketComponent c, Surface which) { + if (glu == null) + throw new IllegalStateException(this + " Not Initialized"); + + glu.gluQuadricNormals(q, GLU.GLU_SMOOTH); + + Coordinate[] oo = c.toAbsolute(new Coordinate(0, 0, 0)); + + for (Coordinate o : oo) { + gl.glPushMatrix(); + + gl.glTranslated(o.x, o.y, o.z); + + if (c instanceof BodyTube) { + renderTube(gl, (BodyTube) c, which); + } else if (c instanceof LaunchLug) { + renderLug(gl, (LaunchLug) c, which); + } else if (c instanceof RingComponent) { + if (which == Surface.OUTSIDE) + renderRing(gl, (RingComponent) c); + } else if (c instanceof Transition) { + renderTransition(gl, (Transition) c, which); + } else if (c instanceof MassObject) { + if (which == Surface.OUTSIDE) + renderMassObject(gl, (MassObject) c); + } else if (c instanceof FinSet) { + if (which == Surface.OUTSIDE) + fr.renderFinSet(gl, (FinSet) c); + } else { + renderOther(gl, c); + } + gl.glPopMatrix(); + } + + } + + private void renderOther(GL2 gl, RocketComponent c) { + gl.glBegin(GL.GL_LINES); + for (Coordinate cc : c.getComponentBounds()) { + for (Coordinate ccc : c.getComponentBounds()) { + gl.glVertex3d(cc.x, cc.y, cc.z); + gl.glVertex3d(ccc.x, ccc.y, ccc.z); + } + } + gl.glEnd(); + } + + private void renderTransition(GL2 gl, Transition t, Surface which) { + + if (which == Surface.OUTSIDE || which == Surface.INSIDE) { + gl.glPushMatrix(); + gl.glRotated(90, 0, 1.0, 0); + if (which == Surface.INSIDE) { + gl.glFrontFace(GL.GL_CCW); + } + TransitionRenderer.drawTransition(gl, t, LOD, t.getType() == Shape.CONICAL ? 4 : LOD / 2, which == Surface.INSIDE ? -t.getThickness() : 0); + if (which == Surface.INSIDE) { + gl.glFrontFace(GL.GL_CW); + } + gl.glPopMatrix(); + } + + if (which == Surface.EDGES || which == Surface.INSIDE) { + //Render aft edge + gl.glPushMatrix(); + gl.glTranslated(t.getLength(), 0, 0); + if (which == Surface.EDGES) { + gl.glRotated(90, 0, 1.0, 0); + glu.gluDisk(q, Math.max(0, t.getAftRadius() - t.getThickness()), t.getAftRadius(), LOD, 2); + } else { + gl.glRotated(270, 0, 1.0, 0); + glu.gluDisk(q, Math.max(0, t.getAftRadius() - t.getThickness()), t.getAftRadius(), LOD, 2); + } + gl.glPopMatrix(); + + // Render AFT shoulder + if (t.getAftShoulderLength() > 0) { + gl.glPushMatrix(); + gl.glTranslated(t.getLength(), 0, 0); + double iR = (t.isFilled() || t.isAftShoulderCapped()) ? 0 : t.getAftShoulderRadius() - t.getAftShoulderThickness(); + if (which == Surface.EDGES) { + renderTube(gl, Surface.OUTSIDE, t.getAftShoulderRadius(), iR, t.getAftShoulderLength()); + renderTube(gl, Surface.EDGES, t.getAftShoulderRadius(), iR, t.getAftShoulderLength()); + gl.glPushMatrix(); + gl.glRotated(90, 0, 1.0, 0); + glu.gluDisk(q, t.getAftShoulderRadius(), t.getAftRadius(), LOD, 2); + gl.glPopMatrix(); + + } else { + renderTube(gl, Surface.INSIDE, t.getAftShoulderRadius(), iR, t.getAftShoulderLength()); + gl.glPushMatrix(); + gl.glRotated(270, 0, 1.0, 0); + glu.gluDisk(q, t.getAftShoulderRadius(), t.getAftRadius(), LOD, 2); + gl.glPopMatrix(); + } + gl.glPopMatrix(); + } + + //Render Fore edge + gl.glPushMatrix(); + gl.glRotated(180, 0, 1.0, 0); + if (which == Surface.EDGES) { + gl.glRotated(90, 0, 1.0, 0); + glu.gluDisk(q, Math.max(0, t.getForeRadius() - t.getThickness()), t.getForeRadius(), LOD, 2); + } else { + gl.glRotated(270, 0, 1.0, 0); + glu.gluDisk(q, Math.max(0, t.getForeRadius() - t.getThickness()), t.getForeRadius(), LOD, 2); + } + gl.glPopMatrix(); + + // Render Fore shoulder + if (t.getForeShoulderLength() > 0) { + gl.glPushMatrix(); + gl.glRotated(180, 0, 1.0, 0); + //gl.glTranslated(t.getLength(), 0, 0); + double iR = (t.isFilled() || t.isForeShoulderCapped()) ? 0 : t.getForeShoulderRadius() - t.getForeShoulderThickness(); + if (which == Surface.EDGES) { + renderTube(gl, Surface.OUTSIDE, t.getForeShoulderRadius(), iR, t.getForeShoulderLength()); + renderTube(gl, Surface.EDGES, t.getForeShoulderRadius(), iR, t.getForeShoulderLength()); + gl.glPushMatrix(); + gl.glRotated(90, 0, 1.0, 0); + glu.gluDisk(q, t.getForeShoulderRadius(), t.getForeRadius(), LOD, 2); + gl.glPopMatrix(); + + } else { + renderTube(gl, Surface.INSIDE, t.getForeShoulderRadius(), iR, t.getForeShoulderLength()); + gl.glPushMatrix(); + gl.glRotated(270, 0, 1.0, 0); + glu.gluDisk(q, t.getForeShoulderRadius(), t.getForeRadius(), LOD, 2); + gl.glPopMatrix(); + } + gl.glPopMatrix(); + } + + } + + } + + private void renderTube(final GL2 gl, final Surface which, final double oR, final double iR, final double len) { + gl.glPushMatrix(); + //outside + gl.glRotated(90, 0, 1.0, 0); + if (which == Surface.OUTSIDE) + glu.gluCylinder(q, oR, oR, len, LOD, 1); + + //edges + gl.glRotated(180, 0, 1.0, 0); + if (which == Surface.EDGES) + glu.gluDisk(q, iR, oR, LOD, 2); + + gl.glRotated(180, 0, 1.0, 0); + gl.glTranslated(0, 0, len); + if (which == Surface.EDGES) + glu.gluDisk(q, iR, oR, LOD, 2); + + //inside + if (which == Surface.INSIDE) { + glu.gluQuadricOrientation(q, GLU.GLU_INSIDE); + glu.gluCylinder(q, iR, iR, -len, LOD, 1); + glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE); + } + gl.glPopMatrix(); + } + + private void renderTube(GL2 gl, BodyTube t, Surface which) { + renderTube(gl, which, t.getOuterRadius(), t.getInnerRadius(), t.getLength()); + } + + private void renderRing(GL2 gl, RingComponent r) { + + gl.glRotated(90, 0, 1.0, 0); + glu.gluCylinder(q, r.getOuterRadius(), r.getOuterRadius(), + r.getLength(), LOD, 1); + + gl.glRotated(180, 0, 1.0, 0); + glu.gluDisk(q, r.getInnerRadius(), r.getOuterRadius(), LOD, 2); + + gl.glRotated(180, 0, 1.0, 0); + gl.glTranslated(0, 0, r.getLength()); + glu.gluDisk(q, r.getInnerRadius(), r.getOuterRadius(), LOD, 2); + + glu.gluQuadricOrientation(q, GLU.GLU_INSIDE); + glu.gluCylinder(q, r.getInnerRadius(), r.getInnerRadius(), + -r.getLength(), LOD, 1); + glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE); + + } + + private void renderLug(GL2 gl, LaunchLug t, Surface which) { + renderTube(gl, which, t.getOuterRadius(), t.getInnerRadius(), t.getLength()); + } + + private void renderMassObject(GL2 gl, MassObject o) { + gl.glRotated(90, 0, 1.0, 0); + + MassObjectRenderer.drawMassObject(gl, o, LOD / 2, LOD / 2); + } + + private void renderMotor(final GL2 gl, Motor motor) { + double l = motor.getLength(); + double r = motor.getDiameter() / 2; + + gl.glPushMatrix(); + + gl.glRotated(90, 0, 1.0, 0); + + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPushMatrix(); + gl.glTranslated(0, .125, 0); + gl.glScaled(1, .75, 0); + + glu.gluCylinder(q, r, r, l, LOD, 1); + + gl.glPopMatrix(); + gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + + { + final double da = (2.0f * Math.PI) / LOD; + final double dt = 1.0 / LOD; + gl.glBegin(GL.GL_TRIANGLE_STRIP); + gl.glNormal3d(0, 0, 1); + for (int i = 0; i < LOD + 1; i++) { + gl.glTexCoord2d(i * dt, .125); + gl.glVertex3d(r * Math.cos(da * i), r * Math.sin(da * i), 0); + gl.glTexCoord2d(i * dt, 0); + gl.glVertex3d(0, 0, 0); + + } + gl.glEnd(); + } + + gl.glTranslated(0, 0, l); + gl.glRotated(180, 0, 1.0, 0); + + { + final double da = (2.0f * Math.PI) / LOD; + final double dt = 1.0 / LOD; + gl.glBegin(GL.GL_TRIANGLE_STRIP); + gl.glNormal3d(0, 0, 1); + for (int i = 0; i < LOD + 1; i++) { + gl.glTexCoord2d(i * dt, .875); + gl.glVertex3d(r * Math.cos(da * i), r * Math.sin(da * i), 0); + gl.glTexCoord2d(i * dt, .9); + gl.glVertex3d(.8 * r * Math.cos(da * i), .8 * r * Math.sin(da * i), 0); + } + gl.glEnd(); + gl.glBegin(GL.GL_TRIANGLE_STRIP); + + for (int i = 0; i < LOD + 1; i++) { + gl.glNormal3d(-Math.cos(da * i), -Math.sin(da * i), 1); + gl.glTexCoord2d(i * dt, .9); + gl.glVertex3d(.8 * r * Math.cos(da * i), .8 * r * Math.sin(da * i), 0); + gl.glTexCoord2d(i * dt, 1); + gl.glVertex3d(0, 0, l * .05); + } + gl.glEnd(); + } + gl.glPopMatrix(); + } +} diff --git a/core/src/net/sf/openrocket/gui/figure3d/geometry/DisplayListComponentRenderer.java b/core/src/net/sf/openrocket/gui/figure3d/geometry/DisplayListComponentRenderer.java new file mode 100644 index 000000000..c27c81d95 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/figure3d/geometry/DisplayListComponentRenderer.java @@ -0,0 +1,78 @@ +package net.sf.openrocket.gui.figure3d.geometry; + +import java.util.HashMap; +import java.util.Map; + +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; + +import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface; +import net.sf.openrocket.rocketcomponent.RocketComponent; + +public class DisplayListComponentRenderer extends ComponentRenderer { + private Map lists = new HashMap(); + + @Override + public void updateFigure(GLAutoDrawable drawable) { + super.updateFigure(drawable); + + GL2 gl = drawable.getGL().getGL2(); + for (int i : lists.values()) { + gl.glDeleteLists(i, 1); + } + lists.clear(); + } + + @Override + protected void renderGeometry(GL2 gl, RocketComponent c, Surface which) { + Key k = new Key(c, which); + if (lists.containsKey(k)) { + gl.glCallList(lists.get(k)); + } else { + int list = gl.glGenLists(1); + gl.glNewList(list, GL2.GL_COMPILE_AND_EXECUTE); + super.renderGeometry(gl, c, which); + gl.glEndList(); + lists.put(k, list); + } + } + + private static class Key { + final RocketComponent c; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((c == null) ? 0 : c.hashCode()); + result = prime * result + ((which == null) ? 0 : which.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Key other = (Key) obj; + if (c == null) { + if (other.c != null) + return false; + } else if (!c.equals(other.c)) + return false; + if (which != other.which) + return false; + return true; + } + + final Surface which; + + Key(final RocketComponent c, final Surface which) { + this.c = c; + this.which = which; + } + } +} diff --git a/core/src/net/sf/openrocket/gui/figure3d/geometry/FinRenderer.java b/core/src/net/sf/openrocket/gui/figure3d/geometry/FinRenderer.java new file mode 100644 index 000000000..497e1a233 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/figure3d/geometry/FinRenderer.java @@ -0,0 +1,375 @@ +package net.sf.openrocket.gui.figure3d.geometry; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.fixedfunc.GLMatrixFunc; +import javax.media.opengl.glu.GLU; +import javax.media.opengl.glu.GLUtessellator; +import javax.media.opengl.glu.GLUtessellatorCallback; +import javax.media.opengl.glu.GLUtessellatorCallbackAdapter; + +import net.sf.openrocket.rocketcomponent.EllipticalFinSet; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.FinSet.CrossSection; +import net.sf.openrocket.util.Coordinate; + +public class FinRenderer { + + private GLUtessellator tobj = GLU.gluNewTess(); + + private void preTess(final GL2 gl) { + GLUtessellatorCallback cb = new GLUtessellatorCallbackAdapter() { + @Override + public void vertex(Object vertexData) { + double d[] = (double[]) vertexData; + gl.glTexCoord2d(d[0], d[1]); + gl.glVertex3dv(d, 0); + } + + @Override + public void begin(int type) { + gl.glBegin(type); + } + + @Override + public void end() { + gl.glEnd(); + } + }; + + GLU.gluTessCallback(tobj, GLU.GLU_TESS_VERTEX, cb); + GLU.gluTessCallback(tobj, GLU.GLU_TESS_BEGIN, cb); + GLU.gluTessCallback(tobj, GLU.GLU_TESS_END, cb); + } + + private void oneFace(final GL2 gl, final Coordinate finPoints[], final FinSet fs) { + GLU.gluTessBeginPolygon(tobj, null); + GLU.gluTessBeginContour(tobj); + gl.glNormal3f(0, 0, -1); + for (int i = 0; i < finPoints.length; i++) { + Coordinate c = finPoints[i]; + double[] p = new double[] { c.x, c.y + fs.getBodyRadius(), + c.z }; + GLU.gluTessVertex(tobj, p, 0, p); + + } + GLU.gluTessEndContour(tobj); + GLU.gluTessEndPolygon(tobj); + } + + private void edgeStrip(final GL2 gl, final Coordinate finPoints[], final Coordinate insetPoints[], final FinSet fs) { + + //Render each face as a separate QUAD (or two triangles) so that + //normals can be controlled per vertex & per face + for (int i = 0; i <= finPoints.length; i++) { + //The index of the first fin point to use in the quad + final int i1 = i % finPoints.length; + //The index of the second fin point to use in the quad + final int i2 = (i - 1 + finPoints.length) + % finPoints.length; + + //the 'i'nner and 'o'uter coordinates of points 1 & 2 + final Coordinate ic1 = insetPoints[i1].add(0, fs.getBodyRadius(), -fs.getThickness() / 2.0); + final Coordinate ic2 = insetPoints[i2].add(0, fs.getBodyRadius(), -fs.getThickness() / 2.0); + final Coordinate oc1 = finPoints[i1].add(0, fs.getBodyRadius(), 0); + final Coordinate oc2 = finPoints[i2].add(0, fs.getBodyRadius(), 0); + + //Base normal for fin point 1, inner & outer + final Coordinate n1 = ic1.sub(oc1).cross(oc2.sub(oc1)).normalize(); + + //Base normal for second fin point is the same + Coordinate n2 = n1; + + //Unless we want fin to look smooth then use the third fin point + //to get the normal for the next edge segment. + if (fs instanceof EllipticalFinSet) { + final int i3 = (i - 2 + finPoints.length) + % finPoints.length; + Coordinate oc3 = finPoints[i3].add(0, fs.getBodyRadius(), 0); + n2 = ic2.sub(oc2).cross(oc3.sub(oc2)).normalize(); + } + + Coordinate in1; + Coordinate on1; + Coordinate in2; + Coordinate on2; + + //Set up the normals based on fin type + if (fs.getCrossSection() == CrossSection.ROUNDED) { + in1 = in2 = new Coordinate(0, 0, -1); + on1 = n1.setZ(0).normalize(); + on2 = n2.setZ(0).normalize(); + } else if (fs.getCrossSection() == CrossSection.AIRFOIL) { + in1 = in2 = new Coordinate(0, 0, -1); + double x = n1.x; + x = Math.max(x, 0); + double z = n1.z * x; + on1 = n1.setZ(z).normalize(); + on2 = n2.setZ(z).normalize(); + } else { + //Square. Will also render Wedge if that ever happens + in1 = n1; + in2 = n2; + on1 = n1; + on2 = n2; + } + + gl.glBegin(GL.GL_TRIANGLE_STRIP); + + gl.glNormal3d(in2.x, in2.y, in2.z); + gl.glTexCoord2d(ic2.x, ic2.y); + gl.glVertex3d(ic2.x, ic2.y, ic2.z); + + gl.glNormal3d(on2.x, on2.y, on2.z); + gl.glTexCoord2d(oc2.x, oc2.y); + gl.glVertex3d(oc2.x, oc2.y, oc2.z); + + gl.glNormal3d(in1.x, in1.y, in1.z); + gl.glTexCoord2d(ic1.x, ic1.y); + gl.glVertex3d(ic1.x, ic1.y, ic1.z); + + gl.glNormal3d(on1.x, on1.y, on1.z); + gl.glTexCoord2d(oc1.x, oc1.y); + gl.glVertex3d(oc1.x, oc1.y, oc1.z); + + gl.glEnd(); + } + + + } + + void renderFinSet(final GL2 gl, FinSet fs) { + + Coordinate finPoints[] = fs.getFinPointsWithTab(); + Coordinate insetPoints[]; + + double loa, hoa; + { //Scale texture & calculate overall length + double minX = Double.MAX_VALUE; + double minY = Double.MAX_VALUE; + double maxX = Double.MIN_VALUE; + double maxY = Double.MIN_VALUE; + + for (int i = 0; i < finPoints.length; i++) { + Coordinate c = finPoints[i]; + minX = Math.min(c.x, minX); + minY = Math.min(c.y, minY); + maxX = Math.max(c.x, maxX); + maxY = Math.max(c.y, maxY); + } + + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPushMatrix(); + gl.glScaled(1 / (maxX - minX), 1 / (maxY - minY), 0); + gl.glTranslated(-minX, -minY - fs.getBodyRadius(), 0); + gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + + loa = maxX - minX; + hoa = maxY - minY; + } + + //Calculate the inset points for a fin + if (fs.getCrossSection() == CrossSection.SQUARE || fs.getThickness() == 0) { + //unchanged if square + insetPoints = finPoints; + } else { + //Otherwise inset the polygon + insetPoints = new Coordinate[finPoints.length]; + System.arraycopy(finPoints, 0, insetPoints, 0, finPoints.length); + double inset = Math.min(loa, hoa) / 40.0; + insetPolygon(insetPoints, inset); + } + + //This is the rotation of the whole finset around the body + gl.glRotated(fs.getBaseRotation() * (180.0 / Math.PI), 1, 0, 0); + + for (int fin = 0; fin < fs.getFinCount(); fin++) { + + gl.glPushMatrix(); + + //Cant this fin around its center + gl.glTranslated(fs.getLength() / 2, 0, 0); + gl.glRotated(fs.getCantAngle() * (180.0 / Math.PI), 0, 1, 0); + gl.glTranslated(-fs.getLength() / 2, 0, 0); + + preTess(gl); + + //Draw one side + gl.glPushMatrix(); + gl.glTranslated(0, 0, -fs.getThickness() / 2.0); + oneFace(gl, insetPoints, fs); + gl.glPopMatrix(); + + if (fs.getThickness() > 0) + edgeStrip(gl, finPoints, insetPoints, fs); + + //Draw the other side + gl.glPushMatrix(); + gl.glScalef(1, 1, -1); + { + gl.glFrontFace(GL.GL_CCW); + gl.glPushMatrix(); + gl.glTranslated(0, 0, -fs.getThickness() / 2.0); + oneFace(gl, insetPoints, fs); + gl.glPopMatrix(); + if (fs.getThickness() > 0) + edgeStrip(gl, finPoints, insetPoints, fs); + gl.glFrontFace(GL.GL_CW); + } + gl.glPopMatrix(); + + gl.glPopMatrix(); + + gl.glRotated(360.0 / fs.getFinCount(), 1, 0, 0); + } + + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPopMatrix(); + gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + + } + + // Based on public-domain code by Darel Rex Finley, 2007 + // See diagrams at http://alienryderflex.com/polygon_inset + private void insetPolygon(Coordinate[] C, double insetDist) { + + double startX = C[0].x, startY = C[0].y, a, b, c, d, e, f; + int i; + final int corners = C.length; + // Polygon must have at least three corners to be inset. + if (corners < 3) + return; + + // Inset the polygon. + c = C[corners - 1].x; + d = C[corners - 1].y; + e = C[0].x; + f = C[0].y; + for (i = 0; i < corners - 1; i++) { + a = c; + b = d; + c = e; + d = f; + e = C[i + 1].x; + f = C[i + 1].y; + C[i] = insetCorner(a, b, c, d, e, f, C[i], insetDist); + } + C[i] = insetCorner(c, d, e, f, startX, startY, C[i], insetDist); + } + + + + // Given the sequentially connected points (a,b), (c,d), and (e,f), this + // function returns, in (C,D), a bevel-inset replacement for point (c,d). + // + // Note: If vectors (a,b)->(c,d) and (c,d)->(e,f) are exactly 180° opposed, + // or if either segment is zero-length, this function will do + // nothing; i.e. point (C,D) will not be set. + private Coordinate insetCorner( + double a, double b, // previous point + double c, double d, // current point that needs to be inset + double e, double f, // next point + Coordinate old, // storage location for new, inset point + double insetDist) { // amount of inset (perpendicular to each line segment) + + double c1 = c, d1 = d, c2 = c, d2 = d, dx1, dy1, dist1, dx2, dy2, dist2, insetX, insetY; + + // Calculate length of line segments. + dx1 = c - a; + dy1 = d - b; + dist1 = Math.sqrt(dx1 * dx1 + dy1 * dy1); + + dx2 = e - c; + dy2 = f - d; + dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2); + + // Exit if either segment is zero-length. + if (dist1 == 0. || dist2 == 0.) + return old; + + //Leave lines in the tab or along the bottom not inset. + //This is OpenRocket fin-specific, remove for general poly inset. + boolean intab = false; //b < 0 || d < 0; + boolean inset1 = !intab && (Math.abs(dy1) > 0.0001f || b > 0); + boolean inset2 = !intab && (Math.abs(dy2) > 0.0001f || d > 0); + + // Inset each of the two line segments. + if (inset1) { + insetX = dy1 / dist1 * insetDist; + a += insetX; + c1 += insetX; + insetY = -dx1 / dist1 * insetDist; + b += insetY; + d1 += insetY; + } + + if (inset2) { + insetX = dy2 / dist2 * insetDist; + e += insetX; + c2 += insetX; + insetY = -dx2 / dist2 * insetDist; + f += insetY; + d2 += insetY; + } + + // If inset segments connect perfectly, return the connection point. + if (c1 == c2 && d1 == d2) { + return new Coordinate(c1, d1, 0); + + } + + // Return the intersection point of the two inset segments (if any). + Coordinate intersection = lineIntersection(a, b, c1, d1, c2, d2, e, f); + if (intersection != null) { + return new Coordinate(intersection.x, intersection.y, 0); + } + + return old; + } + + private Coordinate lineIntersection( + double Ax, double Ay, + double Bx, double By, + double Cx, double Cy, + double Dx, double Dy) { + + double distAB, theCos, theSin, newX, ABpos; + + // Fail if either line is undefined. + if (Ax == Bx && Ay == By || Cx == Dx && Cy == Dy) + return null; + + // (1) Translate the system so that point A is on the origin. + Bx -= Ax; + By -= Ay; + Cx -= Ax; + Cy -= Ay; + Dx -= Ax; + Dy -= Ay; + + // Discover the length of segment A-B. + distAB = Math.sqrt(Bx * Bx + By * By); + + // (2) Rotate the system so that point B is on the positive X axis. + theCos = Bx / distAB; + theSin = By / distAB; + newX = Cx * theCos + Cy * theSin; + Cy = Cy * theCos - Cx * theSin; + Cx = newX; + newX = Dx * theCos + Dy * theSin; + Dy = Dy * theCos - Dx * theSin; + Dx = newX; + + // Fail if the lines are parallel. + if (Cy == Dy) + return null; + + // (3) Discover the position of the intersection point along line A-B. + ABpos = Dx + (Cx - Dx) * Dy / (Dy - Cy); + + // (4) Apply the discovered position to line A-B in the original coordinate system. + + return new Coordinate(Ax + ABpos * theCos, Ay + ABpos * theSin, 0); + } + +} diff --git a/core/src/net/sf/openrocket/gui/figure3d/geometry/Geometry.java b/core/src/net/sf/openrocket/gui/figure3d/geometry/Geometry.java new file mode 100644 index 000000000..f67e22462 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/figure3d/geometry/Geometry.java @@ -0,0 +1,11 @@ +package net.sf.openrocket.gui.figure3d.geometry; + +import javax.media.opengl.GL2; + +public interface Geometry { + public static enum Surface { + ALL, OUTSIDE, INSIDE, EDGES; + } + + public void render(GL2 gl); +} diff --git a/core/src/net/sf/openrocket/gui/figure3d/MassObjectRenderer.java b/core/src/net/sf/openrocket/gui/figure3d/geometry/MassObjectRenderer.java similarity index 87% rename from core/src/net/sf/openrocket/gui/figure3d/MassObjectRenderer.java rename to core/src/net/sf/openrocket/gui/figure3d/geometry/MassObjectRenderer.java index 4c1d93443..073212f88 100644 --- a/core/src/net/sf/openrocket/gui/figure3d/MassObjectRenderer.java +++ b/core/src/net/sf/openrocket/gui/figure3d/geometry/MassObjectRenderer.java @@ -112,31 +112,29 @@ * in the design, construction, operation or maintenance of any nuclear * facility. */ -package net.sf.openrocket.gui.figure3d; +package net.sf.openrocket.gui.figure3d.geometry; import javax.media.opengl.GL; import javax.media.opengl.GL2; import net.sf.openrocket.rocketcomponent.MassObject; -public final class MassObjectRenderer { +final class MassObjectRenderer { private static final boolean textureFlag = true; - + private MassObjectRenderer() { } - - public static final void drawMassObject(final GL2 gl, final MassObject o, + + static final void drawMassObject(final GL2 gl, final MassObject o, final int slices, final int stacks) { - + double da, r, dz; - double x, y, z, nz, nsign; + double x, y, z; int i, j; - - nsign = 1.0f; - + da = 2.0f * PI / slices; dz = o.getLength() / stacks; - + double ds = 1.0f / slices; double dt = 1.0f / stacks; double t = 0.0f; @@ -144,15 +142,10 @@ public final class MassObjectRenderer { for (j = 0; j < stacks; j++) { r = getRadius(o, z); double rNext = getRadius(o, z + dz); + if (j == stacks - 1) rNext = 0; - - if (j == stacks - 1) - rNext = 0; - - // Z component of normal vectors - nz = -(rNext - r) / dz; - + double s = 0.0f; glBegin(gl, GL2.GL_QUAD_STRIP); for (i = 0; i <= slices; i++) { @@ -163,21 +156,21 @@ public final class MassObjectRenderer { x = sin((i * da)); y = cos((i * da)); } - if (nsign == 1.0f) { - normal3d(gl, (x * nsign), (y * nsign), (nz * nsign)); - TXTR_COORD(gl, s, t); - glVertex3d(gl, (x * r), (y * r), z); - normal3d(gl, (x * nsign), (y * nsign), (nz * nsign)); - TXTR_COORD(gl, s, t + dt); - glVertex3d(gl, (x * rNext), (y * rNext), (z + dz)); - } else { - normal3d(gl, x * nsign, y * nsign, nz * nsign); - TXTR_COORD(gl, s, t); - glVertex3d(gl, (x * r), (y * r), z); - normal3d(gl, x * nsign, y * nsign, nz * nsign); - TXTR_COORD(gl, s, t + dt); - glVertex3d(gl, (x * rNext), (y * rNext), (z + dz)); - } + + if (r == 0) + normal3d(gl, 0, 0, 1); + else + normal3d(gl, x, y, z); + TXTR_COORD(gl, s, t); + glVertex3d(gl, (x * r), (y * r), z); + + if (rNext == 0) + normal3d(gl, 0, 0, -1); + else + normal3d(gl, x, y, z); + TXTR_COORD(gl, s, t + dt); + glVertex3d(gl, (x * rNext), (y * rNext), (z + dz)); + s += ds; } // for slices glEnd(gl); @@ -186,7 +179,7 @@ public final class MassObjectRenderer { z += dz; } // for stacks } - + private static final double getRadius(MassObject o, double z) { double arc = Math.min(o.getLength(), 2 * o.getRadius()) * 0.35f; double r = o.getRadius(); @@ -202,33 +195,33 @@ public final class MassObjectRenderer { } return o.getRadius(); } - + // ---------------------------------------------------------------------- // Internals only below this point // - + private static final double PI = Math.PI; - + private static final void glBegin(GL gl, int mode) { gl.getGL2().glBegin(mode); } - + private static final void glEnd(GL gl) { gl.getGL2().glEnd(); } - + private static final void glVertex3d(GL gl, double x, double y, double z) { gl.getGL2().glVertex3d(x, y, z); } - + private static final void glNormal3d(GL gl, double x, double y, double z) { gl.getGL2().glNormal3d(x, y, z); } - + private static final void glTexCoord2d(GL gl, double x, double y) { gl.getGL2().glTexCoord2d(x, y); } - + /** * Call glNormal3f after scaling normal to unit length. * @@ -238,7 +231,7 @@ public final class MassObjectRenderer { */ private static final void normal3d(GL gl, double x, double y, double z) { double mag; - + mag = Math.sqrt(x * x + y * y + z * z); if (mag > 0.00001F) { x /= mag; @@ -247,16 +240,16 @@ public final class MassObjectRenderer { } glNormal3d(gl, x, y, z); } - + private static final void TXTR_COORD(GL gl, double x, double y) { if (textureFlag) glTexCoord2d(gl, x, y); } - + private static final double sin(double r) { return Math.sin(r); } - + private static final double cos(double r) { return Math.cos(r); } diff --git a/core/src/net/sf/openrocket/gui/figure3d/TransitionRenderer.java b/core/src/net/sf/openrocket/gui/figure3d/geometry/TransitionRenderer.java similarity index 83% rename from core/src/net/sf/openrocket/gui/figure3d/TransitionRenderer.java rename to core/src/net/sf/openrocket/gui/figure3d/geometry/TransitionRenderer.java index 3789a7809..1dd093676 100644 --- a/core/src/net/sf/openrocket/gui/figure3d/TransitionRenderer.java +++ b/core/src/net/sf/openrocket/gui/figure3d/geometry/TransitionRenderer.java @@ -112,46 +112,46 @@ * in the design, construction, operation or maintenance of any nuclear * facility. */ -package net.sf.openrocket.gui.figure3d; +package net.sf.openrocket.gui.figure3d.geometry; import javax.media.opengl.GL; import javax.media.opengl.GL2; import net.sf.openrocket.rocketcomponent.Transition; -public final class TransitionRenderer { +final class TransitionRenderer { private static final boolean textureFlag = true; - + private TransitionRenderer() { } - - public static final void drawTransition(final GL2 gl, final Transition tr, - final int slices, final int stacks) { - - double da, r, dz; - double x, y, z, nz, nsign; - int i, j; - - nsign = 1.0f; - + + static final void drawTransition(final GL2 gl, final Transition tr, + final int slices, final int stacks, final double offsetRadius) { + + double da, r, dzBase; + double x, y, z, nz, lnz = 0; + int i; + da = 2.0f * PI / slices; - dz = (double) tr.getLength() / stacks; - + dzBase = (double) tr.getLength() / stacks; + double ds = 1.0f / slices; - double dt = 1.0f / stacks; - double t = 0.0f; + z = 0.0f; r = (double) tr.getForeRadius(); - for (j = 0; j < stacks; j++) { - r = (double) tr.getRadius(z); - double rNext = (double) tr.getRadius(z + dz); - - if (j == stacks - 1) - rNext = (double) tr.getRadius(tr.getLength()); - + while (z < tr.getLength()) { + double t = z / tr.getLength(); + + double dz = t < 0.025 ? dzBase / 8.0 : dzBase; + double zNext = Math.min(z + dz, tr.getLength()); + + r = Math.max(0, tr.getRadius(z) + offsetRadius); + double rNext = Math.max(0, tr.getRadius(zNext) + offsetRadius); + + // Z component of normal vectors - nz = -(rNext - r) / dz; - + nz = (r - rNext) / dz; + double s = 0.0f; glBegin(gl, GL2.GL_QUAD_STRIP); for (i = 0; i <= slices; i++) { @@ -162,57 +162,66 @@ public final class TransitionRenderer { x = sin((i * da)); y = cos((i * da)); } - if (nsign == 1.0f) { - normal3d(gl, (x * nsign), (y * nsign), (nz * nsign)); - TXTR_COORD(gl, s, t); - glVertex3d(gl, (x * r), (y * r), z); - normal3d(gl, (x * nsign), (y * nsign), (nz * nsign)); - TXTR_COORD(gl, s, t + dt); - glVertex3d(gl, (x * rNext), (y * rNext), (z + dz)); + + if (r == 0) { + switch (tr.getType()) { + case CONICAL: + case OGIVE: + case PARABOLIC: + normal3d(gl, x, y, nz); + break; + case ELLIPSOID: + case POWER: + case HAACK: + normal3d(gl, 0, 0, -1); + break; + } + } else { - normal3d(gl, x * nsign, y * nsign, nz * nsign); - TXTR_COORD(gl, s, t); - glVertex3d(gl, (x * r), (y * r), z); - normal3d(gl, x * nsign, y * nsign, nz * nsign); - TXTR_COORD(gl, s, t + dt); - glVertex3d(gl, (x * rNext), (y * rNext), (z + dz)); + normal3d(gl, x, y, lnz); } + TXTR_COORD(gl, s, z / tr.getLength()); + glVertex3d(gl, (x * r), (y * r), z); + + normal3d(gl, x, y, nz); + TXTR_COORD(gl, s, zNext / tr.getLength()); + glVertex3d(gl, (x * rNext), (y * rNext), zNext); + s += ds; } // for slices glEnd(gl); - // r += dr; - t += dt; - z += dz; + lnz = nz; + z = Math.min(z + dz, tr.getLength()); } // for stacks - + } - + // ---------------------------------------------------------------------- // Internals only below this point // - + private static final double PI = (double) Math.PI; - + private static final void glBegin(GL gl, int mode) { gl.getGL2().glBegin(mode); } - + private static final void glEnd(GL gl) { gl.getGL2().glEnd(); } - + private static final void glVertex3d(GL gl, double x, double y, double z) { gl.getGL2().glVertex3d(x, y, z); } - + private static final void glNormal3d(GL gl, double x, double y, double z) { gl.getGL2().glNormal3d(x, y, z); } - + private static final void glTexCoord2d(GL gl, double x, double y) { gl.getGL2().glTexCoord2d(x, y); } - + /** * Call glNormal3f after scaling normal to unit length. * @@ -222,7 +231,7 @@ public final class TransitionRenderer { */ private static final void normal3d(GL gl, double x, double y, double z) { double mag; - + mag = (double) Math.sqrt(x * x + y * y + z * z); if (mag > 0.00001F) { x /= mag; @@ -231,16 +240,16 @@ public final class TransitionRenderer { } glNormal3d(gl, x, y, z); } - + private static final void TXTR_COORD(GL gl, double x, double y) { if (textureFlag) glTexCoord2d(gl, x, y); } - + private static final double sin(double r) { return (double) Math.sin(r); } - + private static final double cos(double r) { return (double) Math.cos(r); } diff --git a/core/src/net/sf/openrocket/gui/main/BasicFrame.java b/core/src/net/sf/openrocket/gui/main/BasicFrame.java index df5256c84..0609ec8c6 100644 --- a/core/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/core/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -15,7 +15,6 @@ import java.awt.event.WindowEvent; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -61,12 +60,11 @@ import javax.swing.tree.TreeSelectionModel; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.file.GeneralRocketLoader; +import net.sf.openrocket.document.OpenRocketDocumentFactory; +import net.sf.openrocket.document.StorageOptions; +import net.sf.openrocket.file.GeneralRocketSaver; import net.sf.openrocket.file.RocketLoadException; -import net.sf.openrocket.file.RocketLoader; -import net.sf.openrocket.file.RocketSaver; -import net.sf.openrocket.file.openrocket.OpenRocketSaver; -import net.sf.openrocket.file.rocksim.export.RocksimSaver; +import net.sf.openrocket.gui.ExportDecalDialog; import net.sf.openrocket.gui.StorageOptionChooser; import net.sf.openrocket.gui.configdialog.ComponentConfigDialog; import net.sf.openrocket.gui.customexpression.CustomExpressionDialog; @@ -75,7 +73,6 @@ import net.sf.openrocket.gui.dialogs.BugReportDialog; import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog; import net.sf.openrocket.gui.dialogs.DebugLogDialog; import net.sf.openrocket.gui.dialogs.DetailDialog; -import net.sf.openrocket.gui.dialogs.ExampleDesignDialog; import net.sf.openrocket.gui.dialogs.LicenseDialog; import net.sf.openrocket.gui.dialogs.PrintDialog; import net.sf.openrocket.gui.dialogs.ScaleDialog; @@ -99,7 +96,6 @@ import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.Stage; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.MemoryManagement; @@ -110,15 +106,12 @@ import net.sf.openrocket.util.TestRockets; public class BasicFrame extends JFrame { private static final LogHelper log = Application.getLogger(); - /** - * The RocketLoader instance used for loading all rocket designs. - */ - private static final RocketLoader ROCKET_LOADER = new GeneralRocketLoader(); - - private static final RocketSaver ROCKET_SAVER = new OpenRocketSaver(); + private static final GeneralRocketSaver ROCKET_SAVER = new GeneralRocketSaver(); private static final Translator trans = Application.getTranslator(); + private static final int SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + public static final int COMPONENT_TAB = 0; public static final int SIMULATION_TAB = 1; @@ -273,13 +266,13 @@ public class BasicFrame extends JFrame { // Remove JTree key events that interfere with menu accelerators InputMap im = SwingUtilities.getUIInputMap(tree, JComponent.WHEN_FOCUSED); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, ActionEvent.CTRL_MASK), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.CTRL_MASK), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, SHORTCUT_KEY), null); @@ -399,7 +392,7 @@ public class BasicFrame extends JFrame { //// New item = new JMenuItem(trans.get("main.menu.file.new"), KeyEvent.VK_N); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK)); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, SHORTCUT_KEY)); item.setMnemonic(KeyEvent.VK_N); //// Create a new rocket design item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.new.desc")); @@ -416,7 +409,7 @@ public class BasicFrame extends JFrame { //// Open... item = new JMenuItem(trans.get("main.menu.file.open"), KeyEvent.VK_O); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK)); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, SHORTCUT_KEY)); //// Open a rocket design item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Openrocketdesign")); item.setIcon(Icons.FILE_OPEN); @@ -431,38 +424,21 @@ public class BasicFrame extends JFrame { //// Open Recent... item = new MRUDesignFileAction(trans.get("main.menu.file.openRecent"), this); - //// Open a recent rocket design item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Openrecentrocketdesign")); item.setIcon(Icons.FILE_OPEN); menu.add(item); //// Open example... - item = new JMenuItem(trans.get("main.menu.file.openExample")); - //// Open an example rocket design + item = new ExampleDesignFileAction(trans.get("main.menu.file.openExample"), this); item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Openexamplerocketdesign")); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, - ActionEvent.CTRL_MASK | ActionEvent.SHIFT_MASK)); item.setIcon(Icons.FILE_OPEN_EXAMPLE); - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - log.user("Open example... selected"); - URL[] urls = ExampleDesignDialog.selectExampleDesigns(BasicFrame.this); - if (urls != null) { - for (URL u : urls) { - log.user("Opening example " + u); - open(u, BasicFrame.this); - } - } - } - }); menu.add(item); menu.addSeparator(); //// Save item = new JMenuItem(trans.get("main.menu.file.save"), KeyEvent.VK_S); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK)); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_KEY)); //// Save the current rocket design item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.SavecurRocketdesign")); item.setIcon(Icons.FILE_SAVE); @@ -478,7 +454,7 @@ public class BasicFrame extends JFrame { //// Save as... item = new JMenuItem(trans.get("main.menu.file.saveAs"), KeyEvent.VK_A); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, - ActionEvent.CTRL_MASK | ActionEvent.SHIFT_MASK)); + SHORTCUT_KEY | ActionEvent.SHIFT_MASK)); //// Save the current rocket design to a new file item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.SavecurRocketdesnewfile")); item.setIcon(Icons.FILE_SAVE_AS); @@ -491,9 +467,22 @@ public class BasicFrame extends JFrame { }); menu.add(item); + + //// Export decal... + item = new JMenuItem(trans.get("main.menu.file.exportDecal")); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.exportDecal.desc")); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportDecalAction(); + } + }); + menu.add(item); + + //// Print... item = new JMenuItem(trans.get("main.menu.file.print"), KeyEvent.VK_P); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.CTRL_MASK)); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, SHORTCUT_KEY)); //// Print parts list and fin template item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.print.desc")); item.setIcon(Icons.FILE_PRINT); @@ -511,7 +500,7 @@ public class BasicFrame extends JFrame { //// Close item = new JMenuItem(trans.get("main.menu.file.close"), KeyEvent.VK_C); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, ActionEvent.CTRL_MASK)); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, SHORTCUT_KEY)); //// Close the current rocket design item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Closedesign")); item.setIcon(Icons.FILE_CLOSE); @@ -528,7 +517,7 @@ public class BasicFrame extends JFrame { //// Quit item = new JMenuItem(trans.get("main.menu.file.quit"), KeyEvent.VK_Q); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK)); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, SHORTCUT_KEY)); //// Quit the program item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Quitprogram")); item.setIcon(Icons.FILE_QUIT); @@ -553,7 +542,7 @@ public class BasicFrame extends JFrame { Action action = UndoRedoAction.newUndoAction(document); item = new JMenuItem(action); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK)); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, SHORTCUT_KEY)); item.setMnemonic(KeyEvent.VK_U); //// Undo the previous operation item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.undo.desc")); @@ -562,7 +551,7 @@ public class BasicFrame extends JFrame { action = UndoRedoAction.newRedoAction(document); item = new JMenuItem(action); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, ActionEvent.CTRL_MASK)); + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, SHORTCUT_KEY)); item.setMnemonic(KeyEvent.VK_R); //// Redo the previously undone operation item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.redo.desc")); @@ -618,7 +607,6 @@ public class BasicFrame extends JFrame { menu.add(item); //// Edit Component Preset File - if (System.getProperty("openrocket.preseteditor.menu") != null) { item = new JMenuItem(trans.get("main.menu.edit.editpreset")); item.addActionListener(new ActionListener() { @@ -634,6 +622,7 @@ public class BasicFrame extends JFrame { menu.add(item); } + //// Analyze menu = new JMenu(trans.get("main.menu.analyze")); menu.setMnemonic(KeyEvent.VK_A); @@ -827,7 +816,7 @@ public class BasicFrame extends JFrame { return; } - OpenRocketDocument doc = new OpenRocketDocument(r); + OpenRocketDocument doc = OpenRocketDocumentFactory.createDocumentFromRocket(r); doc.setSaved(true); BasicFrame frame = new BasicFrame(doc); frame.setVisible(true); @@ -843,7 +832,7 @@ public class BasicFrame extends JFrame { public void actionPerformed(ActionEvent e) { log.user("Create Iso-Haisu selected"); Rocket r = TestRockets.makeIsoHaisu(); - OpenRocketDocument doc = new OpenRocketDocument(r); + OpenRocketDocument doc = OpenRocketDocumentFactory.createDocumentFromRocket(r); doc.setSaved(true); BasicFrame frame = new BasicFrame(doc); frame.setVisible(true); @@ -858,7 +847,7 @@ public class BasicFrame extends JFrame { public void actionPerformed(ActionEvent e) { log.user("Create Big Blue selected"); Rocket r = TestRockets.makeBigBlue(); - OpenRocketDocument doc = new OpenRocketDocument(r); + OpenRocketDocument doc = OpenRocketDocumentFactory.createDocumentFromRocket(r); doc.setSaved(true); BasicFrame frame = new BasicFrame(doc); frame.setVisible(true); @@ -1083,65 +1072,40 @@ public class BasicFrame extends JFrame { * @param parent the parent window for dialogs. * @return true if opened successfully. */ - private static boolean open(URL url, BasicFrame parent) { - String filename = null; - + public static void open(URL url, BasicFrame parent) { + String displayName = null; // First figure out the file name from the URL // Try using URI.getPath(); try { URI uri = url.toURI(); - filename = uri.getPath(); + displayName = uri.getPath(); } catch (URISyntaxException ignore) { } // Try URL-decoding the URL - if (filename == null) { + if (displayName == null) { try { - filename = URLDecoder.decode(url.toString(), "UTF-8"); + displayName = URLDecoder.decode(url.toString(), "UTF-8"); } catch (UnsupportedEncodingException ignore) { } } - // Last resort - if (filename == null) { - filename = ""; + if (displayName == null) { + displayName = ""; } // Remove path from filename - if (filename.lastIndexOf('/') >= 0) { - filename = filename.substring(filename.lastIndexOf('/') + 1); + if (displayName.lastIndexOf('/') >= 0) { + displayName = displayName.substring(displayName.lastIndexOf('/') + 1); } // Open the file - log.info("Opening file from url=" + url + " filename=" + filename); - try { - InputStream is = url.openStream(); - open(is, filename, parent); - } catch (IOException e) { - log.warn("Error opening file" + e); - JOptionPane.showMessageDialog(parent, - "An error occurred while opening the file " + filename, - "Error loading file", JOptionPane.ERROR_MESSAGE); - } + log.info("Opening file from url=" + url + " filename=" + displayName); - return false; - } - - - /** - * Open the specified file from an InputStream in a new design frame. If an error - * occurs, an error dialog is shown and false is returned. - * - * @param stream the stream to load from. - * @param filename the file name to display in dialogs (not set to the document). - * @param parent the parent component for which a progress dialog is opened. - * @return whether the file was successfully loaded and opened. - */ - private static boolean open(InputStream stream, String filename, Window parent) { - OpenFileWorker worker = new OpenFileWorker(stream, ROCKET_LOADER); - return open(worker, filename, null, parent); + OpenFileWorker worker = new OpenFileWorker(url); + open(worker, displayName, parent, true); } @@ -1154,8 +1118,8 @@ public class BasicFrame extends JFrame { * @return whether the file was successfully loaded and opened. */ public static boolean open(File file, Window parent) { - OpenFileWorker worker = new OpenFileWorker(file, ROCKET_LOADER); - return open(worker, file.getName(), file, parent); + OpenFileWorker worker = new OpenFileWorker(file); + return open(worker, file.getName(), parent, false); } @@ -1163,16 +1127,16 @@ public class BasicFrame extends JFrame { * Open the specified file using the provided worker. * * @param worker the OpenFileWorker that loads the file. - * @param filename the file name to display in dialogs. + * @param displayName the file name to display in dialogs. * @param file the File to set the document to (may be null). * @param parent + * @param openRocketConfigDialog if true, will open the configuration dialog of the rocket. This is useful for examples. * @return */ - private static boolean open(OpenFileWorker worker, String filename, File file, Window parent) { - + private static boolean open(OpenFileWorker worker, String displayName, Window parent, boolean openRocketConfigDialog) { // Open the file in a Swing worker thread log.info("Starting OpenFileWorker"); - if (!SwingWorkerDialog.runWorker(parent, "Opening file", "Reading " + filename + "...", worker)) { + if (!SwingWorkerDialog.runWorker(parent, "Opening file", "Reading " + displayName + "...", worker)) { // User cancelled the operation log.info("User cancelled the OpenFileWorker"); return false; @@ -1193,7 +1157,7 @@ public class BasicFrame extends JFrame { log.warn("File not found", cause); JOptionPane.showMessageDialog(parent, - "File not found: " + filename, + "File not found: " + displayName, "Error opening file", JOptionPane.ERROR_MESSAGE); return false; @@ -1201,7 +1165,7 @@ public class BasicFrame extends JFrame { log.warn("Error loading the file", cause); JOptionPane.showMessageDialog(parent, - "Unable to open file '" + filename + "': " + "Unable to open file '" + displayName + "': " + cause.getMessage(), "Error opening file", JOptionPane.ERROR_MESSAGE); return false; @@ -1228,7 +1192,7 @@ public class BasicFrame extends JFrame { WarningDialog.showWarnings(parent, new Object[] { //// The following problems were encountered while opening - trans.get("BasicFrame.WarningDialog.txt1") + " " + filename + ".", + trans.get("BasicFrame.WarningDialog.txt1") + " " + displayName + ".", //// Some design features may not have been loaded correctly. trans.get("BasicFrame.WarningDialog.txt2") }, @@ -1236,12 +1200,6 @@ public class BasicFrame extends JFrame { trans.get("BasicFrame.WarningDialog.title"), warnings); } - - // Set document state - doc.setFile(file); - doc.setSaved(true); - - // Open the frame log.debug("Opening new frame with the document"); BasicFrame frame = new BasicFrame(doc); @@ -1250,6 +1208,10 @@ public class BasicFrame extends JFrame { if (parent != null && parent instanceof BasicFrame) { ((BasicFrame) parent).closeIfReplaceable(); } + if (openRocketConfigDialog) { + ComponentConfigDialog.showDialog(frame, doc, doc.getRocket()); + } + return true; } @@ -1329,7 +1291,12 @@ public class BasicFrame extends JFrame { } else { file = FileHelper.forceExtension(file, "ork"); - return FileHelper.confirmWrite(file, this) && saveAs(file); + boolean result = FileHelper.confirmWrite(file, this) && saveAs(file); + if (result) { + MRUDesignFile opts = MRUDesignFile.getInstance(); + opts.addFile(file.getAbsolutePath()); + } + return result; } } @@ -1347,7 +1314,10 @@ public class BasicFrame extends JFrame { } try { - new RocksimSaver().save(file, document); + StorageOptions options = new StorageOptions(); + options.setFileType(StorageOptions.FileType.ROCKSIM); + ROCKET_SAVER.save(file, document, options); + // Do not update the save state of the document. return true; } catch (IOException e) { return false; @@ -1453,11 +1423,11 @@ public class BasicFrame extends JFrame { return true; } + public void exportDecalAction() { + new ExportDecalDialog(this, document).setVisible(true); + } - /** - * - */ public void printAction() { Double rotation = rocketpanel.getFigure().getRotation(); if (rotation == null) { @@ -1472,19 +1442,11 @@ public class BasicFrame extends JFrame { public static void newAction() { log.info("New action initiated"); - Rocket rocket = new Rocket(); - Stage stage = new Stage(); - //// Sustainer - stage.setName(trans.get("BasicFrame.StageName.Sustainer")); - rocket.addChild(stage); - OpenRocketDocument doc = new OpenRocketDocument(rocket); - doc.setSaved(true); + OpenRocketDocument doc = OpenRocketDocumentFactory.createNewRocket(); BasicFrame frame = new BasicFrame(doc); frame.replaceable = true; frame.setVisible(true); - // kruland commented this out - I don't like it. - //ComponentConfigDialog.showDialog(frame, doc, rocket); } /** diff --git a/core/src/net/sf/openrocket/gui/main/ExampleDesignFile.java b/core/src/net/sf/openrocket/gui/main/ExampleDesignFile.java new file mode 100644 index 000000000..bb8cd8545 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/main/ExampleDesignFile.java @@ -0,0 +1,158 @@ +package net.sf.openrocket.gui.main; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import net.sf.openrocket.file.iterator.DirectoryIterator; +import net.sf.openrocket.file.iterator.FileIterator; +import net.sf.openrocket.gui.util.SimpleFileFilter; +import net.sf.openrocket.util.BugException; +import net.sf.openrocket.util.JarUtil; +import net.sf.openrocket.util.Pair; + +public class ExampleDesignFile implements Comparable { + + private final URL url; + private final String name; + + private ExampleDesignFile(URL url, String name) { + this.url = url; + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public URL getURL() { + return url; + } + + @Override + public int compareTo(ExampleDesignFile o) { + return this.name.compareTo(o.name); + } + + public static ExampleDesignFile[] getExampleDesigns() { + + ExampleDesignFile[] designs = getJarFileNames(); + if (designs == null || designs.length == 0) { + designs = getDirFileNames(); + } + if (designs == null || designs.length == 0) { + return null; + } + + Arrays.sort(designs); + + return designs; + } + + private static final String DIRECTORY = "datafiles/examples/"; + private static final String PATTERN = ".*\\.[oO][rR][kK]$"; + private static final FilenameFilter FILTER = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.matches(PATTERN); + } + }; + + private static ExampleDesignFile[] getDirFileNames() { + + // Try to find directory as a system resource + File dir; + URL url = ClassLoader.getSystemResource(DIRECTORY); + + try { + dir = JarUtil.urlToFile(url); + } catch (Exception e1) { + dir = new File(DIRECTORY); + } + + // Get the list of files + File[] files = dir.listFiles(FILTER); + if (files == null) + return null; + + ExampleDesignFile[] designs = new ExampleDesignFile[files.length]; + + for (int i=0; i list = new ArrayList(); + int dirLength = DIRECTORY.length(); + + // Find and open the jar file this class is contained in + File file = JarUtil.getCurrentJarFile(); + if (file == null) + return null; + + + // Generate URL pointing to JAR file + URL fileUrl; + try { + fileUrl = file.toURI().toURL(); + } catch (MalformedURLException e1) { + e1.printStackTrace(); + throw new BugException(e1); + } + + // Iterate over JAR entries searching for designs + JarFile jarFile = null; + try { + jarFile = new JarFile(file); + + // Loop through JAR entries searching for files to load + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + if (name.startsWith(DIRECTORY) && FILTER.accept(null, name)) { + String urlName = "jar:" + fileUrl + "!/" + name; + URL url = new URL(urlName); + list.add(new ExampleDesignFile(url, + name.substring(dirLength, name.length()-4))); + } + } + + } catch (IOException e) { + // Could be normal condition if not package in JAR + return null; + } finally { + if (jarFile != null) { + try { + jarFile.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + return list.toArray(new ExampleDesignFile[0]); + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/gui/main/ExampleDesignFileAction.java b/core/src/net/sf/openrocket/gui/main/ExampleDesignFileAction.java new file mode 100644 index 000000000..d1ea61b5e --- /dev/null +++ b/core/src/net/sf/openrocket/gui/main/ExampleDesignFileAction.java @@ -0,0 +1,67 @@ +package net.sf.openrocket.gui.main; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JMenu; +import javax.swing.JMenuItem; + +/** + * Implements a menu for the example Open Rocket design files. + */ +public final class ExampleDesignFileAction extends JMenu { + + /** + * The window to which an open design file action will be parented to (typically an instance of BasicFrame). + */ + private final BasicFrame parent; + + /** + * Constructor. + * + * @param s the I18N menu string + * @param theParent the window to which an open design file action will be parented to (typically an instance of + * BasicFrame). + */ + public ExampleDesignFileAction(String s, BasicFrame theParent) { + super(s); + + parent = theParent; + updateMenu(); + } + + /** + * Create menu items. + */ + private void updateMenu() { + removeAll(); + ExampleDesignFile[] examples = ExampleDesignFile.getExampleDesigns(); + for (ExampleDesignFile file : examples) { + Action action = createAction(file); + action.putValue(Action.NAME, file.toString()); + JMenuItem menuItem = new JMenuItem(action); + add(menuItem); + } + } + + /** + * When a user clicks on one of the recently used design files, open it. + * + * @param file the design file name (absolute path) + * + * @return the action to open a design file + */ + private Action createAction(final ExampleDesignFile example) { + Action action = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + String command = e.getActionCommand(); + BasicFrame.open(example.getURL(), parent); + } + }; + + action.putValue(Action.ACTION_COMMAND_KEY, example.toString()); + return action; + } + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/gui/main/RocketActions.java b/core/src/net/sf/openrocket/gui/main/RocketActions.java index 3c94930e1..a702c2839 100644 --- a/core/src/net/sf/openrocket/gui/main/RocketActions.java +++ b/core/src/net/sf/openrocket/gui/main/RocketActions.java @@ -1,6 +1,7 @@ package net.sf.openrocket.gui.main; +import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; @@ -39,11 +40,11 @@ import net.sf.openrocket.util.Pair; public class RocketActions { public static final KeyStroke CUT_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_X, - ActionEvent.CTRL_MASK); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); public static final KeyStroke COPY_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_C, - ActionEvent.CTRL_MASK); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); public static final KeyStroke PASTE_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_V, - ActionEvent.CTRL_MASK); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); private final OpenRocketDocument document; private final Rocket rocket; diff --git a/core/src/net/sf/openrocket/gui/main/SimulationEditDialog.java b/core/src/net/sf/openrocket/gui/main/SimulationEditDialog.java index e4526571f..62175e4de 100644 --- a/core/src/net/sf/openrocket/gui/main/SimulationEditDialog.java +++ b/core/src/net/sf/openrocket/gui/main/SimulationEditDialog.java @@ -24,6 +24,7 @@ import javax.swing.JSpinner; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.ListCellRenderer; +import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; @@ -36,14 +37,16 @@ import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.adaptors.BooleanModel; import net.sf.openrocket.gui.adaptors.DoubleModel; import net.sf.openrocket.gui.adaptors.EnumModel; -import net.sf.openrocket.gui.adaptors.MotorConfigurationModel; +import net.sf.openrocket.gui.adaptors.FlightConfigurationModel; import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.DescriptionArea; import net.sf.openrocket.gui.components.SimulationExportPanel; import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.dialogs.flightconfiguration.FlightConfigurationDialog; import net.sf.openrocket.gui.plot.Axis; import net.sf.openrocket.gui.plot.PlotConfiguration; import net.sf.openrocket.gui.plot.SimulationPlotPanel; +import net.sf.openrocket.gui.scalefigure.RocketPanel; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.l10n.Translator; @@ -208,24 +211,34 @@ public class SimulationEditDialog extends JDialog { DoubleModel m; JSpinner spin; - //// Motor selector - //// Motor configuration: - JLabel label = new JLabel(trans.get("simedtdlg.lbl.Motorcfg")); + //// Flight selector + //// Flight configuration: + JLabel label = new JLabel(trans.get("simedtdlg.lbl.Flightcfg")); //// Select the motor configuration to use. - label.setToolTipText(trans.get("simedtdlg.lbl.ttip.Motorcfg")); + label.setToolTipText(trans.get("simedtdlg.lbl.ttip.Flightcfg")); panel.add(label, "shrinkx, spanx, split 2"); - JComboBox combo = new JComboBox(new MotorConfigurationModel(configuration)); + JComboBox combo = new JComboBox(new FlightConfigurationModel(configuration)); //// Select the motor configuration to use. - combo.setToolTipText(trans.get("simedtdlg.combo.ttip.motorconf")); + combo.setToolTipText(trans.get("simedtdlg.combo.ttip.Flightcfg")); combo.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - conditions.setMotorConfigurationID(configuration.getMotorConfigurationID()); + conditions.setMotorConfigurationID(configuration.getFlightConfigurationID()); } }); - panel.add(combo, "growx, wrap para"); + panel.add(combo, ""); + //// Edit button + JButton button = new JButton(trans.get("simedtdlg.but.FlightcfgEdit")); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JDialog configDialog = new FlightConfigurationDialog(document.getRocket(),SwingUtilities.windowForComponent(SimulationEditDialog.this)); + configDialog.show(); + } + }); + panel.add(button, "wrap"); //// Wind settings: Average wind speed, turbulence intensity, std. deviation sub = new JPanel(new MigLayout("fill, gap rel unrel", diff --git a/core/src/net/sf/openrocket/gui/main/SimulationPanel.java b/core/src/net/sf/openrocket/gui/main/SimulationPanel.java index b12b85617..37091d216 100644 --- a/core/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/core/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -31,6 +31,7 @@ import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.events.DocumentChangeEvent; import net.sf.openrocket.document.events.DocumentChangeListener; import net.sf.openrocket.document.events.SimulationChangeEvent; +import net.sf.openrocket.formatting.RocketDescriptor; import net.sf.openrocket.gui.adaptors.Column; import net.sf.openrocket.gui.adaptors.ColumnTableModel; import net.sf.openrocket.gui.components.StyledLabel; @@ -39,6 +40,7 @@ import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; +import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Preferences; @@ -47,7 +49,7 @@ import net.sf.openrocket.unit.UnitGroup; public class SimulationPanel extends JPanel { private static final LogHelper log = Application.getLogger(); private static final Translator trans = Application.getTranslator(); - + private static final Color WARNING_COLOR = Color.RED; private static final String WARNING_TEXT = "\uFF01"; // Fullwidth exclamation mark @@ -55,8 +57,10 @@ public class SimulationPanel extends JPanel { private static final Color OK_COLOR = new Color(60, 150, 0); private static final String OK_TEXT = "\u2714"; // Heavy check mark - - + + private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); + + private final OpenRocketDocument document; private final ColumnTableModel simulationTableModel; @@ -68,11 +72,11 @@ public class SimulationPanel extends JPanel { JButton button; - + this.document = doc; - - + + //////// The simulation action buttons //// New simulation button @@ -135,7 +139,7 @@ public class SimulationPanel extends JPanel { long t = System.currentTimeMillis(); new SimulationRunDialog(SwingUtilities.getWindowAncestor( - SimulationPanel.this), document, sims).setVisible(true); + SimulationPanel.this), document, sims).setVisible(true); log.info("Running simulations took " + (System.currentTimeMillis() - t) + " ms"); fireMaintainSelection(); } @@ -165,17 +169,17 @@ public class SimulationPanel extends JPanel { panel.add(new StyledLabel(trans.get("simpanel.lbl.defpref"), -2)); int ret = JOptionPane.showConfirmDialog(SimulationPanel.this, - new Object[] { - //// Delete the selected simulations? - trans.get("simpanel.dlg.lbl.DeleteSim1"), - //// This operation cannot be undone. - trans.get("simpanel.dlg.lbl.DeleteSim2"), - "", - panel }, - //// Delete simulations - trans.get("simpanel.dlg.lbl.DeleteSim3"), - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.WARNING_MESSAGE); + new Object[] { + //// Delete the selected simulations? + trans.get("simpanel.dlg.lbl.DeleteSim1"), + //// This operation cannot be undone. + trans.get("simpanel.dlg.lbl.DeleteSim2"), + "", + panel }, + //// Delete simulations + trans.get("simpanel.dlg.lbl.DeleteSim3"), + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.WARNING_MESSAGE); if (ret != JOptionPane.OK_OPTION) return; @@ -216,14 +220,13 @@ public class SimulationPanel extends JPanel { }); this.add(button, "wrap para"); - - - + + //////// The simulation table simulationTableModel = new ColumnTableModel( - - //// Status and warning column + + //// Status and warning column new Column("") { private JLabel label = null; @@ -243,7 +246,7 @@ public class SimulationPanel extends JPanel { Simulation.Status status = document.getSimulation(row).getStatus(); label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status)); - + // Set warning marker if (status == Simulation.Status.NOT_SIMULATED || status == Simulation.Status.EXTERNAL) { @@ -277,7 +280,7 @@ public class SimulationPanel extends JPanel { return JLabel.class; } }, - + //// Simulation name //// Name new Column(trans.get("simpanel.col.Name")) { @@ -293,16 +296,15 @@ public class SimulationPanel extends JPanel { return 125; } }, - - //// Simulation motors - //// Motors - new Column(trans.get("simpanel.col.Motors")) { + + //// Simulation configuration + new Column(trans.get("simpanel.col.Configuration")) { @Override public Object getValueAt(int row) { if (row < 0 || row >= document.getSimulationCount()) return null; - return document.getSimulation(row).getConfiguration() - .getMotorConfigurationDescription(); + Configuration c = document.getSimulation(row).getConfiguration(); + return descriptor.format(c.getRocket(), c.getFlightConfigurationID()); } @Override @@ -310,7 +312,7 @@ public class SimulationPanel extends JPanel { return 125; } }, - + //// Launch rod velocity new Column(trans.get("simpanel.col.Velocityoffrod")) { @Override @@ -326,7 +328,7 @@ public class SimulationPanel extends JPanel { data.getLaunchRodVelocity()); } }, - + //// Apogee new Column(trans.get("simpanel.col.Apogee")) { @Override @@ -342,7 +344,7 @@ public class SimulationPanel extends JPanel { data.getMaxAltitude()); } }, - + //// Velocity at deployment new Column(trans.get("simpanel.col.Velocityatdeploy")) { @Override @@ -358,7 +360,7 @@ public class SimulationPanel extends JPanel { data.getDeploymentVelocity()); } }, - + //// Maximum velocity new Column(trans.get("simpanel.col.Maxvelocity")) { @Override @@ -374,7 +376,7 @@ public class SimulationPanel extends JPanel { data.getMaxVelocity()); } }, - + //// Maximum acceleration new Column(trans.get("simpanel.col.Maxacceleration")) { @Override @@ -390,7 +392,7 @@ public class SimulationPanel extends JPanel { data.getMaxAcceleration()); } }, - + //// Time to apogee new Column(trans.get("simpanel.col.Timetoapogee")) { @Override @@ -406,7 +408,7 @@ public class SimulationPanel extends JPanel { data.getTimeToApogee()); } }, - + //// Flight time new Column(trans.get("simpanel.col.Flighttime")) { @Override @@ -422,7 +424,7 @@ public class SimulationPanel extends JPanel { data.getFlightTime()); } }, - + //// Ground hit velocity new Column(trans.get("simpanel.col.Groundhitvelocity")) { @Override @@ -438,13 +440,13 @@ public class SimulationPanel extends JPanel { data.getGroundHitVelocity()); } } - - ) { - @Override - public int getRowCount() { - return document.getSimulationCount(); - } - }; + + ) { + @Override + public int getRowCount() { + return document.getSimulationCount(); + } + }; // Override processKeyBinding so that the JTable does not catch // key bindings used in menu accelerators @@ -461,7 +463,7 @@ public class SimulationPanel extends JPanel { simulationTable.setDefaultRenderer(Object.class, new JLabelRenderer()); simulationTableModel.setColumnWidths(simulationTable.getColumnModel()); - + // Mouse listener to act on double-clicks simulationTable.addMouseListener(new MouseAdapter() { @Override @@ -490,9 +492,9 @@ public class SimulationPanel extends JPanel { } }); - - - + + + // Fire table change event when the rocket changes document.getRocket().addComponentChangeListener(new ComponentChangeListener() { @Override @@ -501,11 +503,11 @@ public class SimulationPanel extends JPanel { } }); - + JScrollPane scrollpane = new JScrollPane(simulationTable); this.add(scrollpane, "spanx, grow, wrap rel"); - + } @@ -571,38 +573,38 @@ public class SimulationPanel extends JPanel { tip = "" + sim.getName() + "
"; switch (sim.getStatus()) { case UPTODATE: - tip += trans.get ("simpanel.ttip.uptodate") + "
"; + tip += trans.get("simpanel.ttip.uptodate") + "
"; break; case LOADED: - tip += trans.get ("simpanel.ttip.loaded") + "
"; + tip += trans.get("simpanel.ttip.loaded") + "
"; break; case OUTDATED: - tip += trans.get ("simpanel.ttip.outdated") + "
"; + tip += trans.get("simpanel.ttip.outdated") + "
"; break; case EXTERNAL: - tip += trans.get ("simpanel.ttip.external") + "
"; + tip += trans.get("simpanel.ttip.external") + "
"; return tip; case NOT_SIMULATED: - tip += trans.get ("simpanel.ttip.notSimulated"); + tip += trans.get("simpanel.ttip.notSimulated"); return tip; } if (data == null) { - tip += trans.get ("simpanel.ttip.noData"); + tip += trans.get("simpanel.ttip.noData"); return tip; } WarningSet warnings = data.getWarningSet(); if (warnings.isEmpty()) { - tip += trans.get ("simpanel.ttip.noWarnings"); + tip += trans.get("simpanel.ttip.noWarnings"); return tip; } - tip += trans.get ("simpanel.ttip.warnings"); + tip += trans.get("simpanel.ttip.warnings"); for (Warning w : warnings) { tip += "
" + w.toString(); } diff --git a/core/src/net/sf/openrocket/gui/main/SimulationRunDialog.java b/core/src/net/sf/openrocket/gui/main/SimulationRunDialog.java index 976e6ecc1..ab356e9d2 100644 --- a/core/src/net/sf/openrocket/gui/main/SimulationRunDialog.java +++ b/core/src/net/sf/openrocket/gui/main/SimulationRunDialog.java @@ -32,8 +32,8 @@ import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.rocketcomponent.Configuration; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; import net.sf.openrocket.rocketcomponent.MotorMount; -import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent; import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.simulation.SimulationStatus; import net.sf.openrocket.simulation.customexpression.CustomExpression; @@ -309,13 +309,12 @@ public class SimulationRunDialog extends JDialog { Iterator iterator = config.motorIterator(); while (iterator.hasNext()) { MotorMount m = iterator.next(); - if (m.getIgnitionEvent() == IgnitionEvent.LAUNCH) + if (m.getIgnitionConfiguration().getDefault().getIgnitionEvent() == IgnitionConfiguration.IgnitionEvent.LAUNCH) launchBurn = MathUtil.max(launchBurn, m.getMotor(id).getBurnTimeEstimate()); else otherBurn = otherBurn + m.getMotor(id).getBurnTimeEstimate(); } burnoutTimeEstimate = Math.max(launchBurn + otherBurn, 0.1); - } diff --git a/core/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java b/core/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java index 6fe9fe1d9..de9982b58 100644 --- a/core/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java +++ b/core/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java @@ -48,7 +48,7 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer { String comment = c.getComment().trim(); if (comment.length() > 0) { - comment = TextUtil.htmlEncode(comment); + comment = TextUtil.escapeXML(comment); comment = comment.replace("\n", "
"); sb.append("
").append(comment); } diff --git a/core/src/net/sf/openrocket/gui/plot/EventGraphics.java b/core/src/net/sf/openrocket/gui/plot/EventGraphics.java new file mode 100644 index 000000000..7a09ae910 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/plot/EventGraphics.java @@ -0,0 +1,78 @@ +package net.sf.openrocket.gui.plot; + +import java.awt.Color; +import java.awt.Image; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import javax.imageio.ImageIO; + +import net.sf.openrocket.simulation.FlightEvent; + +public class EventGraphics { + + static Color getEventColor(FlightEvent.Type type) { + Color c = EVENT_COLORS.get(type); + if (c != null) + return c; + return DEFAULT_EVENT_COLOR; + } + + static Image getEventImage(FlightEvent.Type type ) { + Image i = EVENT_IMAGES.get(type); + return i; + } + + private static final Color DEFAULT_EVENT_COLOR = new Color(0, 0, 0); + private static final Map EVENT_COLORS = new HashMap(); + static { + EVENT_COLORS.put(FlightEvent.Type.LAUNCH, new Color(255, 0, 0)); + EVENT_COLORS.put(FlightEvent.Type.LIFTOFF, new Color(0, 80, 196)); + EVENT_COLORS.put(FlightEvent.Type.LAUNCHROD, new Color(0, 100, 80)); + EVENT_COLORS.put(FlightEvent.Type.IGNITION, new Color(230, 130, 15)); + EVENT_COLORS.put(FlightEvent.Type.BURNOUT, new Color(80, 55, 40)); + EVENT_COLORS.put(FlightEvent.Type.EJECTION_CHARGE, new Color(80, 55, 40)); + EVENT_COLORS.put(FlightEvent.Type.STAGE_SEPARATION, new Color(80, 55, 40)); + EVENT_COLORS.put(FlightEvent.Type.APOGEE, new Color(15, 120, 15)); + EVENT_COLORS.put(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, new Color(0, 0, 128)); + EVENT_COLORS.put(FlightEvent.Type.GROUND_HIT, new Color(0, 0, 0)); + EVENT_COLORS.put(FlightEvent.Type.SIMULATION_END, new Color(128, 0, 0)); + } + + private static final Map EVENT_IMAGES = new HashMap(); + static { + loadImage(FlightEvent.Type.LAUNCH, "pix/eventicons/event-launch.png"); + loadImage(FlightEvent.Type.LIFTOFF, "pix/eventicons/event-liftoff.png"); + loadImage(FlightEvent.Type.LAUNCHROD, "pix/eventicons/event-launchrod.png"); + loadImage(FlightEvent.Type.IGNITION, "pix/eventicons/event-ignition.png"); + loadImage(FlightEvent.Type.BURNOUT, "pix/eventicons/event-burnout.png"); + loadImage(FlightEvent.Type.EJECTION_CHARGE, "pix/eventicons/event-ejection-charge.png"); + loadImage(FlightEvent.Type.STAGE_SEPARATION, + "pix/eventicons/event-stage-separation.png"); + loadImage(FlightEvent.Type.APOGEE, "pix/eventicons/event-apogee.png"); + loadImage(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, + "pix/eventicons/event-recovery-device-deployment.png"); + loadImage(FlightEvent.Type.GROUND_HIT, "pix/eventicons/event-ground-hit.png"); + loadImage(FlightEvent.Type.SIMULATION_END, "pix/eventicons/event-simulation-end.png"); + } + + private static void loadImage(FlightEvent.Type type, String file) { + InputStream is; + + is = ClassLoader.getSystemResourceAsStream(file); + if (is == null) { + //System.out.println("ERROR: File " + file + " not found!"); + return; + } + + try { + Image image = ImageIO.read(is); + EVENT_IMAGES.put(type, image); + } catch (IOException ignore) { + ignore.printStackTrace(); + } + } + +} diff --git a/core/src/net/sf/openrocket/gui/plot/PlotConfiguration.java b/core/src/net/sf/openrocket/gui/plot/PlotConfiguration.java index 44863f80d..db604a1f2 100644 --- a/core/src/net/sf/openrocket/gui/plot/PlotConfiguration.java +++ b/core/src/net/sf/openrocket/gui/plot/PlotConfiguration.java @@ -36,6 +36,7 @@ public class PlotConfiguration implements Cloneable { config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true); config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true); config.setEvent(FlightEvent.Type.GROUND_HIT, true); + config.setEvent(FlightEvent.Type.TUMBLE, true); configs.add(config); //// Total motion vs. time @@ -49,6 +50,7 @@ public class PlotConfiguration implements Cloneable { config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true); config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true); config.setEvent(FlightEvent.Type.GROUND_HIT, true); + config.setEvent(FlightEvent.Type.TUMBLE, true); configs.add(config); //// Flight side profile @@ -60,6 +62,7 @@ public class PlotConfiguration implements Cloneable { config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true); config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true); config.setEvent(FlightEvent.Type.GROUND_HIT, true); + config.setEvent(FlightEvent.Type.TUMBLE, true); configs.add(config); //// Stability vs. time @@ -73,6 +76,7 @@ public class PlotConfiguration implements Cloneable { config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true); config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true); config.setEvent(FlightEvent.Type.GROUND_HIT, true); + config.setEvent(FlightEvent.Type.TUMBLE, true); configs.add(config); //// Drag coefficients vs. Mach number @@ -97,6 +101,7 @@ public class PlotConfiguration implements Cloneable { config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true); config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true); config.setEvent(FlightEvent.Type.GROUND_HIT, true); + config.setEvent(FlightEvent.Type.TUMBLE, true); configs.add(config); //// Angle of attack and orientation vs. time @@ -110,6 +115,7 @@ public class PlotConfiguration implements Cloneable { config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true); config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true); config.setEvent(FlightEvent.Type.GROUND_HIT, true); + config.setEvent(FlightEvent.Type.TUMBLE, true); configs.add(config); //// Simulation time step and computation time @@ -122,13 +128,14 @@ public class PlotConfiguration implements Cloneable { config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true); config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true); config.setEvent(FlightEvent.Type.GROUND_HIT, true); + config.setEvent(FlightEvent.Type.TUMBLE, true); configs.add(config); DEFAULT_CONFIGURATIONS = configs.toArray(new PlotConfiguration[0]); } - - + + /** Bonus given for the first type being on the first axis */ private static final double BONUS_FIRST_TYPE_ON_FIRST_AXIS = 1.0; @@ -144,11 +151,11 @@ public class PlotConfiguration implements Cloneable { /** Bonus given for only using a single axis. */ private static final double BONUS_ONLY_ONE_AXIS = 50.0; - + private static final double INCLUDE_ZERO_DISTANCE = 0.3; // 30% of total range - - + + /** The data types to be plotted. */ private ArrayList plotDataTypes = new ArrayList(); @@ -163,17 +170,14 @@ public class PlotConfiguration implements Cloneable { private FlightDataType domainAxisType = null; private Unit domainAxisUnit = null; - + /** All available axes. */ private final int axesCount; private ArrayList allAxes = new ArrayList(); - - private String name = null; - public PlotConfiguration() { this(null, FlightDataType.TYPE_TIME); } @@ -192,10 +196,8 @@ public class PlotConfiguration implements Cloneable { setDomainAxisType(domainType); } + //// Axis - - - public FlightDataType getDomainAxisType() { return domainAxisType; } @@ -224,8 +226,8 @@ public class PlotConfiguration implements Cloneable { domainAxisUnit = u; } + //// FlightDataTypes - public void addPlotDataType(FlightDataType type) { plotDataTypes.add(type); plotDataUnits.add(type.getUnitGroup().getDefaultUnit()); @@ -242,8 +244,6 @@ public class PlotConfiguration implements Cloneable { } - - public void setPlotDataType(int index, FlightDataType type) { FlightDataType origType = plotDataTypes.get(index); plotDataTypes.set(index, type); @@ -268,7 +268,6 @@ public class PlotConfiguration implements Cloneable { plotDataAxes.set(index, axis); } - public void setPlotDataType(int index, FlightDataType type, Unit unit, int axis) { if (axis >= axesCount) { throw new IllegalArgumentException("Axis index too large"); @@ -285,7 +284,7 @@ public class PlotConfiguration implements Cloneable { } - + public FlightDataType getType(int index) { return plotDataTypes.get(index); } @@ -322,9 +321,9 @@ public class PlotConfiguration implements Cloneable { } - - - + + + public List getAllAxes() { List list = new ArrayList(); list.addAll(allAxes); @@ -349,7 +348,7 @@ public class PlotConfiguration implements Cloneable { } - + /** * Find the best combination of the auto-selectable axes. * @@ -364,8 +363,8 @@ public class PlotConfiguration implements Cloneable { } - - + + /** * Recursively search for the best combination of the auto-selectable axes. * This is a brute-force search method. @@ -384,13 +383,13 @@ public class PlotConfiguration implements Cloneable { break; } - + if (autoindex >= plotDataAxes.size()) { // All axes have been assigned, just return since we are already the best return new Pair(copy, copy.getGoodnessValue(data)); } - + // Set the auto-selected index one at a time and choose the best one PlotConfiguration best = null; double bestValue = Double.NEGATIVE_INFINITY; @@ -407,9 +406,9 @@ public class PlotConfiguration implements Cloneable { } - - - + + + /** * Fit the axes to hold the provided data. All of the plotDataAxis elements must * be non-negative. @@ -459,7 +458,7 @@ public class PlotConfiguration implements Cloneable { } } - + // Check whether to use a common zero Axis left = allAxes.get(0); Axis right = allAxes.get(1); @@ -469,8 +468,8 @@ public class PlotConfiguration implements Cloneable { Double.isNaN(left.getMinValue()) || Double.isNaN(right.getMinValue())) return; - - + + //// Compute common zero // TODO: MEDIUM: This algorithm may require tweaking @@ -481,7 +480,7 @@ public class PlotConfiguration implements Cloneable { // Calculate and round scaling factor double scale = Math.max(left.getRangeLength(), right.getRangeLength()) / - Math.min(left.getRangeLength(), right.getRangeLength()); + Math.min(left.getRangeLength(), right.getRangeLength()); //System.out.println("Scale: " + scale); @@ -501,54 +500,6 @@ public class PlotConfiguration implements Cloneable { min2 /= scale; max2 /= scale; - - - // Scale to unit length - // double scale1 = left.getRangeLength(); - // double scale2 = right.getRangeLength(); - // - // double min1 = left.getMinValue() / scale1; - // double max1 = left.getMaxValue() / scale1; - // double min2 = right.getMinValue() / scale2; - // double max2 = right.getMaxValue() / scale2; - // - // // Combine unit ranges - // min1 = MathUtil.min(min1, min2); - // min2 = min1; - // max1 = MathUtil.max(max1, max2); - // max2 = max1; - // - // // Scale up - // min1 *= scale1; - // max1 *= scale1; - // min2 *= scale2; - // max2 *= scale2; - // - // // Compute common scale - // double range1 = max1-min1; - // double range2 = max2-min2; - // - // double scale = MathUtil.max(range1, range2) / MathUtil.min(range1, range2); - // double roundScale = roundScale(scale); - // - // if (range2 < range1) { - // if (roundScale < scale) { - // min2 = min1 / roundScale; - // max2 = max1 / roundScale; - // } else { - // min1 = min2 * roundScale; - // max1 = max2 * roundScale; - // } - // } else { - // if (roundScale > scale) { - // min2 = min1 * roundScale; - // max2 = max1 * roundScale; - // } else { - // min1 = min2 / roundScale; - // max1 = max2 / roundScale; - // } - // } - // Apply scale left.addBound(min1); left.addBound(max1); @@ -558,7 +509,7 @@ public class PlotConfiguration implements Cloneable { } - + private double roundScale(double scale) { double mul = 1; while (scale >= 10) { @@ -587,7 +538,7 @@ public class PlotConfiguration implements Cloneable { } - + @SuppressWarnings("unused") private double roundScaleUp(double scale) { double mul = 1; @@ -640,7 +591,7 @@ public class PlotConfiguration implements Cloneable { } - + /** * Fits the axis ranges to the data and returns the "goodness value" of this * selection of axes. All plotDataAxis elements must be non-null. @@ -682,11 +633,11 @@ public class PlotConfiguration implements Cloneable { goodness += d * 100.0; } - + /* * Add extra points for specific things. */ - + // A little for the first type being on the first axis if (plotDataAxes.get(0) == 0) goodness += BONUS_FIRST_TYPE_ON_FIRST_AXIS; @@ -710,7 +661,7 @@ public class PlotConfiguration implements Cloneable { } - + /** * Reset the units of this configuration to the default units. Returns this * PlotConfiguration. @@ -725,8 +676,8 @@ public class PlotConfiguration implements Cloneable { } - - + + @Override public PlotConfiguration clone() { try { @@ -747,7 +698,7 @@ public class PlotConfiguration implements Cloneable { return copy; - + } catch (CloneNotSupportedException e) { throw new BugException("BUG! Could not clone()."); } diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationChart.java b/core/src/net/sf/openrocket/gui/plot/SimulationChart.java new file mode 100644 index 000000000..fa3932d5a --- /dev/null +++ b/core/src/net/sf/openrocket/gui/plot/SimulationChart.java @@ -0,0 +1,268 @@ +package net.sf.openrocket.gui.plot; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; + +import javax.swing.BorderFactory; + +import org.jfree.chart.ChartPanel; +import org.jfree.chart.ChartRenderingInfo; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PiePlot; +import org.jfree.chart.plot.Plot; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.PlotRenderingInfo; +import org.jfree.chart.plot.Zoomable; + +import com.jogamp.newt.event.InputEvent; + +/** + * Custom implementation of JFreeChart's ChartPanel which changes the mouse handling. + * + * Changed mouse drag (left click + move) to pan the image. + * + * Changed mouse wheel handling. wheel zooms. Alt+wheel zooms only domain axis. + * + * @author kruland + * + */ +public class SimulationChart extends ChartPanel { + + private Point2D panLast; + private Point startPoint; + private double panW; + private double panH; + + private enum Interaction { + ZOOM + }; + + private Interaction interaction = null; + + private MouseWheelHandler mouseWheelHandler = null; + + public SimulationChart(JFreeChart chart) { + super(chart, + /* properties */false, + /* save */true, + /* print */false, + /* zoom */true, + /* tooltips */true); + this.setMouseWheelEnabled(true); + this.setEnforceFileExtensions(true); + this.setInitialDelay(500); + this.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1)); + } + + @Override + public boolean isMouseWheelEnabled() { + return mouseWheelHandler != null; + } + + @Override + public void setMouseWheelEnabled(boolean flag) { + if (flag && mouseWheelHandler == null) { + this.mouseWheelHandler = new MouseWheelHandler(); + this.addMouseWheelListener(this.mouseWheelHandler); + } else if (!flag && mouseWheelHandler != null) { + this.removeMouseWheelListener(this.mouseWheelHandler); + this.mouseWheelHandler = null; + } + } + + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3) { + + Rectangle2D screenDataArea = getScreenDataArea(e.getX(), e.getY()); + + if (screenDataArea != null && screenDataArea.contains(e.getPoint())) { + this.panW = screenDataArea.getWidth(); + this.panH = screenDataArea.getHeight(); + this.panLast = e.getPoint(); + this.startPoint = e.getPoint(); + } + interaction = Interaction.ZOOM; + setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR)); + + } + else { + interaction = null; + super.mousePressed(e); + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if (interaction == null) { + super.mouseDragged(e); + return; + } + + if (panLast == null) { + return; + } + + double dx = e.getX() - this.panLast.getX(); + double dy = e.getY() - this.panLast.getY(); + if (dx == 0.0 && dy == 0.0) { + return; + } + double wPercent = -dx / this.panW; + double hPercent = dy / this.panH; + boolean old = this.getChart().getPlot().isNotify(); + this.getChart().getPlot().setNotify(false); + + switch (interaction) { + case ZOOM: + Zoomable pz = (Zoomable) this.getChart().getPlot(); + + double zoomx = 1 + 2 * wPercent; + double zoomy = 1 + 2 * hPercent; + + Point2D anchor = SimulationChart.this.translateScreenToJava2D(startPoint); + + if (pz.getOrientation() == PlotOrientation.VERTICAL) { + pz.zoomDomainAxes(zoomx, this.getChartRenderingInfo().getPlotInfo(), anchor, true); + pz.zoomRangeAxes(zoomy, this.getChartRenderingInfo().getPlotInfo(), anchor, true); + } else { + pz.zoomRangeAxes(zoomx, this.getChartRenderingInfo().getPlotInfo(), anchor, true); + pz.zoomDomainAxes(zoomy, this.getChartRenderingInfo().getPlotInfo(), anchor, true); + } + + break; + } + + + this.panLast = e.getPoint(); + this.getChart().getPlot().setNotify(old); + } + + @Override + public void mouseReleased(MouseEvent e) { + if (interaction == null) { + super.mouseReleased(e); + return; + } + + if (this.panLast != null) { + this.panLast = null; + setCursor(Cursor.getDefaultCursor()); + } + interaction = null; + } + + + /** + * + * Hacked up copy of MouseWheelHandler from JFreechart. This version + * has the special ability to only zoom on the domain if the alt key is pressed. + * + * A class that handles mouse wheel events for the {@link ChartPanel} class. + * Mouse wheel event support was added in JDK 1.4, so this class will be omitted + * from JFreeChart if you build it using JDK 1.3. + * + * @since 1.0.13 + */ + class MouseWheelHandler implements MouseWheelListener, Serializable { + + /** The zoom factor. */ + double zoomFactor; + + /** + * Creates a new instance for the specified chart panel. + * + * @param chartPanel the chart panel (null not permitted). + */ + public MouseWheelHandler() { + this.zoomFactor = 0.10; + } + + /** + * Returns the current zoom factor. The default value is 0.10 (ten + * percent). + * + * @return The zoom factor. + * + * @see #setZoomFactor(double) + */ + public double getZoomFactor() { + return this.zoomFactor; + } + + /** + * Sets the zoom factor. + * + * @param zoomFactor the zoom factor. + * + * @see #getZoomFactor() + */ + public void setZoomFactor(double zoomFactor) { + this.zoomFactor = zoomFactor; + } + + /** + * Handles a mouse wheel event from the underlying chart panel. + * + * @param e the event. + */ + public void mouseWheelMoved(MouseWheelEvent e) { + JFreeChart chart = SimulationChart.this.getChart(); + if (chart == null) { + return; + } + Plot plot = chart.getPlot(); + if (plot instanceof Zoomable) { + Zoomable zoomable = (Zoomable) plot; + handleZoomable(zoomable, e); + } + else if (plot instanceof PiePlot) { + PiePlot pp = (PiePlot) plot; + pp.handleMouseWheelRotation(e.getWheelRotation()); + } + } + + /** + * Handle the case where a plot implements the {@link Zoomable} interface. + * + * @param zoomable the zoomable plot. + * @param e the mouse wheel event. + */ + private void handleZoomable(Zoomable zoomable, MouseWheelEvent e) { + // don't zoom unless the mouse pointer is in the plot's data area + ChartRenderingInfo info = SimulationChart.this.getChartRenderingInfo(); + PlotRenderingInfo pinfo = info.getPlotInfo(); + Point2D p = SimulationChart.this.translateScreenToJava2D(e.getPoint()); + if (!pinfo.getDataArea().contains(p)) { + return; + } + + Plot plot = (Plot) zoomable; + // do not notify while zooming each axis + boolean notifyState = plot.isNotify(); + plot.setNotify(false); + int clicks = e.getWheelRotation(); + double zf = 1.0 + this.zoomFactor; + if (clicks < 0) { + zf = 1.0 / zf; + } + if (SimulationChart.this.isDomainZoomable()) { + zoomable.zoomDomainAxes(zf, pinfo, p, true); + } + boolean domainOnly = (e.getModifiers() & InputEvent.CTRL_MASK) != 0; + if (SimulationChart.this.isRangeZoomable() && !domainOnly) { + zoomable.zoomRangeAxes(zf, pinfo, p, true); + } + plot.setNotify(notifyState); // this generates the change event too + } + + } + +} diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java b/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java new file mode 100644 index 000000000..a254275c1 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java @@ -0,0 +1,650 @@ +package net.sf.openrocket.gui.plot; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; + +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.simulation.FlightDataBranch; +import net.sf.openrocket.simulation.FlightDataType; +import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.unit.Unit; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.LinearInterpolator; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.LegendItem; +import org.jfree.chart.LegendItemCollection; +import org.jfree.chart.LegendItemSource; +import org.jfree.chart.annotations.XYImageAnnotation; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.block.LineBorder; +import org.jfree.chart.plot.DefaultDrawingSupplier; +import org.jfree.chart.plot.Marker; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.ValueMarker; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYItemRenderer; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.chart.title.LegendTitle; +import org.jfree.chart.title.TextTitle; +import org.jfree.data.Range; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; +import org.jfree.text.TextUtilities; +import org.jfree.ui.LengthAdjustmentType; +import org.jfree.ui.RectangleAnchor; +import org.jfree.ui.RectangleEdge; +import org.jfree.ui.RectangleInsets; +import org.jfree.ui.TextAnchor; + +/* + * It should be possible to simplify this code quite a bit by using a single Renderer instance for + * both datasets and the legend. But for now, the renderers are queried for the line color information + * and this is held in the Legend. + */ +public class SimulationPlot { + + private static final float PLOT_STROKE_WIDTH = 1.5f; + + private final JFreeChart chart; + + private final PlotConfiguration config; + private final Simulation simulation; + private final PlotConfiguration filled; + + private final List eventList; + private final List renderers = new ArrayList(); + + private final LegendItems legendItems; + + int branchCount; + + void setShowPoints(boolean showPoints) { + for (ModifiedXYItemRenderer r : renderers) { + r.setBaseShapesVisible(showPoints); + } + } + + void setShowBranch(int branch) { + XYPlot plot = (XYPlot) chart.getPlot(); + int datasetcount = plot.getDatasetCount(); + for (int i = 0; i < datasetcount; i++) { + int seriescount = ((XYSeriesCollection) plot.getDataset(i)).getSeriesCount(); + XYItemRenderer r = ((XYPlot) chart.getPlot()).getRenderer(i); + for (int j = 0; j < seriescount; j++) { + boolean show = (branch < 0) || (j % branchCount == branch); + r.setSeriesVisible(j, show); + } + } + drawDomainMarkers(branch); + } + + SimulationPlot(Simulation simulation, PlotConfiguration config, boolean initialShowPoints) { + this.simulation = simulation; + this.config = config; + this.branchCount = simulation.getSimulatedData().getBranchCount(); + + this.chart = ChartFactory.createXYLineChart( + //// Simulated flight + /*title*/simulation.getName(), + /*xAxisLabel*/null, + /*yAxisLabel*/null, + /*dataset*/null, + /*orientation*/PlotOrientation.VERTICAL, + /*legend*/false, + /*tooltips*/true, + /*urls*/false + ); + + this.legendItems = new LegendItems(); + LegendTitle legend = new LegendTitle(legendItems); + legend.setMargin(new RectangleInsets(1.0, 1.0, 1.0, 1.0)); + legend.setFrame(new LineBorder()); + legend.setBackgroundPaint(Color.white); + legend.setPosition(RectangleEdge.BOTTOM); + chart.addSubtitle(legend); + + chart.addSubtitle(new TextTitle(config.getName())); + + // Fill the auto-selections based on first branch selected. + FlightDataBranch mainBranch = simulation.getSimulatedData().getBranch(0); + this.filled = config.fillAutoAxes(mainBranch); + List axes = filled.getAllAxes(); + + // Create the data series for both axes + XYSeriesCollection[] data = new XYSeriesCollection[2]; + data[0] = new XYSeriesCollection(); + data[1] = new XYSeriesCollection(); + + // Get the domain axis type + final FlightDataType domainType = filled.getDomainAxisType(); + final Unit domainUnit = filled.getDomainAxisUnit(); + if (domainType == null) { + throw new IllegalArgumentException("Domain axis type not specified."); + } + + // Get plot length (ignore trailing NaN's) + int typeCount = filled.getTypeCount(); + + // Create the XYSeries objects from the flight data and store into the collections + String[] axisLabel = new String[2]; + for (int i = 0; i < typeCount; i++) { + // Get info + FlightDataType type = filled.getType(i); + Unit unit = filled.getUnit(i); + int axis = filled.getAxis(i); + String name = getLabel(type, unit); + this.legendItems.lineLabels.add(name); + + List seriesNames = Util.generateSeriesLabels(simulation); + + // Populate data for each branch. + + // The primary branch (branchIndex = 0) is easy since all the data is copied + { + int branchIndex = 0; + FlightDataBranch thisBranch = simulation.getSimulatedData().getBranch(branchIndex); + // Store data in provided units + List plotx = thisBranch.get(domainType); + List ploty = thisBranch.get(type); + XYSeries series = new XYSeries(seriesNames.get(branchIndex) + ": " + name, false, true); + series.setDescription(thisBranch.getBranchName() + ": " + name); + int pointCount = plotx.size(); + for (int j = 0; j < pointCount; j++) { + series.add(domainUnit.toUnit(plotx.get(j)), unit.toUnit(ploty.get(j))); + } + data[axis].addSeries(series); + } + // For each of the secondary branches, we use data from branch 0 for the earlier times + for (int branchIndex = 1; branchIndex < branchCount; branchIndex++) { + FlightDataBranch primaryBranch = simulation.getSimulatedData().getBranch(0); + FlightDataBranch thisBranch = simulation.getSimulatedData().getBranch(branchIndex); + + // Get first time index used in secondary branch; + double firstSampleTime = thisBranch.get(FlightDataType.TYPE_TIME).get(0); + + XYSeries series = new XYSeries(seriesNames.get(branchIndex) + ": " + name, false, true); + series.setDescription(thisBranch.getBranchName() + ": " + name); + + // Copy the first points from the primaryBranch. + List primaryT = primaryBranch.get(FlightDataType.TYPE_TIME); + List primaryx = primaryBranch.get(domainType); + List primaryy = primaryBranch.get(type); + + for (int j = 0; j < primaryT.size(); j++) { + if (primaryT.get(j) >= firstSampleTime) { + break; + } + series.add(domainUnit.toUnit(primaryx.get(j)), unit.toUnit(primaryy.get(j))); + } + + // Now copy all the data from the secondary branch + List plotx = thisBranch.get(domainType); + List ploty = thisBranch.get(type); + + int pointCount = plotx.size(); + for (int j = 0; j < pointCount; j++) { + series.add(domainUnit.toUnit(plotx.get(j)), unit.toUnit(ploty.get(j))); + } + data[axis].addSeries(series); + } + + // Update axis label + if (axisLabel[axis] == null) + axisLabel[axis] = type.getName(); + else + axisLabel[axis] += "; " + type.getName(); + } + + // Add the data and formatting to the plot + XYPlot plot = chart.getXYPlot(); + plot.setDomainPannable(true); + plot.setRangePannable(true); + + int axisno = 0; + for (int i = 0; i < 2; i++) { + // Check whether axis has any data + if (data[i].getSeriesCount() > 0) { + // Create and set axis + double min = axes.get(i).getMinValue(); + double max = axes.get(i).getMaxValue(); + NumberAxis axis = new PresetNumberAxis(min, max); + axis.setLabel(axisLabel[i]); + // axis.setRange(axes.get(i).getMinValue(), axes.get(i).getMaxValue()); + plot.setRangeAxis(axisno, axis); + + double domainMin = data[i].getDomainLowerBound(true); + double domainMax = data[i].getDomainUpperBound(true); + + plot.setDomainAxis(new PresetNumberAxis(domainMin, domainMax)); + + // Add data and map to the axis + plot.setDataset(axisno, data[i]); + ModifiedXYItemRenderer r = new ModifiedXYItemRenderer(branchCount); + renderers.add(r); + plot.setRenderer(axisno, r); + r.setBaseShapesVisible(initialShowPoints); + r.setBaseShapesFilled(true); + for (int j = 0; j < data[i].getSeriesCount(); j++) { + Stroke lineStroke = new BasicStroke(PLOT_STROKE_WIDTH); + r.setSeriesStroke(j, lineStroke); + } + // Now we pull the colors for the legend. + for (int j = 0; j < data[i].getSeriesCount(); j += branchCount) { + Paint linePaint = r.lookupSeriesPaint(j); + this.legendItems.linePaints.add(linePaint); + Shape itemShape = r.lookupSeriesShape(j); + this.legendItems.pointShapes.add(itemShape); + Stroke lineStroke = r.getSeriesStroke(j); + this.legendItems.lineStrokes.add(lineStroke); + } + + plot.mapDatasetToRangeAxis(axisno, axisno); + axisno++; + } + } + + plot.getDomainAxis().setLabel(getLabel(domainType, domainUnit)); + plot.addDomainMarker(new ValueMarker(0)); + plot.addRangeMarker(new ValueMarker(0)); + + + + // Create list of events to show (combine event too close to each other) + this.eventList = buildEventInfo(); + + // Create the event markers + drawDomainMarkers(-1); + + } + + JFreeChart getJFreeChart() { + return chart; + } + + private String getLabel(FlightDataType type, Unit unit) { + String name = type.getName(); + if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) && + !UnitGroup.UNITS_COEFFICIENT.contains(unit) && unit.getUnit().length() > 0) + name += " (" + unit.getUnit() + ")"; + return name; + } + + private void drawDomainMarkers(int stage) { + XYPlot plot = chart.getXYPlot(); + FlightDataBranch mainBranch = simulation.getSimulatedData().getBranch(0); + + // Clear existing domain markers + plot.clearDomainMarkers(); + + // Construct domain marker lists collapsing based on time. + + List eventTimes = new ArrayList(); + List eventLabels = new ArrayList(); + List eventColors = new ArrayList(); + List eventImages = new ArrayList(); + { + HashSet typeSet = new HashSet(); + double prevTime = -100; + String text = null; + Color color = null; + Image image = null; + for (EventDisplayInfo info : eventList) { + if (stage >= 0 && stage != info.stage) { + continue; + } + + double t = info.time; + FlightEvent.Type type = info.event.getType(); + + if (Math.abs(t - prevTime) <= 0.05) { + + if (!typeSet.contains(type)) { + text = text + ", " + type.toString(); + color = EventGraphics.getEventColor(type); + image = EventGraphics.getEventImage(type); + typeSet.add(type); + } + + } else { + + if (text != null) { + eventTimes.add(prevTime); + eventLabels.add(text); + eventColors.add(color); + eventImages.add(image); + } + prevTime = t; + text = type.toString(); + color = EventGraphics.getEventColor(type); + image = EventGraphics.getEventImage(type); + typeSet.clear(); + typeSet.add(type); + } + + } + if (text != null) { + eventTimes.add(prevTime); + eventLabels.add(text); + eventColors.add(color); + eventImages.add(image); + } + } + + // Plot the markers + if (config.getDomainAxisType() == FlightDataType.TYPE_TIME) { + + // Domain time is plotted as vertical markers + for (int i = 0; i < eventTimes.size(); i++) { + double t = eventTimes.get(i); + String event = eventLabels.get(i); + Color color = eventColors.get(i); + + ValueMarker m = new ValueMarker(t); + m.setLabel(event); + m.setPaint(color); + m.setLabelPaint(color); + m.setAlpha(0.7f); + plot.addDomainMarker(m); + } + + } else { + + // Other domains are plotted as image annotations + List time = mainBranch.get(FlightDataType.TYPE_TIME); + List domain = mainBranch.get(config.getDomainAxisType()); + + LinearInterpolator domainInterpolator = new LinearInterpolator(time, domain); + + for (int i = 0; i < eventTimes.size(); i++) { + double t = eventTimes.get(i); + String event = eventLabels.get(i); + Image image = eventImages.get(i); + + if (image == null) + continue; + + double xcoord = domainInterpolator.getValue(t); + for (int index = 0; index < config.getTypeCount(); index++) { + FlightDataType type = config.getType(index); + List range = mainBranch.get(type); + + LinearInterpolator rangeInterpolator = new LinearInterpolator(time, range); + // Image annotations are not supported on the right-side axis + // TODO: LOW: Can this be achieved by JFreeChart? + if (filled.getAxis(index) != SimulationPlotPanel.LEFT) { + continue; + } + + double ycoord = rangeInterpolator.getValue(t); + + // Convert units + xcoord = config.getDomainAxisUnit().toUnit(xcoord); + ycoord = config.getUnit(index).toUnit(ycoord); + + XYImageAnnotation annotation = + new XYImageAnnotation(xcoord, ycoord, image, RectangleAnchor.CENTER); + annotation.setToolTipText(event); + plot.addAnnotation(annotation); + } + } + } + } + + private List buildEventInfo() { + ArrayList eventList = new ArrayList(); + + for (int branch = 0; branch < branchCount; branch++) { + List events = simulation.getSimulatedData().getBranch(branch).getEvents(); + for (FlightEvent event : events) { + FlightEvent.Type type = event.getType(); + if (type != FlightEvent.Type.ALTITUDE && config.isEventActive(type)) { + EventDisplayInfo info = new EventDisplayInfo(); + info.stage = branch; + info.time = event.getTime(); + info.event = event; + eventList.add(info); + } + } + } + + Collections.sort(eventList, new Comparator() { + + @Override + public int compare(EventDisplayInfo o1, EventDisplayInfo o2) { + if (o1.time < o2.time) + return -1; + if (o1.time == o2.time) + return 0; + return 1; + } + + }); + + return eventList; + + } + + private static class LegendItems implements LegendItemSource { + + private final List lineLabels = new ArrayList(); + private final List linePaints = new ArrayList(); + private final List lineStrokes = new ArrayList(); + private final List pointShapes = new ArrayList(); + + @Override + public LegendItemCollection getLegendItems() { + LegendItemCollection c = new LegendItemCollection(); + int i = 0; + for (String s : lineLabels) { + String label = s; + String description = s; + String toolTipText = null; + String urlText = null; + boolean shapeIsVisible = false; + Shape shape = pointShapes.get(i); + boolean shapeIsFilled = false; + Paint fillPaint = linePaints.get(i); + boolean shapeOutlineVisible = false; + Paint outlinePaint = linePaints.get(i); + Stroke outlineStroke = lineStrokes.get(i); + boolean lineVisible = true; + Stroke lineStroke = lineStrokes.get(i); + Paint linePaint = linePaints.get(i); + + Shape legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0); + + LegendItem result = new LegendItem(label, description, toolTipText, + urlText, shapeIsVisible, shape, shapeIsFilled, fillPaint, + shapeOutlineVisible, outlinePaint, outlineStroke, lineVisible, + legendLine, lineStroke, linePaint); + + c.add(result); + i++; + } + return c; + } + } + + /** + * A modification to the standard renderer that renders the domain marker + * labels vertically instead of horizontally. + * + * This class is special in that it assumes the data series are added to it + * in a specific order. In particular they must be "by parameter by stage". + * Assuming that three series are chosen (a, b, c) and the rocket has 2 stages, the + * data series are added in this order: + * + * series a stage 0 + * series a stage 1 + * series b stage 0 + * series b stage 1 + * series c stage 0 + * series c stage 1 + */ + private static class ModifiedXYItemRenderer extends XYLineAndShapeRenderer { + + private final int branchCount; + + private ModifiedXYItemRenderer(int branchCount) { + this.branchCount = branchCount; + } + + @Override + public Paint lookupSeriesPaint(int series) { + return super.lookupSeriesPaint(series / branchCount); + } + + @Override + public Paint lookupSeriesFillPaint(int series) { + return super.lookupSeriesFillPaint(series / branchCount); + } + + @Override + public Paint lookupSeriesOutlinePaint(int series) { + return super.lookupSeriesOutlinePaint(series / branchCount); + } + + @Override + public Stroke lookupSeriesStroke(int series) { + return super.lookupSeriesStroke(series / branchCount); + } + + @Override + public Stroke lookupSeriesOutlineStroke(int series) { + return super.lookupSeriesOutlineStroke(series / branchCount); + } + + @Override + public Shape lookupSeriesShape(int series) { + return DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE[series % branchCount % DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE.length]; + } + + @Override + public Shape lookupLegendShape(int series) { + return DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE[series % branchCount % DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE.length]; + } + + @Override + public Font lookupLegendTextFont(int series) { + return super.lookupLegendTextFont(series / branchCount); + } + + @Override + public Paint lookupLegendTextPaint(int series) { + return super.lookupLegendTextPaint(series / branchCount); + } + + @Override + public void drawDomainMarker(Graphics2D g2, XYPlot plot, ValueAxis domainAxis, + Marker marker, Rectangle2D dataArea) { + + if (!(marker instanceof ValueMarker)) { + // Use parent for all others + super.drawDomainMarker(g2, plot, domainAxis, marker, dataArea); + return; + } + + /* + * Draw the normal marker, but with rotated text. + * Copied from the overridden method. + */ + ValueMarker vm = (ValueMarker) marker; + double value = vm.getValue(); + Range range = domainAxis.getRange(); + if (!range.contains(value)) { + return; + } + + double v = domainAxis.valueToJava2D(value, dataArea, plot.getDomainAxisEdge()); + + PlotOrientation orientation = plot.getOrientation(); + Line2D line = null; + if (orientation == PlotOrientation.HORIZONTAL) { + line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(), v); + } else { + line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea.getMaxY()); + } + + final Composite originalComposite = g2.getComposite(); + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, marker + .getAlpha())); + g2.setPaint(marker.getPaint()); + g2.setStroke(marker.getStroke()); + g2.draw(line); + + String label = marker.getLabel(); + RectangleAnchor anchor = marker.getLabelAnchor(); + if (label != null) { + Font labelFont = marker.getLabelFont(); + g2.setFont(labelFont); + g2.setPaint(marker.getLabelPaint()); + Point2D coordinates = calculateDomainMarkerTextAnchorPoint(g2, + orientation, dataArea, line.getBounds2D(), marker + .getLabelOffset(), LengthAdjustmentType.EXPAND, anchor); + + // Changed: + TextAnchor textAnchor = TextAnchor.TOP_RIGHT; + TextUtilities.drawRotatedString(label, g2, (float) coordinates.getX() + 2, + (float) coordinates.getY(), textAnchor, + -Math.PI / 2, textAnchor); + } + g2.setComposite(originalComposite); + } + + } + + private static class PresetNumberAxis extends NumberAxis { + private final double min; + private final double max; + + public PresetNumberAxis(double min, double max) { + this.min = min; + this.max = max; + autoAdjustRange(); + } + + @Override + protected void autoAdjustRange() { + this.setRange(min, max); + } + + @Override + public void setRange(Range range) { + double lowerValue = range.getLowerBound(); + double upperValue = range.getUpperBound(); + if (lowerValue < min || upperValue > max) { + // Don't blow past the min & max of the range this is important to keep + // panning constrained within the current bounds. + return; + } + super.setRange(new Range(lowerValue, upperValue)); + } + + } + + private static class EventDisplayInfo { + int stage; + double time; + FlightEvent event; + } + +} diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java index ada19bfcd..1bb158549 100644 --- a/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java +++ b/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java @@ -1,31 +1,18 @@ package net.sf.openrocket.gui.plot; -import java.awt.AlphaComposite; -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Composite; -import java.awt.Font; -import java.awt.Graphics2D; -import java.awt.Image; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.geom.Line2D; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.io.IOException; -import java.io.InputStream; +import java.awt.event.InputEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import javax.imageio.ImageIO; -import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; +import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; @@ -34,36 +21,12 @@ import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.simulation.FlightDataBranch; -import net.sf.openrocket.simulation.FlightDataType; -import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Preferences; -import net.sf.openrocket.unit.Unit; -import net.sf.openrocket.unit.UnitGroup; -import net.sf.openrocket.util.BugException; -import net.sf.openrocket.util.MathUtil; -import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.annotations.XYImageAnnotation; -import org.jfree.chart.axis.NumberAxis; -import org.jfree.chart.axis.ValueAxis; -import org.jfree.chart.plot.Marker; -import org.jfree.chart.plot.PlotOrientation; -import org.jfree.chart.plot.ValueMarker; -import org.jfree.chart.plot.XYPlot; -import org.jfree.chart.renderer.xy.StandardXYItemRenderer; -import org.jfree.chart.title.TextTitle; -import org.jfree.data.Range; -import org.jfree.data.xy.XYSeries; -import org.jfree.data.xy.XYSeriesCollection; -import org.jfree.text.TextUtilities; -import org.jfree.ui.LengthAdjustmentType; -import org.jfree.ui.RectangleAnchor; -import org.jfree.ui.TextAnchor; /** * Dialog that shows a plot of a simulation results based on user options. @@ -72,359 +35,28 @@ import org.jfree.ui.TextAnchor; */ public class SimulationPlotDialog extends JDialog { - private static final float PLOT_STROKE_WIDTH = 1.5f; private static final Translator trans = Application.getTranslator(); - private static final Color DEFAULT_EVENT_COLOR = new Color(0, 0, 0); - private static final Map EVENT_COLORS = - new HashMap(); - static { - EVENT_COLORS.put(FlightEvent.Type.LAUNCH, new Color(255, 0, 0)); - EVENT_COLORS.put(FlightEvent.Type.LIFTOFF, new Color(0, 80, 196)); - EVENT_COLORS.put(FlightEvent.Type.LAUNCHROD, new Color(0, 100, 80)); - EVENT_COLORS.put(FlightEvent.Type.IGNITION, new Color(230, 130, 15)); - EVENT_COLORS.put(FlightEvent.Type.BURNOUT, new Color(80, 55, 40)); - EVENT_COLORS.put(FlightEvent.Type.EJECTION_CHARGE, new Color(80, 55, 40)); - EVENT_COLORS.put(FlightEvent.Type.STAGE_SEPARATION, new Color(80, 55, 40)); - EVENT_COLORS.put(FlightEvent.Type.APOGEE, new Color(15, 120, 15)); - EVENT_COLORS.put(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, new Color(0, 0, 128)); - EVENT_COLORS.put(FlightEvent.Type.GROUND_HIT, new Color(0, 0, 0)); - EVENT_COLORS.put(FlightEvent.Type.SIMULATION_END, new Color(128, 0, 0)); - } - - private static final Map EVENT_IMAGES = - new HashMap(); - static { - loadImage(FlightEvent.Type.LAUNCH, "pix/eventicons/event-launch.png"); - loadImage(FlightEvent.Type.LIFTOFF, "pix/eventicons/event-liftoff.png"); - loadImage(FlightEvent.Type.LAUNCHROD, "pix/eventicons/event-launchrod.png"); - loadImage(FlightEvent.Type.IGNITION, "pix/eventicons/event-ignition.png"); - loadImage(FlightEvent.Type.BURNOUT, "pix/eventicons/event-burnout.png"); - loadImage(FlightEvent.Type.EJECTION_CHARGE, "pix/eventicons/event-ejection-charge.png"); - loadImage(FlightEvent.Type.STAGE_SEPARATION, - "pix/eventicons/event-stage-separation.png"); - loadImage(FlightEvent.Type.APOGEE, "pix/eventicons/event-apogee.png"); - loadImage(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, - "pix/eventicons/event-recovery-device-deployment.png"); - loadImage(FlightEvent.Type.GROUND_HIT, "pix/eventicons/event-ground-hit.png"); - loadImage(FlightEvent.Type.SIMULATION_END, "pix/eventicons/event-simulation-end.png"); - } - - private static void loadImage(FlightEvent.Type type, String file) { - InputStream is; - - is = ClassLoader.getSystemResourceAsStream(file); - if (is == null) { - //System.out.println("ERROR: File " + file + " not found!"); - return; - } - - try { - Image image = ImageIO.read(is); - EVENT_IMAGES.put(type, image); - } catch (IOException ignore) { - ignore.printStackTrace(); - } - } - - - - - private final List renderers = - new ArrayList(); - private SimulationPlotDialog(Window parent, Simulation simulation, PlotConfiguration config) { //// Flight data plot - super(parent, trans.get("PlotDialog.title.Flightdataplot")); + super(parent, simulation.getName()); this.setModalityType(ModalityType.DOCUMENT_MODAL); final boolean initialShowPoints = Application.getPreferences().getBoolean(Preferences.PLOT_SHOW_POINTS, false); - - // Fill the auto-selections - FlightDataBranch branch = simulation.getSimulatedData().getBranch(0); - PlotConfiguration filled = config.fillAutoAxes(branch); - List axes = filled.getAllAxes(); + final SimulationPlot myPlot = new SimulationPlot(simulation, config, initialShowPoints); - - // Create the data series for both axes - XYSeriesCollection[] data = new XYSeriesCollection[2]; - data[0] = new XYSeriesCollection(); - data[1] = new XYSeriesCollection(); - - - // Get the domain axis type - final FlightDataType domainType = filled.getDomainAxisType(); - final Unit domainUnit = filled.getDomainAxisUnit(); - if (domainType == null) { - throw new IllegalArgumentException("Domain axis type not specified."); - } - List x = branch.get(domainType); - - - // Get plot length (ignore trailing NaN's) - int typeCount = filled.getTypeCount(); - int dataLength = 0; - for (int i = 0; i < typeCount; i++) { - FlightDataType type = filled.getType(i); - List y = branch.get(type); - - for (int j = dataLength; j < y.size(); j++) { - if (!Double.isNaN(y.get(j)) && !Double.isInfinite(y.get(j))) - dataLength = j; - } - } - dataLength = Math.min(dataLength, x.size()); - - - // Create the XYSeries objects from the flight data and store into the collections - String[] axisLabel = new String[2]; - for (int i = 0; i < typeCount; i++) { - // Get info - FlightDataType type = filled.getType(i); - Unit unit = filled.getUnit(i); - int axis = filled.getAxis(i); - String name = getLabel(type, unit); - - // Store data in provided units - List y = branch.get(type); - XYSeries series = new XYSeries(name, false, true); - for (int j = 0; j < dataLength; j++) { - series.add(domainUnit.toUnit(x.get(j)), unit.toUnit(y.get(j))); - } - data[axis].addSeries(series); - - // Update axis label - if (axisLabel[axis] == null) - axisLabel[axis] = type.getName(); - else - axisLabel[axis] += "; " + type.getName(); - } - - - // Create the chart using the factory to get all default settings - JFreeChart chart = ChartFactory.createXYLineChart( - //// Simulated flight - trans.get("PlotDialog.Chart.Simulatedflight"), - null, - null, - null, - PlotOrientation.VERTICAL, - true, - true, - false - ); - - chart.addSubtitle(new TextTitle(config.getName())); - - // Add the data and formatting to the plot - XYPlot plot = chart.getXYPlot(); - int axisno = 0; - for (int i = 0; i < 2; i++) { - // Check whether axis has any data - if (data[i].getSeriesCount() > 0) { - // Create and set axis - double min = axes.get(i).getMinValue(); - double max = axes.get(i).getMaxValue(); - NumberAxis axis = new PresetNumberAxis(min, max); - axis.setLabel(axisLabel[i]); - // axis.setRange(axes.get(i).getMinValue(), axes.get(i).getMaxValue()); - plot.setRangeAxis(axisno, axis); - - // Add data and map to the axis - plot.setDataset(axisno, data[i]); - ModifiedXYItemRenderer r = new ModifiedXYItemRenderer(); - r.setBaseShapesVisible(initialShowPoints); - r.setBaseShapesFilled(true); - for (int j = 0; j < data[i].getSeriesCount(); j++) { - r.setSeriesStroke(j, new BasicStroke(PLOT_STROKE_WIDTH)); - } - renderers.add(r); - plot.setRenderer(axisno, r); - plot.mapDatasetToRangeAxis(axisno, axisno); - axisno++; - } - } - - plot.getDomainAxis().setLabel(getLabel(domainType, domainUnit)); - plot.addDomainMarker(new ValueMarker(0)); - plot.addRangeMarker(new ValueMarker(0)); - - - - // Create list of events to show (combine event too close to each other) - ArrayList timeList = new ArrayList(); - ArrayList eventList = new ArrayList(); - ArrayList colorList = new ArrayList(); - ArrayList imageList = new ArrayList(); - - HashSet typeSet = new HashSet(); - - double prevTime = -100; - String text = null; - Color color = null; - Image image = null; - - List events = branch.getEvents(); - for (int i = 0; i < events.size(); i++) { - FlightEvent event = events.get(i); - double t = event.getTime(); - FlightEvent.Type type = event.getType(); - - if (type != FlightEvent.Type.ALTITUDE && config.isEventActive(type)) { - if (Math.abs(t - prevTime) <= 0.01) { - - if (!typeSet.contains(type)) { - text = text + ", " + type.toString(); - color = getEventColor(type); - image = EVENT_IMAGES.get(type); - typeSet.add(type); - } - - } else { - - if (text != null) { - timeList.add(prevTime); - eventList.add(text); - colorList.add(color); - imageList.add(image); - } - prevTime = t; - text = type.toString(); - color = getEventColor(type); - image = EVENT_IMAGES.get(type); - typeSet.clear(); - typeSet.add(type); - - } - } - } - if (text != null) { - timeList.add(prevTime); - eventList.add(text); - colorList.add(color); - imageList.add(image); - } - - - // Create the event markers - - if (config.getDomainAxisType() == FlightDataType.TYPE_TIME) { - - // Domain time is plotted as vertical markers - for (int i = 0; i < eventList.size(); i++) { - double t = timeList.get(i); - String event = eventList.get(i); - color = colorList.get(i); - - ValueMarker m = new ValueMarker(t); - m.setLabel(event); - m.setPaint(color); - m.setLabelPaint(color); - m.setAlpha(0.7f); - plot.addDomainMarker(m); - } - - } else { - - // Other domains are plotted as image annotations - List time = branch.get(FlightDataType.TYPE_TIME); - List domain = branch.get(config.getDomainAxisType()); - - for (int i = 0; i < eventList.size(); i++) { - final double t = timeList.get(i); - String event = eventList.get(i); - image = imageList.get(i); - - if (image == null) - continue; - - // Calculate index and interpolation position a - final double a; - int tindex = Collections.binarySearch(time, t); - if (tindex < 0) { - tindex = -tindex - 1; - } - if (tindex >= time.size()) { - // index greater than largest value in time list - tindex = time.size() - 1; - a = 0; - } else if (tindex <= 0) { - // index smaller than smallest value in time list - tindex = 0; - a = 0; - } else { - tindex--; - double t1 = time.get(tindex); - double t2 = time.get(tindex + 1); - - if ((t1 > t) || (t2 < t)) { - throw new BugException("t1=" + t1 + " t2=" + t2 + " t=" + t); - } - - if (MathUtil.equals(t1, t2)) { - a = 0; - } else { - a = 1 - (t - t1) / (t2 - t1); - } - } - - double xcoord; - if (a == 0) { - xcoord = domain.get(tindex); - } else { - xcoord = a * domain.get(tindex) + (1 - a) * domain.get(tindex + 1); - } - - for (int index = 0; index < config.getTypeCount(); index++) { - FlightDataType type = config.getType(index); - List range = branch.get(type); - - // Image annotations are not supported on the right-side axis - // TODO: LOW: Can this be achieved by JFreeChart? - if (filled.getAxis(index) != SimulationPlotPanel.LEFT) { - continue; - } - - double ycoord; - if (a == 0) { - ycoord = range.get(tindex); - } else { - ycoord = a * range.get(tindex) + (1 - a) * range.get(tindex + 1); - } - - // Convert units - xcoord = config.getDomainAxisUnit().toUnit(xcoord); - ycoord = config.getUnit(index).toUnit(ycoord); - - XYImageAnnotation annotation = - new XYImageAnnotation(xcoord, ycoord, image, RectangleAnchor.CENTER); - annotation.setToolTipText(event); - plot.addAnnotation(annotation); - } - } - } - - // Create the dialog - JPanel panel = new JPanel(new MigLayout("fill")); this.add(panel); - ChartPanel chartPanel = new ChartPanel(chart, - false, // properties - true, // save - false, // print - true, // zoom - true); // tooltips - chartPanel.setMouseWheelEnabled(true); - chartPanel.setEnforceFileExtensions(true); - chartPanel.setInitialDelay(500); - - chartPanel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1)); - + final ChartPanel chartPanel = new SimulationChart(myPlot.getJFreeChart()); panel.add(chartPanel, "grow, wrap 20lp"); + //// Description text + JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2); + panel.add(label, "wrap"); + //// Show data points final JCheckBox check = new JCheckBox(trans.get("PlotDialog.CheckBox.Showdatapoints")); check.setSelected(initialShowPoints); @@ -433,22 +65,76 @@ public class SimulationPlotDialog extends JDialog { public void actionPerformed(ActionEvent e) { boolean show = check.isSelected(); Application.getPreferences().putBoolean(Preferences.PLOT_SHOW_POINTS, show); - for (ModifiedXYItemRenderer r : renderers) { - r.setBaseShapesVisible(show); - } + myPlot.setShowPoints(show); } }); panel.add(check, "split, left"); - - JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2); - panel.add(label, "gapleft para"); + //// Zoom in button + JButton button = new JButton(Icons.ZOOM_IN); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if ((e.getModifiers() & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) { + chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_IN_DOMAIN_COMMAND)); + } else { + chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_IN_BOTH_COMMAND)); + + } + } + }); + panel.add(button, "gapleft rel"); + //// Reset Zoom button. + button = new JButton(Icons.ZOOM_RESET); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_RESET_BOTH_COMMAND)); + } + }); + panel.add(button, "gapleft rel"); + + + //// Zoom out button + button = new JButton(Icons.ZOOM_OUT); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if ((e.getModifiers() & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) { + chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_OUT_DOMAIN_COMMAND)); + } else { + chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_OUT_BOTH_COMMAND)); + } + } + }); + panel.add(button, "gapleft rel"); + + //// Add series selection box + ArrayList stages = new ArrayList(); + stages.add("All"); + stages.addAll( Util.generateSeriesLabels(simulation)); + final JComboBox stageSelection = new JComboBox(stages.toArray(new String[0])); + stageSelection.addItemListener(new ItemListener() { + + @Override + public void itemStateChanged(ItemEvent e) { + int selectedStage = stageSelection.getSelectedIndex() - 1; + myPlot.setShowBranch(selectedStage); + } + + }); + if (stages.size() > 2) { + // Only show the combo box if there are at least 3 entries (ie, "All", "Main", and one other one + panel.add(stageSelection, "gapleft rel"); + } + + //// Spacer for layout to push close button to the right. panel.add(new JPanel(), "growx"); //// Close button - JButton button = new JButton(trans.get("dlg.but.close")); + button = new JButton(trans.get("dlg.but.close")); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -464,31 +150,6 @@ public class SimulationPlotDialog extends JDialog { GUIUtil.rememberWindowSize(this); } - private String getLabel(FlightDataType type, Unit unit) { - String name = type.getName(); - if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) && - !UnitGroup.UNITS_COEFFICIENT.contains(unit) && unit.getUnit().length() > 0) - name += " (" + unit.getUnit() + ")"; - return name; - } - - - - private class PresetNumberAxis extends NumberAxis { - private final double min; - private final double max; - - public PresetNumberAxis(double min, double max) { - this.min = min; - this.max = max; - autoAdjustRange(); - } - - @Override - protected void autoAdjustRange() { - this.setRange(min, max); - } - } /** @@ -502,82 +163,4 @@ public class SimulationPlotDialog extends JDialog { new SimulationPlotDialog(parent, simulation, config).setVisible(true); } - - - private static Color getEventColor(FlightEvent.Type type) { - Color c = EVENT_COLORS.get(type); - if (c != null) - return c; - return DEFAULT_EVENT_COLOR; - } - - - - - - /** - * A modification to the standard renderer that renders the domain marker - * labels vertically instead of horizontally. - */ - private static class ModifiedXYItemRenderer extends StandardXYItemRenderer { - - @Override - public void drawDomainMarker(Graphics2D g2, XYPlot plot, ValueAxis domainAxis, - Marker marker, Rectangle2D dataArea) { - - if (!(marker instanceof ValueMarker)) { - // Use parent for all others - super.drawDomainMarker(g2, plot, domainAxis, marker, dataArea); - return; - } - - /* - * Draw the normal marker, but with rotated text. - * Copied from the overridden method. - */ - ValueMarker vm = (ValueMarker) marker; - double value = vm.getValue(); - Range range = domainAxis.getRange(); - if (!range.contains(value)) { - return; - } - - double v = domainAxis.valueToJava2D(value, dataArea, plot.getDomainAxisEdge()); - - PlotOrientation orientation = plot.getOrientation(); - Line2D line = null; - if (orientation == PlotOrientation.HORIZONTAL) { - line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(), v); - } else { - line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea.getMaxY()); - } - - final Composite originalComposite = g2.getComposite(); - g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, marker - .getAlpha())); - g2.setPaint(marker.getPaint()); - g2.setStroke(marker.getStroke()); - g2.draw(line); - - String label = marker.getLabel(); - RectangleAnchor anchor = marker.getLabelAnchor(); - if (label != null) { - Font labelFont = marker.getLabelFont(); - g2.setFont(labelFont); - g2.setPaint(marker.getLabelPaint()); - Point2D coordinates = calculateDomainMarkerTextAnchorPoint(g2, - orientation, dataArea, line.getBounds2D(), marker - .getLabelOffset(), LengthAdjustmentType.EXPAND, anchor); - - // Changed: - TextAnchor textAnchor = TextAnchor.TOP_RIGHT; - TextUtilities.drawRotatedString(label, g2, (float) coordinates.getX() + 2, - (float) coordinates.getY(), textAnchor, - -Math.PI / 2, textAnchor); - } - g2.setComposite(originalComposite); - } - - } - } diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java b/core/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java index e149bf85b..5a8787b28 100644 --- a/core/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java +++ b/core/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java @@ -107,7 +107,7 @@ public class SimulationPlotPanel extends JPanel { types = branch.getTypes(); setConfiguration(defaultConfiguration); - + //// Configuration selector // Setup the combo box @@ -313,7 +313,6 @@ public class SimulationPlotPanel extends JPanel { }); this.add(button, "right"); - updatePlots(); } diff --git a/core/src/net/sf/openrocket/gui/plot/Util.java b/core/src/net/sf/openrocket/gui/plot/Util.java new file mode 100644 index 000000000..81003aae4 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/plot/Util.java @@ -0,0 +1,36 @@ +package net.sf.openrocket.gui.plot; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.sf.openrocket.document.Simulation; + +public abstract class Util { + + public static List generateSeriesLabels( Simulation simulation ) { + int size = simulation.getSimulatedData().getBranchCount(); + ArrayList stages = new ArrayList(size); + // we need to generate unique strings for each of the branches. Since the branch names are based + // on the stage name there is no guarantee they are unique. In order to address this, we first assume + // all the names are unique, then go through them looking for duplicates. + for (int i = 0; i < simulation.getSimulatedData().getBranchCount(); i++) { + stages.add(simulation.getSimulatedData().getBranch(i).getBranchName()); + } + // check for duplicates: + for( int i = 0; i< stages.size(); i++ ) { + String stagename = stages.get(i); + int numberDuplicates = Collections.frequency(stages, stagename); + if ( numberDuplicates > 1 ) { + int index = i; + int count = 1; + while( count <= numberDuplicates ) { + stages.set(index, stagename + "(" + count + ")" ); + count ++; + for( index++; index < stages.size() && !stagename.equals(stages.get(index)); index++ ); + } + } + } + return stages; + } +} diff --git a/core/src/net/sf/openrocket/gui/print/AbstractPrintable.java b/core/src/net/sf/openrocket/gui/print/AbstractPrintable.java index 585e4975b..1e9f0c1f8 100644 --- a/core/src/net/sf/openrocket/gui/print/AbstractPrintable.java +++ b/core/src/net/sf/openrocket/gui/print/AbstractPrintable.java @@ -22,11 +22,10 @@ public abstract class AbstractPrintable extends PrintableComponent { /** * Constructor. Initialize this printable with the component to be printed. * - * @param isDoubleBuffered a boolean, true for double-buffering - * @param transition the component to be printed + * @param component the component to be printed */ - public AbstractPrintable(boolean isDoubleBuffered, T transition) { - init(transition); + public AbstractPrintable(T component) { + init(component); } /** @@ -50,8 +49,8 @@ public abstract class AbstractPrintable extends PrintableComponent { * @return an awt image of the printable component */ public Image createImage() { - int width = getWidth() + getOffsetX(); - int height = getHeight() + getOffsetY(); + int width = getWidth(); + int height = getHeight(); // Create a buffered image in which to draw BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); // Create a graphics contents on the buffered image @@ -73,8 +72,12 @@ public abstract class AbstractPrintable extends PrintableComponent { g2.setColor(Color.BLACK); g2.setStroke(thinStroke); - g2.translate(getOffsetX(), getOffsetY()); + translate(g2); draw(g2); } + + protected void translate(final Graphics2D theG2) { + theG2.translate(getOffsetX(), getOffsetY()); + } } diff --git a/core/src/net/sf/openrocket/gui/print/DesignReport.java b/core/src/net/sf/openrocket/gui/print/DesignReport.java index a2a1ab40d..2838c5173 100644 --- a/core/src/net/sf/openrocket/gui/print/DesignReport.java +++ b/core/src/net/sf/openrocket/gui/print/DesignReport.java @@ -9,6 +9,7 @@ import java.util.List; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.formatting.RocketDescriptor; import net.sf.openrocket.gui.figureelements.FigureElement; import net.sf.openrocket.gui.figureelements.RocketInfo; import net.sf.openrocket.gui.scalefigure.RocketPanel; @@ -74,33 +75,36 @@ import com.itextpdf.text.pdf.PdfWriter; * */ public class DesignReport { - + /** * The logger. */ private static final LogHelper log = Application.getLogger(); public static final double SCALE_FUDGE_FACTOR = 0.4d; - + + private static final RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); + + /** * The OR Document. */ private OpenRocketDocument rocketDocument; - + /** * A panel used for rendering of the design diagram. */ final RocketPanel panel; - + /** * The iText document. */ protected Document document; - + /** * The figure rotation. */ private double rotation = 0d; - + /** The displayed strings. */ private static final String STAGES = "Stages: "; private static final String MASS_WITH_MOTORS = "Mass (with motors): "; @@ -126,7 +130,7 @@ public class DesignReport { private static final String LANDING_VELOCITY = "Landing Velocity"; private static final String ROCKET_DESIGN = "Rocket Design"; private static final double GRAVITY_CONSTANT = 9.80665d; - + /** * Constructor. * @@ -140,7 +144,7 @@ public class DesignReport { panel = new RocketPanel(rocketDocument); rotation = figureRotation; } - + /** * Main entry point. Prints the rocket drawing and design data. * @@ -153,23 +157,23 @@ public class DesignReport { com.itextpdf.text.Rectangle pageSize = document.getPageSize(); int pageImageableWidth = (int) pageSize.getWidth() - (int) pageSize.getBorderWidth() * 2; int pageImageableHeight = (int) pageSize.getHeight() / 2 - (int) pageSize.getBorderWidthTop(); - + PrintUtilities.addText(document, PrintUtilities.BIG_BOLD, ROCKET_DESIGN); - + Rocket rocket = rocketDocument.getRocket(); final Configuration configuration = rocket.getDefaultConfiguration().clone(); configuration.setAllStages(); PdfContentByte canvas = writer.getDirectContent(); - + final PrintFigure figure = new PrintFigure(configuration); figure.setRotation(rotation); - + FigureElement cp = panel.getExtraCP(); FigureElement cg = panel.getExtraCG(); RocketInfo text = panel.getExtraText(); - + double scale = paintRocketDiagram(pageImageableWidth, pageImageableHeight, canvas, figure, cp, cg); - + canvas.beginText(); canvas.setFontAndSize(ITextHelper.getBaseFont(), PrintUtilities.NORMAL_FONT_SIZE); int figHeightPts = (int) (PrintUnit.METERS.toPoints(figure.getFigureHeight()) * 0.4 * (scale / PrintUnit.METERS @@ -177,15 +181,15 @@ public class DesignReport { final int diagramHeight = pageImageableHeight * 2 - 70 - (figHeightPts); canvas.moveText(document.leftMargin() + pageSize.getBorderWidthLeft(), diagramHeight); canvas.moveTextWithLeading(0, -16); - + float initialY = canvas.getYTLM(); - + canvas.showText(rocketDocument.getRocket().getName()); - + canvas.newlineShowText(STAGES); canvas.showText("" + rocket.getStageCount()); - - + + if (configuration.hasMotors()) { if (configuration.getStageCount() > 1) { canvas.newlineShowText(MASS_WITH_MOTORS); @@ -196,29 +200,29 @@ public class DesignReport { canvas.newlineShowText(MASS_EMPTY); } canvas.showText(text.getMass(UnitGroup.UNITS_MASS.getDefaultUnit())); - + canvas.newlineShowText(STABILITY); canvas.showText(text.getStability()); - + canvas.newlineShowText(CG); canvas.showText(text.getCg()); - + canvas.newlineShowText(CP); canvas.showText(text.getCp()); canvas.endText(); - + try { //Move the internal pointer of the document below that of what was just written using the direct byte buffer. Paragraph paragraph = new Paragraph(); float finalY = canvas.getYTLM(); int heightOfDiagramAndText = (int) (pageSize.getHeight() - (finalY - initialY + diagramHeight)); - + paragraph.setSpacingAfter(heightOfDiagramAndText); document.add(paragraph); - - String[] motorIds = rocket.getMotorConfigurationIDs(); + + String[] motorIds = rocket.getFlightConfigurationIDs(); List simulations = rocketDocument.getSimulations(); - + for (int j = 0; j < motorIds.length; j++) { String motorId = motorIds[j]; if (motorId != null) { @@ -242,8 +246,8 @@ public class DesignReport { log.error("Could not modify document.", e); } } - - + + /** * Paint a diagram of the rocket into the PDF document. * @@ -264,7 +268,7 @@ public class DesignReport { theFigure.addRelativeExtra(theCp); theFigure.addRelativeExtra(theCg); theFigure.updateFigure(); - + double scale = (thePageImageableWidth * 2.2) / theFigure.getFigureWidth(); theFigure.setScale(scale); @@ -273,7 +277,7 @@ public class DesignReport { */ theFigure.setSize(thePageImageableWidth, thePageImageableHeight); theFigure.updateFigure(); - + final DefaultFontMapper mapper = new DefaultFontMapper(); Graphics2D g2d = theCanvas.createGraphics(thePageImageableWidth, thePageImageableHeight * 2, mapper); final double halfFigureHeight = SCALE_FUDGE_FACTOR * theFigure.getFigureHeightPx() / 2; @@ -284,13 +288,13 @@ public class DesignReport { y += (int) halfFigureHeight; } g2d.translate(20, y); - + g2d.scale(SCALE_FUDGE_FACTOR, SCALE_FUDGE_FACTOR); theFigure.paint(g2d); g2d.dispose(); return scale; } - + /** * Add the motor data for a motor configuration to the table. * @@ -299,11 +303,11 @@ public class DesignReport { * @param parent the parent to which the motor data will be added */ private void addMotorData(Rocket rocket, String motorId, final PdfPTable parent) { - + PdfPTable motorTable = new PdfPTable(8); motorTable.setWidthPercentage(68); motorTable.setHorizontalAlignment(Element.ALIGN_LEFT); - + final PdfPCell motorCell = ITextHelper.createCell(MOTOR, PdfPCell.BOTTOM, PrintUtilities.SMALL); final int mPad = 10; motorCell.setPaddingLeft(mPad); @@ -315,25 +319,25 @@ public class DesignReport { motorTable.addCell(ITextHelper.createCell(THRUST_TO_WT, PdfPCell.BOTTOM, PrintUtilities.SMALL)); motorTable.addCell(ITextHelper.createCell(PROPELLANT_WT, PdfPCell.BOTTOM, PrintUtilities.SMALL)); motorTable.addCell(ITextHelper.createCell(SIZE, PdfPCell.BOTTOM, PrintUtilities.SMALL)); - + DecimalFormat ttwFormat = new DecimalFormat("0.00"); - + MassCalculator massCalc = new BasicMassCalculator(); - + Configuration config = new Configuration(rocket); - config.setMotorConfigurationID(motorId); - + config.setFlightConfigurationID(motorId); + int totalMotorCount = 0; double totalPropMass = 0; double totalImpulse = 0; double totalTTW = 0; - + int stage = 0; double stageMass = 0; - + boolean topBorder = false; for (RocketComponent c : rocket) { - + if (c instanceof Stage) { config.setToStage(stage); stage++; @@ -342,26 +346,26 @@ public class DesignReport { totalTTW = 0; topBorder = true; } - + if (c instanceof MotorMount && ((MotorMount) c).isMotorMount()) { MotorMount mount = (MotorMount) c; - + if (mount.isMotorMount() && mount.getMotor(motorId) != null) { Motor motor = mount.getMotor(motorId); int motorCount = c.toAbsolute(Coordinate.NUL).length; - - + + int border = Rectangle.NO_BORDER; if (topBorder) { border = Rectangle.TOP; topBorder = false; } - + String name = motor.getDesignation(); if (motorCount > 1) { name += " (" + Chars.TIMES + motorCount + ")"; } - + final PdfPCell motorVCell = ITextHelper.createCell(name, border); motorVCell.setPaddingLeft(mPad); motorTable.addCell(motorVCell); @@ -373,21 +377,21 @@ public class DesignReport { UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(motor.getMaxThrustEstimate()), border)); motorTable.addCell(ITextHelper.createCell( UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit(motor.getTotalImpulseEstimate()), border)); - + double ttw = motor.getAverageThrustEstimate() / (stageMass * GRAVITY_CONSTANT); motorTable.addCell(ITextHelper.createCell( ttwFormat.format(ttw) + ":1", border)); - + double propMass = (motor.getLaunchCG().weight - motor.getEmptyCG().weight); motorTable.addCell(ITextHelper.createCell( UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(propMass), border)); - + final Unit motorUnit = UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit(); motorTable.addCell(ITextHelper.createCell(motorUnit.toString(motor.getDiameter()) + "/" + motorUnit.toString(motor.getLength()) + " " + motorUnit.toString(), border)); - + // Sum up total count totalMotorCount += motorCount; totalPropMass += propMass * motorCount; @@ -396,7 +400,7 @@ public class DesignReport { } } } - + if (totalMotorCount > 1) { int border = Rectangle.TOP; final PdfPCell motorVCell = ITextHelper.createCell("Total:", border); @@ -412,17 +416,17 @@ public class DesignReport { motorTable.addCell(ITextHelper.createCell( UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(totalPropMass), border)); motorTable.addCell(ITextHelper.createCell("", border)); - + } - + PdfPCell c = new PdfPCell(motorTable); c.setBorder(PdfPCell.LEFT); c.setBorderWidthTop(0f); parent.addCell(c); - config.release(); + config.release(); } - - + + /** * Add the flight data for a simulation configuration to the table. * @@ -433,47 +437,47 @@ public class DesignReport { * @param leading the number of points for the leading */ private void addFlightData(final FlightData flight, final Rocket theRocket, final String motorId, final PdfPTable parent, int leading) { - + // Output the flight data if (flight != null) { try { final Unit distanceUnit = UnitGroup.UNITS_DISTANCE.getDefaultUnit(); final Unit velocityUnit = UnitGroup.UNITS_VELOCITY.getDefaultUnit(); final Unit flightTimeUnit = UnitGroup.UNITS_FLIGHT_TIME.getDefaultUnit(); - + PdfPTable labelTable = new PdfPTable(2); labelTable.setWidths(new int[] { 3, 2 }); final Paragraph chunk = ITextHelper.createParagraph(stripBrackets( - theRocket.getMotorConfigurationNameOrDescription(motorId)), PrintUtilities.BOLD); + descriptor.format(theRocket, motorId)), PrintUtilities.BOLD); chunk.setLeading(leading); chunk.setSpacingAfter(3f); - + document.add(chunk); - + final PdfPCell cell = ITextHelper.createCell(ALTITUDE, 2, 2); cell.setUseBorderPadding(false); cell.setBorderWidthTop(0f); labelTable.addCell(cell); labelTable.addCell(ITextHelper.createCell(distanceUnit.toStringUnit(flight.getMaxAltitude()), 2, 2)); - + labelTable.addCell(ITextHelper.createCell(FLIGHT_TIME, 2, 2)); labelTable.addCell(ITextHelper.createCell(flightTimeUnit.toStringUnit(flight.getFlightTime()), 2, 2)); - + labelTable.addCell(ITextHelper.createCell(TIME_TO_APOGEE, 2, 2)); labelTable.addCell(ITextHelper.createCell(flightTimeUnit.toStringUnit(flight.getTimeToApogee()), 2, 2)); - + labelTable.addCell(ITextHelper.createCell(VELOCITY_OFF_PAD, 2, 2)); labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getLaunchRodVelocity()), 2, 2)); - + labelTable.addCell(ITextHelper.createCell(MAX_VELOCITY, 2, 2)); labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getMaxVelocity()), 2, 2)); - + labelTable.addCell(ITextHelper.createCell(DEPLOYMENT_VELOCITY, 2, 2)); labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getDeploymentVelocity()), 2, 2)); - + labelTable.addCell(ITextHelper.createCell(LANDING_VELOCITY, 2, 2)); labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getGroundHitVelocity()), 2, 2)); - + //Add the table to the parent; have to wrap it in a cell PdfPCell c = new PdfPCell(labelTable); c.setBorder(PdfPCell.RIGHT); @@ -485,7 +489,7 @@ public class DesignReport { } } } - + /** * Locate the simulation based on the motor id. Copy the simulation and execute it, then return the resulting * flight data. @@ -513,7 +517,7 @@ public class DesignReport { } return flight; } - + /** * Strip [] brackets from a string. * @@ -524,7 +528,7 @@ public class DesignReport { private String stripBrackets(String target) { return stripLeftBracket(stripRightBracket(target)); } - + /** * Strip [ from a string. * @@ -535,7 +539,7 @@ public class DesignReport { private String stripLeftBracket(String target) { return target.replace("[", ""); } - + /** * Strip ] from a string. * @@ -546,5 +550,5 @@ public class DesignReport { private String stripRightBracket(String target) { return target.replace("]", ""); } - + } diff --git a/core/src/net/sf/openrocket/gui/print/FinMarkingGuide.java b/core/src/net/sf/openrocket/gui/print/FinMarkingGuide.java index 64b617ab1..d2509c145 100644 --- a/core/src/net/sf/openrocket/gui/print/FinMarkingGuide.java +++ b/core/src/net/sf/openrocket/gui/print/FinMarkingGuide.java @@ -1,15 +1,5 @@ package net.sf.openrocket.gui.print; -import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.rocketcomponent.BodyTube; -import net.sf.openrocket.rocketcomponent.ExternalComponent; -import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.LaunchLug; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.startup.Application; - -import javax.swing.JPanel; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; @@ -21,12 +11,22 @@ import java.awt.geom.Path2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import javax.swing.JPanel; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.ExternalComponent; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; + /** * This is the core Swing representation of a fin marking guide. It can handle multiple fin sets on the same or * different body tubes. One marking guide will be created for any body tube that has a finset. If a tube has multiple @@ -36,448 +36,446 @@ import java.util.Map; *

*/ public class FinMarkingGuide extends JPanel { - - /** - * The stroke of normal lines. - */ - private final static BasicStroke thinStroke = new BasicStroke(1.0f); - - /** - * The size of the arrow in points. - */ - private static final int ARROW_SIZE = 10; - - /** - * Typical thickness of a piece of printer paper (~20-24 lb paper). Wrapping paper around a tube results in the - * radius being increased by the thickness of the paper. The smaller the tube, the more pronounced this becomes as a - * percentage of circumference. Using 1/10mm as an approximation here. - */ - private static final double PAPER_THICKNESS_IN_METERS = PrintUnit.MILLIMETERS.toMeters(0.1d); - - /** - * The default guide width in inches. - */ - public final static double DEFAULT_GUIDE_WIDTH = 3d; - - /** - * 2 PI radians (represents a circle). - */ - public final static double TWO_PI = 2 * Math.PI; - - /** - * The I18N translator. - */ - private static final Translator trans = Application.getTranslator(); - - /** - * The margin. - */ - private static final int MARGIN = (int) PrintUnit.INCHES.toPoints(0.25f); - - /** - * The height (circumference) of the biggest body tube with a finset. - */ - private int maxHeight = 0; - - /** - * A map of body tubes, to a list of components that contains finsets and launch lugs. - */ - private Map> markingGuideItems; - - /** - * Constructor. - * - * @param rocket the rocket instance - */ - public FinMarkingGuide(Rocket rocket) { - super(false); - setBackground(Color.white); - markingGuideItems = init(rocket); - //Max of 2 drawing guides horizontally per page. - setSize((int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH) * 2 + 3 * MARGIN, maxHeight); - } - - /** - * Initialize the marking guide class by iterating over a rocket and finding all finsets. - * - * @param component the root rocket component - this is iterated to find all finset and launch lugs - * - * @return a map of body tubes to lists of finsets and launch lugs. - */ - private Map> init(Rocket component) { - Iterator iter = component.iterator(false); - Map> results = new LinkedHashMap>(); - BodyTube current = null; - int totalHeight = 0; - int iterationHeight = 0; - int count = 0; - - while (iter.hasNext()) { - RocketComponent next = iter.next(); - if (next instanceof BodyTube) { - current = (BodyTube) next; - } - else if (next instanceof FinSet || next instanceof LaunchLug) { - java.util.List list = results.get(current); - if (list == null && current != null) { - list = new ArrayList(); - results.put(current, list); - - double radius = current.getOuterRadius(); - int circumferenceInPoints = (int) PrintUnit.METERS.toPoints(radius * TWO_PI); - - // Find the biggest body tube circumference. - if (iterationHeight < (circumferenceInPoints + MARGIN)) { - iterationHeight = circumferenceInPoints + MARGIN; - } - //At most, two marking guides horizontally. After that, move down and back to the left margin. - count++; - if (count % 2 == 0) { - totalHeight += iterationHeight; - iterationHeight = 0; - } - } - if (list != null) { - list.add((ExternalComponent) next); - } - } - } - maxHeight = totalHeight + iterationHeight; - return results; - } - - /** - * Returns a generated image of the fin marking guide. May then be used wherever AWT images can be used, or - * converted to another image/picture format and used accordingly. - * - * @return an awt image of the fin marking guide - */ - public Image createImage() { - int width = getWidth() + 25; - int height = getHeight() + 25; - // Create a buffered image in which to draw - BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - // Create a graphics context on the buffered image - Graphics2D g2d = bufferedImage.createGraphics(); - // Draw graphics - g2d.setBackground(Color.white); - g2d.clearRect(0, 0, width, height); - paintComponent(g2d); - // Graphics context no longer needed so dispose it - g2d.dispose(); - return bufferedImage; - } - - /** - *

-     *   ---------------------- Page Edge --------------------------------------------------------
-     *   |                                        ^
-     *   |                                        |
-     *   |
-     *   |                                        y
-     *   |
-     *   |                                        |
-     *   P                                        v
-     *   a      ---                 +----------------------------+  ------------
-     *   g<------^-- x ------------>+                            +       ^
-     *   e       |                  +                            +       |
-     *           |                  +                            +     baseYOffset
-     *   E       |                  +                            +       v
-     *   d       |                  +<----------Fin------------->+ -------------
-     *   g       |                  +                            +
-     *   e       |                  +                            +
-     *   |       |                  +                            +
-     *   |       |                  +                            +
-     *   |       |                  +                            +   baseSpacing
-     *   |       |                  +                            +
-     *   |       |                  +                            +
-     *   |       |                  +                            +
-     *   |       |                  +                            +
-     *   |       |                  +<----------Fin------------->+  --------------
-     *   |       |                  +                            +
-     *   | circumferenceInPoints    +                            +
-     *   |       |                  +                            +
-     *   |       |                  +                            +
-     *   |       |                  +                            +    baseSpacing
-     *   |       |                  +<------Launch Lug --------->+           -----
-     *   |       |                  +                            +                 \
-     *   |       |                  +                            +                 + yLLOffset
-     *   |       |                  +                            +                 /
-     *   |       |                  +<----------Fin------------->+  --------------
-     *   |       |                  +                            +       ^
-     *   |       |                  +                            +       |
-     *   |       |                  +                            +    baseYOffset
-     *   |       v                  +                            +       v
-     *   |      ---                 +----------------------------+  --------------
-     *
-     *                              |<-------- width ----------->|
-     *
-     * yLLOffset is computed from the difference between the base rotation of the fin and the radial direction of the
-     * lug.
-     *
-     * Note: There is a current limitation that a tube with multiple launch lugs may not render the lug lines
-     * correctly.
-     * 
- * - * @param g the Graphics context - */ - @Override - public void paintComponent(Graphics g) { - super.paintComponent(g); - Graphics2D g2 = (Graphics2D) g; - paintFinMarkingGuide(g2); - } - - private void paintFinMarkingGuide(Graphics2D g2) { - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - - g2.setColor(Color.BLACK); - g2.setStroke(thinStroke); - int x = MARGIN; - int y = MARGIN; - - int width = (int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH); - - int column = 0; - - for (BodyTube next : markingGuideItems.keySet()) { - double circumferenceInPoints = PrintUnit.METERS.toPoints((next.getOuterRadius() + PAPER_THICKNESS_IN_METERS) * - TWO_PI); - List componentList = markingGuideItems.get(next); - //Don't draw the lug if there are no fins. - if (hasFins(componentList)) { - - drawMarkingGuide(g2, x, y, (int) Math.ceil(circumferenceInPoints), width); - - //Sort so that fins always precede lugs - sort(componentList); - - boolean hasMultipleComponents = componentList.size() > 1; - - double baseSpacing = 0d; - double baseYOrigin = 0; - double finRadial = 0d; - int yFirstFin = y; - int yLastFin = y; - boolean firstFinSet = true; - - //fin1: 42 fin2: 25 - for (ExternalComponent externalComponent : componentList) { - if (externalComponent instanceof FinSet) { - FinSet fins = (FinSet) externalComponent; - int finCount = fins.getFinCount(); - int offset = 0; - baseSpacing = (circumferenceInPoints / finCount); - double baseRotation = fins.getBaseRotation(); - if (!firstFinSet) { - //Adjust the rotation to a positive number. - while (baseRotation < 0) { - baseRotation += TWO_PI / finCount; - } - offset = computeYOffset(y, circumferenceInPoints, baseSpacing, baseYOrigin, finRadial, baseRotation); - } - else { - //baseYOrigin is the distance from the top of the marking guide to the first fin of the first fin set. - //This measurement is used to base all subsequent finsets and lugs off of. - baseYOrigin = baseSpacing / 2; - offset = (int) (baseYOrigin) + y; - firstFinSet = false; - } - yFirstFin = y; - yLastFin = y; - finRadial = baseRotation; - - //Draw the fin marking lines. - for (int fin = 0; fin < finCount; fin++) { - if (fin > 0) { - offset += baseSpacing; - yLastFin = offset; - } - else { - yFirstFin = offset; - } - drawDoubleArrowLine(g2, x, offset, x + width, offset); - // if (hasMultipleComponents) { - g2.drawString(externalComponent.getName(), x + (width / 3), offset - 2); - // } - } - } - else if (externalComponent instanceof LaunchLug) { - LaunchLug lug = (LaunchLug) externalComponent; - double yLLOffset = (lug.getRadialDirection() - finRadial) / TWO_PI; - //The placement of the lug line must respect the boundary of the outer marking guide. In order - //to do that, place it above or below either the top or bottom fin line, based on the difference - //between their rotational directions. - if (yLLOffset < 0) { - yLLOffset = yLLOffset * circumferenceInPoints + yLastFin; - } - else { - yLLOffset = yLLOffset * circumferenceInPoints + yFirstFin; - } - drawDoubleArrowLine(g2, x, (int) yLLOffset, x + width, (int) yLLOffset); - g2.drawString(lug.getName(), x + (width / 3), (int) yLLOffset - 2); - - } - } - //Only if the tube has a lug or multiple finsets does the orientation of the marking guide matter. So print 'Front'. - if (hasMultipleComponents) { - drawFrontIndication(g2, x, y, (int) baseSpacing, (int) circumferenceInPoints, width); - } - - //At most, two marking guides horizontally. After that, move down and back to the left margin. - column++; - if (column % 2 == 0) { - x = MARGIN; - y += circumferenceInPoints + MARGIN; - } - else { - x += MARGIN + width; - } - } - } - } - - /** - * Compute the y offset for the next fin line. - * - * @param y the top margin - * @param circumferenceInPoints the circumference (height) of the guide - * @param baseSpacing the circumference / fin count - * @param baseYOrigin the offset from the top of the guide to the first fin of the first fin set drawn - * @param prevBaseRotation the rotation of the previous finset - * @param baseRotation the rotation of the current finset - * - * @return number of points from the top of the marking guide to the line to be drawn - */ - private int computeYOffset(int y, double circumferenceInPoints, double baseSpacing, double baseYOrigin, double prevBaseRotation, double baseRotation) { - int offset; - double finRadialDifference = (baseRotation - prevBaseRotation) / TWO_PI; - //If the fin line would be off the top of the marking guide, then readjust. - if (baseYOrigin + finRadialDifference * circumferenceInPoints < 0) { - offset = (int) (baseYOrigin + baseSpacing + finRadialDifference * circumferenceInPoints) + y; - } - else if (baseYOrigin - finRadialDifference * circumferenceInPoints > 0) { - offset = (int) (finRadialDifference * circumferenceInPoints + baseYOrigin) + y; - } - else { - offset = (int) (finRadialDifference * circumferenceInPoints - baseYOrigin) + y; - } - return offset; - } - - /** - * Determines if the list contains a FinSet. - * - * @param list a list of ExternalComponent - * - * @return true if the list contains at least one FinSet - */ - private boolean hasFins(List list) { - for (ExternalComponent externalComponent : list) { - if (externalComponent instanceof FinSet) { - return true; - } - } - return false; - } - - /** - * Sort a list of ExternalComponent in-place. Forces FinSets to precede Launch Lugs. - * - * @param componentList a list of ExternalComponent - */ - private void sort(List componentList) { - Collections.sort(componentList, new Comparator() { - @Override - public int compare(ExternalComponent o1, ExternalComponent o2) { - if (o1 instanceof FinSet) { - return -1; - } - if (o2 instanceof FinSet) { - return 1; - } - return 0; - } - }); - } - - /** - * Draw the marking guide outline. - * - * @param g2 the graphics context - * @param x the starting x coordinate - * @param y the starting y coordinate - * @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube - * circumference - * @param width the width of the marking guide in print units; somewhat arbitrary - */ - private void drawMarkingGuide(Graphics2D g2, int x, int y, int length, int width) { - Path2D outline = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4); - outline.moveTo(x, y); - outline.lineTo(width + x, y); - outline.lineTo(width + x, length + y); - outline.lineTo(x, length + y); - outline.closePath(); - g2.draw(outline); - - //Draw tick marks for alignment, 1/4 of the width in from either edge - int fromEdge = (width) / 4; - final int tickLength = 8; - //Upper left - g2.drawLine(x + fromEdge, y, x + fromEdge, y + tickLength); - //Upper right - g2.drawLine(x + width - fromEdge, y, x + width - fromEdge, y + tickLength); - //Lower left - g2.drawLine(x + fromEdge, y + length - tickLength, x + fromEdge, y + length); - //Lower right - g2.drawLine(x + width - fromEdge, y + length - tickLength, x + width - fromEdge, y + length); - } - - /** - * Draw a vertical string indicating the front of the rocket. This is necessary when a launch lug exists to give - * proper orientation of the guide (assuming that the lug is asymmetrically positioned with respect to a fin). - * - * @param g2 the graphics context - * @param x the starting x coordinate - * @param y the starting y coordinate - * @param spacing the space between fin lines - * @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube - * circumference - * @param width the width of the marking guide in print units; somewhat arbitrary - */ - private void drawFrontIndication(Graphics2D g2, int x, int y, int spacing, int length, int width) { - //The magic numbers here are fairly arbitrary. They're chosen in a manner that best positions 'Front' to be - //readable, without going to complex string layout prediction logic. - int rotateX = x + width - 16; - int rotateY = y + (int) (spacing * 1.5) + 20; - if (rotateY > y + length + 14) { - rotateY = y + length / 2 - 10; - } - g2.translate(rotateX, rotateY); - g2.rotate(Math.PI / 2); - g2.drawString(trans.get("FinMarkingGuide.lbl.Front"), 0, 0); - g2.rotate(-Math.PI / 2); - g2.translate(-rotateX, -rotateY); - } - - /** - * Draw a horizontal line with arrows on both endpoints. Depicts a fin alignment. - * - * @param g2 the graphics context - * @param x1 the starting x coordinate - * @param y1 the starting y coordinate - * @param x2 the ending x coordinate - * @param y2 the ending y coordinate - */ - void drawDoubleArrowLine(Graphics2D g2, int x1, int y1, int x2, int y2) { - int len = x2 - x1; - - g2.drawLine(x1, y1, x1 + len, y2); - g2.fillPolygon(new int[]{x1 + len, x1 + len - ARROW_SIZE, x1 + len - ARROW_SIZE, x1 + len}, - new int[]{y2, y2 - ARROW_SIZE / 2, y2 + ARROW_SIZE / 2, y2}, 4); - - g2.fillPolygon(new int[]{x1, x1 + ARROW_SIZE, x1 + ARROW_SIZE, x1}, - new int[]{y1, y1 - ARROW_SIZE / 2, y1 + ARROW_SIZE / 2, y1}, 4); - } + + /** + * The stroke of normal lines. + */ + private final static BasicStroke thinStroke = new BasicStroke(1.0f); + + /** + * The size of the arrow in points. + */ + private static final int ARROW_SIZE = 10; + + /** + * Typical thickness of a piece of printer paper (~20-24 lb paper). Wrapping paper around a tube results in the + * radius being increased by the thickness of the paper. The smaller the tube, the more pronounced this becomes as a + * percentage of circumference. Using 1/10mm as an approximation here. + */ + private static final double PAPER_THICKNESS_IN_METERS = PrintUnit.MILLIMETERS.toMeters(0.1d); + + /** + * The default guide width in inches. + */ + public final static double DEFAULT_GUIDE_WIDTH = 3d; + + /** + * 2 PI radians (represents a circle). + */ + public final static double TWO_PI = 2 * Math.PI; + public final static double PI = Math.PI; + + /** + * The I18N translator. + */ + private static final Translator trans = Application.getTranslator(); + + /** + * The margin. + */ + private static final int MARGIN = (int) PrintUnit.INCHES.toPoints(0.25f); + + /** + * The height (circumference) of the biggest body tube with a finset. + */ + private int maxHeight = 0; + + /** + * A map of body tubes, to a list of components that contains finsets and launch lugs. + */ + private Map> markingGuideItems; + + /** + * Constructor. + * + * @param rocket the rocket instance + */ + public FinMarkingGuide(Rocket rocket) { + super(false); + setBackground(Color.white); + markingGuideItems = init(rocket); + //Max of 2 drawing guides horizontally per page. + setSize((int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH) * 2 + 3 * MARGIN, maxHeight); + } + + /** + * Initialize the marking guide class by iterating over a rocket and finding all finsets. + * + * @param component the root rocket component - this is iterated to find all finset and launch lugs + * + * @return a map of body tubes to lists of finsets and launch lugs. + */ + private Map> init(Rocket component) { + Iterator iter = component.iterator(false); + Map> results = new LinkedHashMap>(); + BodyTube current = null; + int totalHeight = 0; + int iterationHeight = 0; + int count = 0; + + while (iter.hasNext()) { + RocketComponent next = iter.next(); + if (next instanceof BodyTube) { + current = (BodyTube) next; + } + else if (next instanceof FinSet || next instanceof LaunchLug) { + java.util.List list = results.get(current); + if (list == null && current != null) { + list = new ArrayList(); + results.put(current, list); + + double radius = current.getOuterRadius(); + int circumferenceInPoints = (int) PrintUnit.METERS.toPoints(radius * TWO_PI); + + // Find the biggest body tube circumference. + if (iterationHeight < (circumferenceInPoints + MARGIN)) { + iterationHeight = circumferenceInPoints + MARGIN; + } + //At most, two marking guides horizontally. After that, move down and back to the left margin. + count++; + if (count % 2 == 0) { + totalHeight += iterationHeight; + iterationHeight = 0; + } + } + if (list != null) { + list.add((ExternalComponent) next); + } + } + } + maxHeight = totalHeight + iterationHeight; + return results; + } + + /** + * Returns a generated image of the fin marking guide. May then be used wherever AWT images can be used, or + * converted to another image/picture format and used accordingly. + * + * @return an awt image of the fin marking guide + */ + public Image createImage() { + int width = getWidth() + 25; + int height = getHeight() + 25; + // Create a buffered image in which to draw + BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + // Create a graphics context on the buffered image + Graphics2D g2d = bufferedImage.createGraphics(); + // Draw graphics + g2d.setBackground(Color.white); + g2d.clearRect(0, 0, width, height); + paintComponent(g2d); + // Graphics context no longer needed so dispose it + g2d.dispose(); + return bufferedImage; + } + + /** + *
+	 *   ---------------------- Page Edge --------------------------------------------------------
+	 *   |                                        ^
+	 *   |                                        |
+	 *   |
+	 *   |                                        y
+	 *   |
+	 *   |                                        |
+	 *   P                                        v
+	 *   a      ---                 +----------------------------+  <- radialOrigin (in radians)
+	 *   g<------^-- x ------------>+                            +
+	 *   e       |                  +                            +
+	 *           |                  +                            +
+	 *   E       |                  +                            +
+	 *   d       |                  +<----------Fin------------->+ <- y+ (finRadialPosition - radialOrigin) / TWO_PI * circumferenceInPoints
+	 *   g       |                  +                            +
+	 *   e       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +<----------Fin------------->+
+	 *   |       |                  +                            +
+	 *   | circumferenceInPoints    +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +<------Launch Lug --------->+
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +<----------Fin------------->+
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       |                  +                            +
+	 *   |       v                  +                            +
+	 *   |      ---                 +----------------------------+ <- radialOrigin + TWO_PI
+	 *
+	 *                              |<-------- width ----------->|
+	 *
+	 * yLLOffset is computed from the difference between the base rotation of the fin and the radial direction of the
+	 * lug.
+	 *
+	 * Note: There is a current limitation that a tube with multiple launch lugs may not render the lug lines
+	 * correctly.
+	 * 
+ * + * @param g the Graphics context + */ + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g; + paintFinMarkingGuide(g2); + } + + private void paintFinMarkingGuide(Graphics2D g2) { + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + g2.setColor(Color.BLACK); + g2.setStroke(thinStroke); + int x = MARGIN; + int y = MARGIN; + + int width = (int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH); + + int column = 0; + + for (BodyTube next : markingGuideItems.keySet()) { + double circumferenceInPoints = PrintUnit.METERS.toPoints((next.getOuterRadius() + PAPER_THICKNESS_IN_METERS) * + TWO_PI); + List componentList = markingGuideItems.get(next); + //Don't draw the lug if there are no fins. + if (hasFins(componentList)) { + + drawMarkingGuide(g2, x, y, (int) Math.ceil(circumferenceInPoints), width); + + double radialOrigin = findRadialOrigin(componentList); + + boolean hasMultipleComponents = componentList.size() > 1; + + //fin1: 42 fin2: 25 + for (ExternalComponent externalComponent : componentList) { + if (externalComponent instanceof FinSet) { + FinSet fins = (FinSet) externalComponent; + int finCount = fins.getFinCount(); + double baseAngularSpacing = (TWO_PI / finCount); + double baseAngularOffset = fins.getBaseRotation(); + //Draw the fin marking lines. + for (int fin = 0; fin < finCount; fin++) { + double angle = baseAngularOffset + fin * baseAngularSpacing - radialOrigin; + // Translate angle into pixels using a linear transformation: + // radialOrigin -> y + // radialOrigin + TWO_PI -> y + circumferenceInPoints + + while (angle < 0) { + angle += TWO_PI; + } + while (angle > TWO_PI) { + angle -= TWO_PI; + } + + int offset = (int) Math.round(y + angle / TWO_PI * circumferenceInPoints); + + drawDoubleArrowLine(g2, x, offset, x + width, offset); + // if (hasMultipleComponents) { + g2.drawString(externalComponent.getName(), x + (width / 3), offset - 2); + // } + } + } + else if (externalComponent instanceof LaunchLug) { + LaunchLug lug = (LaunchLug) externalComponent; + double angle = lug.getRadialDirection() - radialOrigin; + while (angle < 0) { + angle += TWO_PI; + } + int yLLOffset = (int) Math.round(y + angle / TWO_PI * circumferenceInPoints); + drawDoubleArrowLine(g2, x, (int) yLLOffset, x + width, (int) yLLOffset); + g2.drawString(lug.getName(), x + (width / 3), (int) yLLOffset - 2); + + } + } + //Only if the tube has a lug or multiple finsets does the orientation of the marking guide matter. So print 'Front'. + if (hasMultipleComponents) { + drawFrontIndication(g2, x, y, 0, (int) circumferenceInPoints, width); + } + + //At most, two marking guides horizontally. After that, move down and back to the left margin. + column++; + if (column % 2 == 0) { + x = MARGIN; + y += circumferenceInPoints + MARGIN; + } + else { + x += MARGIN + width; + } + } + } + } + + /** + * This function finds a origin in radians for the template so no component is on the template seam. + * + * If no fin or launch lug is at 0.0 radians, then the origin is 0. If there is one, then half the distance + * between the two are taken. + * + * @param components + * @return + */ + private double findRadialOrigin(List components) { + + ArrayList positions = new ArrayList(3 * components.size()); + + for (ExternalComponent component : components) { + + if (component instanceof LaunchLug) { + double componentPosition = ((LaunchLug) component).getRadialDirection(); + + positions.add(makeZeroTwoPi(componentPosition)); + } + + if (component instanceof FinSet) { + FinSet fins = (FinSet) component; + double basePosition = fins.getBaseRotation(); + double angle = TWO_PI / fins.getFinCount(); + for (int i = fins.getFinCount(); i > 0; i--) { + positions.add(makeZeroTwoPi(basePosition)); + basePosition += angle; + } + } + } + + Collections.sort(positions); + + Double[] pos = positions.toArray(new Double[0]); + + if (pos.length == 1) { + + return makeZeroTwoPi(pos[0] + PI); + + } + + double biggestDistance = TWO_PI - pos[pos.length - 1] + pos[0]; + double center = makeZeroTwoPi(pos[0] - biggestDistance / 2.0); + + for (int i = 1; i < pos.length; i++) { + + double d = pos[i] - pos[i - 1]; + if (d > biggestDistance) { + biggestDistance = d; + center = makeZeroTwoPi(pos[i - 1] + biggestDistance / 2.0); + } + } + + return center; + } + + private static double makeZeroTwoPi(double value) { + + double v = value; + while (v < 0) { + v += TWO_PI; + } + while (v > TWO_PI) { + v -= TWO_PI; + } + + return v; + } + + /** + * Determines if the list contains a FinSet. + * + * @param list a list of ExternalComponent + * + * @return true if the list contains at least one FinSet + */ + private boolean hasFins(List list) { + for (ExternalComponent externalComponent : list) { + if (externalComponent instanceof FinSet) { + return true; + } + } + return false; + } + + /** + * Draw the marking guide outline. + * + * @param g2 the graphics context + * @param x the starting x coordinate + * @param y the starting y coordinate + * @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube + * circumference + * @param width the width of the marking guide in print units; somewhat arbitrary + */ + private void drawMarkingGuide(Graphics2D g2, int x, int y, int length, int width) { + Path2D outline = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4); + outline.moveTo(x, y); + outline.lineTo(width + x, y); + outline.lineTo(width + x, length + y); + outline.lineTo(x, length + y); + outline.closePath(); + g2.draw(outline); + + //Draw tick marks for alignment, 1/4 of the width in from either edge + int fromEdge = (width) / 4; + final int tickLength = 8; + //Upper left + g2.drawLine(x + fromEdge, y, x + fromEdge, y + tickLength); + //Upper right + g2.drawLine(x + width - fromEdge, y, x + width - fromEdge, y + tickLength); + //Lower left + g2.drawLine(x + fromEdge, y + length - tickLength, x + fromEdge, y + length); + //Lower right + g2.drawLine(x + width - fromEdge, y + length - tickLength, x + width - fromEdge, y + length); + } + + /** + * Draw a vertical string indicating the front of the rocket. This is necessary when a launch lug exists to give + * proper orientation of the guide (assuming that the lug is asymmetrically positioned with respect to a fin). + * + * @param g2 the graphics context + * @param x the starting x coordinate + * @param y the starting y coordinate + * @param spacing the space between fin lines + * @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube + * circumference + * @param width the width of the marking guide in print units; somewhat arbitrary + */ + private void drawFrontIndication(Graphics2D g2, int x, int y, int spacing, int length, int width) { + //The magic numbers here are fairly arbitrary. They're chosen in a manner that best positions 'Front' to be + //readable, without going to complex string layout prediction logic. + int rotateX = x + width - 16; + int rotateY = y + (int) (spacing * 1.5) + 20; + if (rotateY > y + length + 14) { + rotateY = y + length / 2 - 10; + } + g2.translate(rotateX, rotateY); + g2.rotate(Math.PI / 2); + g2.drawString(trans.get("FinMarkingGuide.lbl.Front"), 0, 0); + g2.rotate(-Math.PI / 2); + g2.translate(-rotateX, -rotateY); + } + + /** + * Draw a horizontal line with arrows on both endpoints. Depicts a fin alignment. + * + * @param g2 the graphics context + * @param x1 the starting x coordinate + * @param y1 the starting y coordinate + * @param x2 the ending x coordinate + * @param y2 the ending y coordinate + */ + void drawDoubleArrowLine(Graphics2D g2, int x1, int y1, int x2, int y2) { + int len = x2 - x1; + + g2.drawLine(x1, y1, x1 + len, y2); + g2.fillPolygon(new int[] { x1 + len, x1 + len - ARROW_SIZE, x1 + len - ARROW_SIZE, x1 + len }, + new int[] { y2, y2 - ARROW_SIZE / 2, y2 + ARROW_SIZE / 2, y2 }, 4); + + g2.fillPolygon(new int[] { x1, x1 + ARROW_SIZE, x1 + ARROW_SIZE, x1 }, + new int[] { y1, y1 - ARROW_SIZE / 2, y1 + ARROW_SIZE / 2, y1 }, 4); + } } \ No newline at end of file diff --git a/core/src/net/sf/openrocket/gui/print/ITextHelper.java b/core/src/net/sf/openrocket/gui/print/ITextHelper.java index 456505439..6c17d92f1 100644 --- a/core/src/net/sf/openrocket/gui/print/ITextHelper.java +++ b/core/src/net/sf/openrocket/gui/print/ITextHelper.java @@ -3,9 +3,6 @@ */ package net.sf.openrocket.gui.print; -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; - import com.itextpdf.text.Chunk; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; @@ -19,24 +16,29 @@ import com.itextpdf.text.pdf.PdfPCell; import com.itextpdf.text.pdf.PdfPTable; import com.itextpdf.text.pdf.PdfWriter; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + /** * A bunch of helper methods for creating iText components. */ public final class ITextHelper { - public static BaseFont getBaseFont(){ - try { - return BaseFont.createFont("/dejavu-font/DejaVuSerif.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); - } catch (Exception ex ) { - throw new RuntimeException(ex); - } - } + public static BaseFont getBaseFont() { + try { + return BaseFont.createFont("/dejavu-font/DejaVuSerif.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + /** * Create a cell for an iText table. * * @return a cell with bottom border */ - public static PdfPCell createCell () { + public static PdfPCell createCell() { return createCell(Rectangle.BOTTOM); } @@ -47,7 +49,7 @@ public final class ITextHelper { * * @return a cell with given border */ - public static PdfPCell createCell (int border) { + public static PdfPCell createCell(int border) { PdfPCell result = new PdfPCell(); result.setBorder(border); @@ -61,7 +63,7 @@ public final class ITextHelper { * * @return the cell containing a table */ - public static PdfPCell createCell (PdfPTable table) { + public static PdfPCell createCell(PdfPTable table) { PdfPCell result = new PdfPCell(); result.setBorder(PdfPCell.NO_BORDER); result.addElement(table); @@ -76,7 +78,7 @@ public final class ITextHelper { * * @return the cell containing the text */ - public static PdfPCell createCell (String v) { + public static PdfPCell createCell(String v) { return createCell(v, Rectangle.NO_BORDER, PrintUtilities.NORMAL); } @@ -88,7 +90,7 @@ public final class ITextHelper { * * @return the cell containing the text */ - public static PdfPCell createCell (String v, Font font) { + public static PdfPCell createCell(String v, Font font) { return createCell(v, Rectangle.NO_BORDER, font); } @@ -101,7 +103,7 @@ public final class ITextHelper { * * @return the cell containing the text */ - public static PdfPCell createCell (String v, int leftPad, int rightPad) { + public static PdfPCell createCell(String v, int leftPad, int rightPad) { PdfPCell c = createCell(v, Rectangle.NO_BORDER, PrintUtilities.NORMAL); c.setPaddingLeft(leftPad); c.setPaddingRight(rightPad); @@ -116,13 +118,12 @@ public final class ITextHelper { * * @return the cell containing the text */ - public static PdfPCell createCell (String v, int border) { + public static PdfPCell createCell(String v, int border) { return createCell(v, border, PrintUtilities.NORMAL); } /** - * Complete create cell - fully qualified. Create a cell whose contents are the given string with the given border - * and font. + * Complete create cell - fully qualified. Create a cell whose contents are the given string with the given border and font. * * @param v the text of the cell * @param border the border type @@ -130,7 +131,7 @@ public final class ITextHelper { * * @return the cell containing the text */ - public static PdfPCell createCell (String v, int border, Font font) { + public static PdfPCell createCell(String v, int border, Font font) { PdfPCell result = new PdfPCell(); result.setBorder(border); Chunk c = new Chunk(); @@ -148,7 +149,7 @@ public final class ITextHelper { * * @return an iText phrase */ - public static Phrase createPhrase (String text, Font font) { + public static Phrase createPhrase(String text, Font font) { Phrase p = new Phrase(); final Chunk chunk = new Chunk(text); chunk.setFont(font); @@ -163,7 +164,7 @@ public final class ITextHelper { * * @return an iText phrase */ - public static Phrase createPhrase (String text) { + public static Phrase createPhrase(String text) { return createPhrase(text, PrintUtilities.NORMAL); } @@ -175,7 +176,7 @@ public final class ITextHelper { * * @return an iText paragraph */ - public static Paragraph createParagraph (String text, Font font) { + public static Paragraph createParagraph(String text, Font font) { Paragraph p = new Paragraph(); final Chunk chunk = new Chunk(text); chunk.setFont(font); @@ -190,14 +191,13 @@ public final class ITextHelper { * * @return an iText paragraph */ - public static Paragraph createParagraph (String text) { + public static Paragraph createParagraph(String text) { return createParagraph(text, PrintUtilities.NORMAL); } /** - * Break a large image up into page-size pieces and output each page in order to an iText document. The image is - * overlayed with an matrix of pages running from left to right until the right side of the image is reached. Then - * the next 'row' of pages is output from left to right, and so on. + * Break a large image up into page-size pieces and output each page in order to an iText document. The image is overlayed with an matrix of pages + * running from left to right until the right side of the image is reached. Then the next 'row' of pages is output from left to right, and so on. * * @param pageSize a rectangle that defines the bounds of the page size * @param doc the iText document @@ -206,9 +206,9 @@ public final class ITextHelper { * * @throws DocumentException thrown if the document could not be written */ - public static void renderImageAcrossPages (Rectangle pageSize, Document doc, PdfWriter writer, java.awt.Image image) + public static void renderImageAcrossPages(Rectangle pageSize, Document doc, PdfWriter writer, java.awt.Image image) throws DocumentException { - final int margin = (int)Math.min(doc.topMargin(), PrintUnit.POINTS_PER_INCH * 0.3f); + final int margin = (int) Math.min(doc.topMargin(), PrintUnit.POINTS_PER_INCH * 0.3f); float wPage = pageSize.getWidth() - 2 * margin; float hPage = pageSize.getHeight() - 2 * margin; @@ -218,7 +218,7 @@ public final class ITextHelper { hImage)); PdfContentByte content = writer.getDirectContent(); - int ymargin = 0; + int ymargin = margin; while (true) { BufferedImage subImage = ((BufferedImage) image).getSubimage((int) crop.getX(), (int) crop.getY(), @@ -228,20 +228,21 @@ public final class ITextHelper { g2.drawImage(subImage, margin, ymargin, null); g2.dispose(); - // After the first page, the y-margin needs to be set. - ymargin = margin; + final int newImageX = (int) (crop.getWidth() + crop.getX()); - final int newX = (int) (crop.getWidth() + crop.getX()); - if (newX < wImage) { - double adjust = Math.min(wImage - newX, wPage); - crop = new java.awt.Rectangle(newX, (int) crop.getY(), (int) adjust, + if (newImageX < wImage) { + //Spans multiple pages horizontally + double subImageWidth = Math.min(wImage - newImageX, wPage); + crop = new java.awt.Rectangle(newImageX, (int) crop.getY(), (int) subImageWidth, (int) crop.getHeight()); } else { - final int newY = (int) (crop.getHeight() + crop.getY()); - if (newY < hImage) { - double adjust = Math.min(hImage - newY, hPage); - crop = new java.awt.Rectangle(0, newY, (int) Math.min(wPage, wImage), (int) adjust); + //Spans multiple pages vertically + final int newImageY = (int) (crop.getHeight() + crop.getY()); + + if (newImageY < hImage) { + double subImageHeight = Math.min(hImage - newImageY, hPage); + crop = new java.awt.Rectangle(0, newImageY, (int) Math.min(wPage, wImage), (int) subImageHeight); } else { break; diff --git a/core/src/net/sf/openrocket/gui/print/PrintController.java b/core/src/net/sf/openrocket/gui/print/PrintController.java index 2e62df2a2..fd3f04a3c 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintController.java +++ b/core/src/net/sf/openrocket/gui/print/PrintController.java @@ -89,15 +89,15 @@ public class PrintController { break; case TRANSITION_TEMPLATE: - final TransitionStrategy tranWriter = new TransitionStrategy(idoc, writer, stages, pageFitPrint); - if (tranWriter.writeToDocument(doc.getRocket(), false)) { + final TransitionStrategy tranWriter = new TransitionStrategy(idoc, writer, stages, pageFitPrint, false); + if (tranWriter.writeToDocument(doc.getRocket())) { addRule = true; } break; case NOSE_CONE_TEMPLATE: - final TransitionStrategy coneWriter = new TransitionStrategy(idoc, writer, stages, pageFitPrint); - if (coneWriter.writeToDocument(doc.getRocket(), true)) { + final TransitionStrategy coneWriter = new TransitionStrategy(idoc, writer, stages, pageFitPrint, true); + if (coneWriter.writeToDocument(doc.getRocket())) { addRule = true; } break; @@ -124,7 +124,7 @@ public class PrintController { } // Write out parts that we are going to combine onto single sheets of paper - pageFitPrint.writeToDocument(doc.getRocket()); + pageFitPrint.writeToDocument(); idoc.newPage(); //Stupid iText throws a really nasty exception if there is no data when close is called. diff --git a/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java b/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java index 00c4e89c0..4bd361bc1 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java +++ b/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java @@ -1,6 +1,6 @@ package net.sf.openrocket.gui.print; -import net.sf.openrocket.gui.print.visitor.CenteringRingStrategy; +import net.sf.openrocket.gui.print.visitor.Dimension; import net.sf.openrocket.rocketcomponent.CenteringRing; import net.sf.openrocket.rocketcomponent.ClusterConfiguration; import net.sf.openrocket.rocketcomponent.InnerTube; @@ -34,7 +34,7 @@ public class PrintableCenteringRing extends AbstractPrintable { /** * A set of the inner 'holes'. At least one, but will have many if clustered. */ - private Set innerCenterPoints = new HashSet(); + private Set innerCenterPoints = new HashSet(); /** * Construct a simple, non-clustered, printable centering ring, or if the motor mount represents a clustered @@ -44,12 +44,12 @@ public class PrintableCenteringRing extends AbstractPrintable { * @param theMotorMount the motor mount if clustered, else null */ private PrintableCenteringRing(CenteringRing theRing, InnerTube theMotorMount) { - super(false, theRing); + super(theRing); if (theMotorMount == null || theMotorMount.getClusterConfiguration().equals(ClusterConfiguration.SINGLE)) { //Single motor. final float v = (float) PrintUnit.METERS.toPoints(target.getOuterRadius()); innerCenterPoints.add( - new CenteringRingStrategy.Dimension(v, v, + new Dimension(v, v, (float) PrintUnit.METERS.toPoints((target.getInnerRadius())))); } else { @@ -70,7 +70,7 @@ public class PrintableCenteringRing extends AbstractPrintable { * @param theMotorMounts a list of the motor mount tubes that are physically supported by the centering ring */ private PrintableCenteringRing(CenteringRing theRing, List theMotorMounts) { - super(false, theRing); + super(theRing); List points = new ArrayList(); //Transform the radial positions of the tubes. for (InnerTube it : theMotorMounts) { @@ -117,7 +117,7 @@ public class PrintableCenteringRing extends AbstractPrintable { private void populateCenterPoints(final List theCoords) { float radius = (float) PrintUnit.METERS.toPoints(target.getOuterRadius()); for (Coordinate coordinate : theCoords) { - innerCenterPoints.add(new CenteringRingStrategy.Dimension( + innerCenterPoints.add(new Dimension( (float) PrintUnit.METERS.toPoints(coordinate.y) + radius, //center point x (float) PrintUnit.METERS.toPoints(coordinate.z) + radius, //center point y (float) PrintUnit.METERS.toPoints(coordinate.x))); //radius of motor mount @@ -153,7 +153,7 @@ public class PrintableCenteringRing extends AbstractPrintable { g2.setColor(Color.black); g2.draw(outerCircle); - for (CenteringRingStrategy.Dimension next : innerCenterPoints) { + for (Dimension next : innerCenterPoints) { drawInnerCircle(g2, next.getWidth(), next.getHeight(), next.getBreadth()); } g2.setColor(original); diff --git a/core/src/net/sf/openrocket/gui/print/PrintableComponent.java b/core/src/net/sf/openrocket/gui/print/PrintableComponent.java index de345fb9c..4abaed907 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintableComponent.java +++ b/core/src/net/sf/openrocket/gui/print/PrintableComponent.java @@ -92,7 +92,6 @@ public class PrintableComponent extends JPanel implements Printable, Comparable< return offsetY; } - /** * Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer * as this object is less than, equal to, or greater than the specified object. diff --git a/core/src/net/sf/openrocket/gui/print/PrintableFinSet.java b/core/src/net/sf/openrocket/gui/print/PrintableFinSet.java index 8e5389b2b..60d35a005 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintableFinSet.java +++ b/core/src/net/sf/openrocket/gui/print/PrintableFinSet.java @@ -6,33 +6,29 @@ package net.sf.openrocket.gui.print; import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.util.Coordinate; -import java.awt.Color; -import java.awt.Graphics; import java.awt.Graphics2D; -import java.awt.Image; import java.awt.geom.GeneralPath; -import java.awt.image.BufferedImage; /** * This class allows for a FinSet to be printable. It does so by decorating an existing finset (which will not be * modified) and rendering it within a JPanel. The JPanel is not actually visualized on a display, but instead renders * it to a print device. */ -public class PrintableFinSet extends PrintableComponent { +public class PrintableFinSet extends AbstractPrintable { /** * The object that represents the shape (outline) of the fin. This gets drawn onto the Swing component. */ - protected GeneralPath polygon = null; + protected GeneralPath polygon; /** * The minimum X coordinate. */ - private int minX = 0; + private int minX; /** * The minimum Y coordinate. */ - private int minY = 0; + private int minY; /** * Constructor. @@ -40,28 +36,23 @@ public class PrintableFinSet extends PrintableComponent { * @param fs the finset to print */ public PrintableFinSet (FinSet fs) { - this(fs.getFinPointsWithTab()); - } - - /** - * Construct a fin set from a set of points. - * - * @param points an array of points. - */ - public PrintableFinSet (Coordinate[] points) { - init(points); + super(fs); } /** * Initialize the fin set polygon and set the size of the component. * - * @param points an array of points. + * @param component the fin set */ - private void init (Coordinate[] points) { + protected void init (FinSet component) { + + Coordinate[] points = component.getFinPointsWithTab(); polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, points.length); polygon.moveTo(0, 0); + minX = 0; + minY = 0; int maxX = 0; int maxY = 0; @@ -76,29 +67,7 @@ public class PrintableFinSet extends PrintableComponent { } polygon.closePath(); - setSize(maxX - minX, maxY - minY); - } - - /** - * Returns a generated image of the fin set. May then be used wherever AWT images can be used, or converted to - * another image/picture format and used accordingly. - * - * @return an awt image of the fin set - */ - public Image createImage () { - int width = getWidth(); - int height = getHeight(); - // Create a buffered image in which to draw - BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - // Create a graphics contents on the buffered image - Graphics2D g2d = bufferedImage.createGraphics(); - // Draw graphics - g2d.setBackground(Color.white); - g2d.clearRect(0, 0, width, height); - paintComponent(g2d); - // Graphics context no longer needed so dispose it - g2d.dispose(); - return bufferedImage; + setSize(maxX - minX + 1, maxY - minY + 1); } /** @@ -106,12 +75,9 @@ public class PrintableFinSet extends PrintableComponent { * outline of the fin set coordinates to create a polygon, which is then drawn onto the graphics context. * Through-the-wall fin tabs are supported if they are present. * - * @param g the Java2D graphics context + * @param g2d the Java2D graphics context */ - @Override - public void paintComponent(Graphics g) { - Graphics2D g2d = (Graphics2D) g; - + protected void draw(Graphics2D g2d) { int x = 0; int y = 0; @@ -132,4 +98,13 @@ public class PrintableFinSet extends PrintableComponent { g2d.setPaint(TemplateProperties.getLineColor()); g2d.draw(polygon); } + + /** + * Don't let super class translate the coordinates - we'll do that ourselves in the draw method. + * + * @param theG2 the graphics context + */ + @Override + protected void translate(final Graphics2D theG2) { + } } diff --git a/core/src/net/sf/openrocket/gui/print/PrintableNoseCone.java b/core/src/net/sf/openrocket/gui/print/PrintableNoseCone.java index 2cfdfa688..dd6847108 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintableNoseCone.java +++ b/core/src/net/sf/openrocket/gui/print/PrintableNoseCone.java @@ -1,5 +1,6 @@ package net.sf.openrocket.gui.print; +import net.sf.openrocket.gui.print.visitor.PageFitPrintStrategy; import net.sf.openrocket.gui.rocketfigure.TransitionShapes; import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.util.Transformation; @@ -10,50 +11,71 @@ import java.awt.Shape; public class PrintableNoseCone extends AbstractPrintable { - /** - * If the component to be drawn is a nose cone, save a reference to it. - */ - private NoseCone target; + /** + * If the component to be drawn is a nose cone, save a reference to it. + */ + private NoseCone target; - /** - * Construct a printable nose cone. - * - * @param noseCone the component to print - */ - public PrintableNoseCone(NoseCone noseCone) { - super(false, noseCone); - } + /** + * Offset for shoulder radius edge case. + */ + private int xOffset; - @Override - protected void init(NoseCone component) { + /** + * Construct a printable nose cone. + * + * @param noseCone the component to print + */ + public PrintableNoseCone(NoseCone noseCone) { + super(noseCone); + } - target = component; - double radius = target.getForeRadius(); - if (radius < target.getAftRadius()) { - radius = target.getAftRadius(); - } - setSize((int) PrintUnit.METERS.toPoints(2 * radius) + 4, - (int) PrintUnit.METERS.toPoints(target.getLength() + target.getAftShoulderLength()) + 4); - } + @Override + protected void init(NoseCone component) { - /** - * Draw a nose cone. Presumes that the graphics context has already had the x/y position translated based on - * where it should be drawn. - * - * @param g2 the graphics context - */ - @Override - protected void draw(Graphics2D g2) { - Shape[] shapes = TransitionShapes.getShapesSide(target, Transformation.rotate_x(0d), PrintUnit.METERS.toPoints(1)); + target = component; + xOffset = 0; + double radius = target.getForeRadius(); + if (radius < target.getAftRadius()) { + radius = target.getAftRadius(); + } + //Really odd edge case where the shoulder radius exceeds the radius of the nose cone. + if (radius < target.getAftShoulderRadius()) { + double tmp = radius; + radius = target.getAftShoulderRadius(); + xOffset = (int) PrintUnit.METERS.toPoints(radius - tmp) + PageFitPrintStrategy.MARGIN; + } + setSize((int) PrintUnit.METERS.toPoints(2 * radius) + 4, + (int) PrintUnit.METERS.toPoints(target.getLength() + target.getAftShoulderLength()) + 4); + } - if (shapes != null && shapes.length > 0) { - Rectangle r = shapes[0].getBounds(); - g2.translate(r.getHeight() / 2, 0); - g2.rotate(Math.PI / 2); - for (Shape shape : shapes) { - g2.draw(shape); - } - g2.rotate(-Math.PI / 2); - } - } + /** + * Draw a nose cone. Presumes that the graphics context has already had the x/y position translated based on where it should be drawn. + * + * @param g2 the graphics context + */ + @Override + protected void draw(Graphics2D g2) { + Shape[] shapes = TransitionShapes.getShapesSide(target, Transformation.rotate_x(0d), PrintUnit.METERS.toPoints(1)); + + if (shapes != null && shapes.length > 0) { + Rectangle r = shapes[0].getBounds(); + g2.translate(r.getHeight() / 2, 0); + g2.rotate(Math.PI / 2); + for (Shape shape : shapes) { + g2.draw(shape); + } + g2.rotate(-Math.PI / 2); + } + } + + @Override + protected void translate(final Graphics2D theG2) { + if (xOffset == 0) { + super.translate(theG2); + } + else { + theG2.translate(xOffset, getOffsetY()); + } + } } diff --git a/core/src/net/sf/openrocket/gui/print/PrintableTransition.java b/core/src/net/sf/openrocket/gui/print/PrintableTransition.java index 9aa2d9d3d..6ff994092 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintableTransition.java +++ b/core/src/net/sf/openrocket/gui/print/PrintableTransition.java @@ -69,7 +69,7 @@ public class PrintableTransition extends AbstractPrintable { * @param transition the transition to print */ public PrintableTransition(Transition transition) { - super(false, transition); + super(transition); } /** diff --git a/core/src/net/sf/openrocket/gui/print/visitor/AbstractPrintStrategy.java b/core/src/net/sf/openrocket/gui/print/visitor/AbstractPrintStrategy.java new file mode 100644 index 000000000..869c7aaae --- /dev/null +++ b/core/src/net/sf/openrocket/gui/print/visitor/AbstractPrintStrategy.java @@ -0,0 +1,137 @@ +package net.sf.openrocket.gui.print.visitor; + +import com.itextpdf.text.Document; +import com.itextpdf.text.DocumentException; +import com.itextpdf.text.Rectangle; +import com.itextpdf.text.pdf.PdfWriter; +import net.sf.openrocket.gui.print.AbstractPrintable; +import net.sf.openrocket.gui.print.ITextHelper; +import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; + +import java.awt.image.BufferedImage; +import java.util.List; +import java.util.Set; + +/** + * Common logic for printing strategies. + */ +public abstract class AbstractPrintStrategy { + /** + * The logger. + */ + protected static final LogHelper log = Application.getLogger(); + /** + * The iText document. + */ + protected Document document; + /** + * The direct iText writer. + */ + protected PdfWriter writer; + /** + * The stages selected. + */ + protected Set stages; + /** + * Strategy for fitting multiple components onto a page. + */ + protected PageFitPrintStrategy pageFitPrint; + + /** + * Constructor. + * + * @param doc the document + * @param pageFit the page fitting strategy + * @param theWriter the pdf writer + * @param theStages the set of stages in the rocket + */ + public AbstractPrintStrategy(Document doc, PageFitPrintStrategy pageFit, PdfWriter theWriter, + Set theStages) { + document = doc; + pageFitPrint = pageFit; + writer = theWriter; + stages = theStages; + } + + /** + * Recurse through the given rocket component. + * + * @param root the root component; all children will be printed recursively + */ + public V writeToDocument(final RocketComponent root) { + return goDeep(root.getChildren()); + } + + /** + * Recurse through the given rocket component. + * + * @param theRc an array of rocket components; all children will be printed recursively + */ + protected abstract V goDeep(List theRc); + + /** + * Determine if the image will fit on the given page. + * + * @param pageSize the page size + * @param wImage the width of the thing to be printed + * @param hImage the height of the thing to be printed + * + * @return true if the thing to be printed will fit on a single page + */ + protected boolean fitsOnOnePage(Dimension pageSize, double wImage, double hImage) { + double wPage = pageSize.getWidth() - PageFitPrintStrategy.MARGIN * 2; + double hPage = pageSize.getHeight() - PageFitPrintStrategy.MARGIN * 2; + + int wRatio = (int) Math.ceil(wImage / wPage); + int hRatio = (int) Math.ceil(hImage / hPage); + + return wRatio <= 1.0d && hRatio <= 1.0d; + } + + /** + * Get the dimensions of the paper page. + * + * @return an internal Dimension + */ + protected Dimension getPageSize() { + return new Dimension(document.getPageSize().getWidth(), + document.getPageSize().getHeight()); + } + + /** + * Determine if the strategy's set of stage numbers (to print) contains the specified stage. + * + * @param stageNumber a stage number + * + * @return true if the strategy contains the stage number provided + */ + public boolean shouldPrintStage(int stageNumber) { + if (stages == null || stages.isEmpty()) { + return false; + } + + for (final Integer stage : stages) { + if (stage == stageNumber) { + return true; + } + } + + return false; + } + + void render(final AbstractPrintable thePrintable) throws DocumentException { + java.awt.Dimension size = thePrintable.getSize(); + final Dimension pageSize = getPageSize(); + if (fitsOnOnePage(pageSize, size.getWidth(), size.getHeight())) { + pageFitPrint.addComponent(thePrintable); + } + else { + BufferedImage image = (BufferedImage) thePrintable.createImage(); + ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()), + document, writer, image); + document.newPage(); + } + } +} diff --git a/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java b/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java index 70428f85a..e1f69e140 100644 --- a/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java +++ b/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java @@ -2,52 +2,21 @@ package net.sf.openrocket.gui.print.visitor; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; -import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.PdfWriter; import net.sf.openrocket.gui.print.AbstractPrintable; -import net.sf.openrocket.gui.print.ITextHelper; -import net.sf.openrocket.gui.print.PrintUnit; import net.sf.openrocket.gui.print.PrintableCenteringRing; -import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.rocketcomponent.CenteringRing; import net.sf.openrocket.rocketcomponent.InnerTube; import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.ArrayList; -import java.awt.image.BufferedImage; import java.util.List; import java.util.Set; /** * A strategy for printing a centering ring to iText. */ -public class CenteringRingStrategy { - - /** - * The logger. - */ - private static final LogHelper log = Application.getLogger(); - - /** - * The iText document. - */ - protected Document document; - - /** - * The direct iText writer. - */ - protected PdfWriter writer; - - /** - * The stages selected. - */ - protected Set stages; - - /** - * Strategy for fitting multiple components onto a page. - */ - protected PageFitPrintStrategy pageFitPrint; +public class CenteringRingStrategy extends AbstractPrintStrategy { /** * Constructor. @@ -57,29 +26,19 @@ public class CenteringRingStrategy { * @param theStagesToVisit The stages to be visited by this strategy */ public CenteringRingStrategy(Document doc, PdfWriter theWriter, Set theStagesToVisit, PageFitPrintStrategy pageFit) { + super(doc, pageFit, theWriter, theStagesToVisit); document = doc; writer = theWriter; stages = theStagesToVisit; pageFitPrint = pageFit; } - /** - * Recurse through the given rocket component. - * - * @param root the root component; all children will be visited recursively - */ - public void writeToDocument(final RocketComponent root) { - List rc = root.getChildren(); - goDeep(rc); - } - - /** * Recurse through the given rocket component. * * @param theRc an array of rocket components; all children will be visited recursively */ - protected void goDeep(final List theRc) { + protected Void goDeep(final List theRc) { for (RocketComponent rocketComponent : theRc) { if (rocketComponent instanceof CenteringRing) { render((CenteringRing) rocketComponent); @@ -88,6 +47,7 @@ public class CenteringRingStrategy { goDeep(rocketComponent.getChildren()); } } + return null; } /** @@ -141,130 +101,21 @@ public class CenteringRingStrategy { } /** - * The core behavior of this visitor. + * The core behavior of this strategy. * * @param component the object to extract info about; a graphical image of the centering ring shape is drawn to the * document */ private void render(final CenteringRing component) { try { - AbstractPrintable pfs; + AbstractPrintable pfs; pfs = PrintableCenteringRing.create(component, findMotorMount(component)); - java.awt.Dimension size = pfs.getSize(); - final Dimension pageSize = getPageSize(); - if (fitsOnOnePage(pageSize, size.getWidth(), size.getHeight())) { - pageFitPrint.addComponent(pfs); - } - else { - int off = (int) (PrintUnit.POINTS_PER_INCH * 0.3f); - pfs.setPrintOffset(off, off); - BufferedImage image = (BufferedImage) pfs.createImage(); - ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()), - document, writer, image); - document.newPage(); - } + render(pfs); } catch (DocumentException e) { log.error("Could not render the centering ring.", e); } } - /** - * Determine if the image will fit on the given page. - * - * @param pageSize the page size - * @param wImage the width of the thing to be printed - * @param hImage the height of the thing to be printed - * - * @return true if the thing to be printed will fit on a single page - */ - private boolean fitsOnOnePage(Dimension pageSize, double wImage, double hImage) { - double wPage = pageSize.getWidth(); - double hPage = pageSize.getHeight(); - - int wRatio = (int) Math.ceil(wImage / wPage); - int hRatio = (int) Math.ceil(hImage / hPage); - - return wRatio <= 1.0d && hRatio <= 1.0d; - } - - /** - * Get the dimensions of the paper page. - * - * @return an internal Dimension - */ - protected Dimension getPageSize() { - return new Dimension(document.getPageSize().getWidth(), - document.getPageSize().getHeight()); - } - - /** - * Convenience class to model a dimension. - */ - public static class Dimension { - /** - * Width, in points. - */ - public float width; - /** - * Height, in points. - */ - public float height; - /** - * Breadth, in points. - */ - public float breadth = 0f; - - /** - * Constructor. - * - * @param w width - * @param h height - */ - public Dimension(float w, float h) { - width = w; - height = h; - } - - /** - * Constructor. - * - * @param w width - * @param h height - * @param b breadth; optionally used to represent radius - */ - public Dimension(float w, float h, float b) { - width = w; - height = h; - breadth = b; - } - - /** - * Get the width. - * - * @return the width - */ - public float getWidth() { - return width; - } - - /** - * Get the height. - * - * @return the height - */ - public float getHeight() { - return height; - } - - /** - * Get the breadth. - * - * @return the breadth - */ - public float getBreadth() { - return breadth; - } - } } diff --git a/core/src/net/sf/openrocket/gui/print/visitor/Dimension.java b/core/src/net/sf/openrocket/gui/print/visitor/Dimension.java new file mode 100644 index 000000000..70dbf85e6 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/print/visitor/Dimension.java @@ -0,0 +1,70 @@ +package net.sf.openrocket.gui.print.visitor; + +/** + * Convenience class to model a dimension. + */ +public class Dimension { + /** + * Width, in points. + */ + public float width; + /** + * Height, in points. + */ + public float height; + /** + * Breadth, in points. + */ + public float breadth = 0f; + + /** + * Constructor. + * + * @param w width + * @param h height + */ + public Dimension(float w, float h) { + width = w; + height = h; + } + + /** + * Constructor. + * + * @param w width + * @param h height + * @param b breadth; optionally used to represent radius + */ + public Dimension(float w, float h, float b) { + width = w; + height = h; + breadth = b; + } + + /** + * Get the width. + * + * @return the width + */ + public float getWidth() { + return width; + } + + /** + * Get the height. + * + * @return the height + */ + public float getHeight() { + return height; + } + + /** + * Get the breadth. + * + * @return the breadth + */ + public float getBreadth() { + return breadth; + } +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/gui/print/visitor/FinSetPrintStrategy.java b/core/src/net/sf/openrocket/gui/print/visitor/FinSetPrintStrategy.java index 387049083..e25807988 100644 --- a/core/src/net/sf/openrocket/gui/print/visitor/FinSetPrintStrategy.java +++ b/core/src/net/sf/openrocket/gui/print/visitor/FinSetPrintStrategy.java @@ -5,49 +5,19 @@ package net.sf.openrocket.gui.print.visitor; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; -import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.PdfWriter; -import net.sf.openrocket.gui.print.ITextHelper; -import net.sf.openrocket.gui.print.PrintUnit; +import net.sf.openrocket.gui.print.AbstractPrintable; import net.sf.openrocket.gui.print.PrintableFinSet; -import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.startup.Application; -import java.awt.image.BufferedImage; import java.util.List; import java.util.Set; /** * A strategy for drawing fin templates. */ -public class FinSetPrintStrategy { - - /** - * The logger. - */ - private static final LogHelper log = Application.getLogger(); - - /** - * The iText document. - */ - protected Document document; - - /** - * The direct iText writer. - */ - protected PdfWriter writer; - - /** - * The stages selected. - */ - protected Set stages; - - /** - * Strategy for fitting multiple components onto a page. - */ - protected PageFitPrintStrategy pageFitPrint; +public class FinSetPrintStrategy extends AbstractPrintStrategy { /** * Constructor. @@ -57,37 +27,25 @@ public class FinSetPrintStrategy { * @param theStages The stages to be printed by this strategy */ public FinSetPrintStrategy(Document doc, PdfWriter theWriter, Set theStages, PageFitPrintStrategy pageFit) { - document = doc; - writer = theWriter; - stages = theStages; - pageFitPrint = pageFit; + super(doc, pageFit, theWriter, theStages); } - /** - * Recurse through the given rocket component. - * - * @param root the root component; all children will be printed recursively - */ - public void writeToDocument (final RocketComponent root) { - List rc = root.getChildren(); - goDeep(rc); - } - - /** * Recurse through the given rocket component. * * @param theRc an array of rocket components; all children will be printed recursively */ - protected void goDeep (final List theRc) { + @Override + protected Void goDeep (final List theRc) { for (RocketComponent rocketComponent : theRc) { if (rocketComponent instanceof FinSet) { - printFinSet((FinSet) rocketComponent); + render((FinSet) rocketComponent); } else if (rocketComponent.getChildCount() > 0) { goDeep(rocketComponent.getChildren()); } } + return null; } /** @@ -95,24 +53,11 @@ public class FinSetPrintStrategy { * * @param finSet the object to extract info about; a graphical image of the fin shape is drawn to the document */ - private void printFinSet(final FinSet finSet) { + private void render(final FinSet finSet) { if (shouldPrintStage(finSet.getStageNumber())) { try { - PrintableFinSet pfs = new PrintableFinSet(finSet); - - java.awt.Dimension finSize = pfs.getSize(); - final Dimension pageSize = getPageSize(); - if (fitsOnOnePage(pageSize, finSize.getWidth(), finSize.getHeight())) { - pageFitPrint.addComponent(pfs); - } - else { - int off = (int)(PrintUnit.POINTS_PER_INCH * 0.3f); - pfs.setPrintOffset(off, off); - BufferedImage image = (BufferedImage) pfs.createImage(); - ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()), - document, writer, image); - document.newPage(); - } + AbstractPrintable pfs = new PrintableFinSet(finSet); + render(pfs); } catch (DocumentException e) { log.error("Could not render fin.", e); @@ -120,91 +65,4 @@ public class FinSetPrintStrategy { } } - /** - * Determine if the strategy's set of stage numbers (to print) contains the specified stage. - * - * @param stageNumber a stage number - * - * @return true if the strategy contains the stage number provided - */ - public boolean shouldPrintStage(int stageNumber) { - if (stages == null || stages.isEmpty()) { - return false; - } - - for (final Integer stage : stages) { - if (stage == stageNumber) { - return true; - } - } - - return false; - } - - /** - * Determine if the image will fit on the given page. - * - * @param pageSize the page size - * @param wImage the width of the thing to be printed - * @param hImage the height of the thing to be printed - * - * @return true if the thing to be printed will fit on a single page - */ - private boolean fitsOnOnePage (Dimension pageSize, double wImage, double hImage) { - double wPage = pageSize.getWidth(); - double hPage = pageSize.getHeight(); - - int wRatio = (int) Math.ceil(wImage / wPage); - int hRatio = (int) Math.ceil(hImage / hPage); - - return wRatio <= 1.0d && hRatio <= 1.0d; - } - - /** - * Get the dimensions of the paper page. - * - * @return an internal Dimension - */ - protected Dimension getPageSize () { - return new Dimension(document.getPageSize().getWidth(), - document.getPageSize().getHeight()); - } - - /** - * Convenience class to model a dimension. - */ - class Dimension { - /** Width, in points. */ - public float width; - /** Height, in points. */ - public float height; - - /** - * Constructor. - * @param w width - * @param h height - */ - public Dimension (float w, float h) { - width = w; - height = h; - } - - /** - * Get the width. - * - * @return the width - */ - public float getWidth () { - return width; - } - - /** - * Get the height. - * - * @return the height - */ - public float getHeight () { - return height; - } - } -} +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/gui/print/visitor/PageFitPrintStrategy.java b/core/src/net/sf/openrocket/gui/print/visitor/PageFitPrintStrategy.java index 5bc14f5ef..c9fe0aa4d 100644 --- a/core/src/net/sf/openrocket/gui/print/visitor/PageFitPrintStrategy.java +++ b/core/src/net/sf/openrocket/gui/print/visitor/PageFitPrintStrategy.java @@ -8,7 +8,9 @@ import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfWriter; import net.sf.openrocket.gui.print.PrintUnit; import net.sf.openrocket.gui.print.PrintableComponent; -import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.startup.Application; + import java.awt.Graphics2D; import java.util.ArrayList; import java.util.Collections; @@ -22,6 +24,14 @@ import java.util.Set; */ public class PageFitPrintStrategy { + /** The margin. */ + public final static int MARGIN = (int)(PrintUnit.POINTS_PER_INCH * 0.3f); + + /** + * The logger. + */ + private static final LogHelper log = Application.getLogger(); + /** * The iText document. */ @@ -62,10 +72,8 @@ public class PageFitPrintStrategy { /** * Recurse through the given rocket component. - * - * @param root the root component; all children will be printed recursively */ - public void writeToDocument (final RocketComponent root) { + public void writeToDocument () { fitPrintComponents(); } @@ -76,8 +84,8 @@ public class PageFitPrintStrategy { final Dimension pageSize = getPageSize(); double wPage = pageSize.getWidth(); double hPage = pageSize.getHeight(); - int marginX = (int)(PrintUnit.POINTS_PER_INCH * 0.3f); - int marginY = (int)(PrintUnit.POINTS_PER_INCH * 0.3f); + int marginX = MARGIN; + int marginY = MARGIN; PdfContentByte cb = writer.getDirectContent(); Collections.sort(componentToPrint); @@ -86,7 +94,7 @@ public class PageFitPrintStrategy { int pageY = marginY; Boolean anyAddedToRow; - Graphics2D g2 = cb.createGraphics(pageSize.width, pageSize.height); + Graphics2D g2 = createGraphics((float) wPage, (float) hPage, cb); do { // Fill the row @@ -98,17 +106,19 @@ public class PageFitPrintStrategy { while (entry.hasNext()) { PrintableComponent component = entry.next(); java.awt.Dimension dim = component.getSize(); - if ((rowX + dim.width + marginX < wPage) && (rowY + dim.height + marginY < hPage)) { + if ((rowX + dim.width + marginX <= wPage) && (rowY + dim.height + marginY <= hPage)) { component.setPrintOffset(rowX, rowY); + // Separate each component horizontally by a space equal to the margin rowX += dim.width + marginX; if (rowY + dim.height + marginY > pageY) { pageY = rowY + dim.height + marginY; } - entry.remove(); + entry.remove(); component.print(g2); anyAddedToRow = true; } } + // Separate each component vertically by a space equal to the margin pageY += marginY; } while (anyAddedToRow); @@ -117,6 +127,20 @@ public class PageFitPrintStrategy { } } + /** + * Create a graphics context. + * + * @param theWPage the width of the physical page + * @param theHPage the height of the physical + * + * @param theCb the pdf content byte instance + * + * @return a Graphics2D instance + */ + Graphics2D createGraphics(final float theWPage, final float theHPage, final PdfContentByte theCb) { + return theCb.createGraphics(theWPage, theHPage); + } + /** * Get the dimensions of the paper page. * @@ -126,42 +150,4 @@ public class PageFitPrintStrategy { return new Dimension(document.getPageSize().getWidth(), document.getPageSize().getHeight()); } - - /** - * Convenience class to model a dimension. - */ - class Dimension { - /** Width, in points. */ - public float width; - /** Height, in points. */ - public float height; - - /** - * Constructor. - * @param w width - * @param h height - */ - public Dimension (float w, float h) { - width = w; - height = h; - } - - /** - * Get the width. - * - * @return the width - */ - public float getWidth () { - return width; - } - - /** - * Get the height. - * - * @return the height - */ - public float getHeight () { - return height; - } - } } diff --git a/core/src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java b/core/src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java index 14c1b43e3..1fbf3969d 100644 --- a/core/src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java +++ b/core/src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java @@ -2,52 +2,26 @@ package net.sf.openrocket.gui.print.visitor; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; -import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.PdfWriter; import net.sf.openrocket.gui.print.AbstractPrintable; -import net.sf.openrocket.gui.print.ITextHelper; -import net.sf.openrocket.gui.print.PrintUnit; import net.sf.openrocket.gui.print.PrintableNoseCone; import net.sf.openrocket.gui.print.PrintableTransition; -import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Transition; -import net.sf.openrocket.startup.Application; -import java.awt.image.BufferedImage; import java.util.List; import java.util.Set; /** * A strategy for drawing transition/shroud/nose cone templates. */ -public class TransitionStrategy { +public class TransitionStrategy extends AbstractPrintStrategy { /** - * The logger. + * Print nose cones? */ - private static final LogHelper log = Application.getLogger(); - - /** - * The iText document. - */ - protected Document document; - - /** - * The direct iText writer. - */ - protected PdfWriter writer; - - /** - * The stages selected. - */ - protected Set stages; - - /** - * Strategy for fitting multiple components onto a page. - */ - protected PageFitPrintStrategy pageFitPrint; + private boolean printNoseCones = false; /** * Constructor. @@ -55,65 +29,53 @@ public class TransitionStrategy { * @param doc The iText document * @param theWriter The direct iText writer * @param theStagesToVisit The stages to be visited by this strategy + * @param pageFit the page fit strategy + * @param noseCones nose cones are a special form of a transition; if true, then print nose cones */ - public TransitionStrategy(Document doc, PdfWriter theWriter, Set theStagesToVisit, PageFitPrintStrategy pageFit) { + public TransitionStrategy(Document doc, PdfWriter theWriter, Set theStagesToVisit, PageFitPrintStrategy pageFit, boolean noseCones) { + super(doc, pageFit, theWriter, theStagesToVisit); document = doc; writer = theWriter; stages = theStagesToVisit; pageFitPrint = pageFit; + printNoseCones = noseCones; } - /** - * Recurse through the given rocket component. - * - * @param root the root component; all children will be visited recursively - * @param noseCones nose cones are a special form of a transition; if true, then print nose cones - * - * @return true if a transition/nosecone was rendered - */ - public boolean writeToDocument(final RocketComponent root, boolean noseCones) { - List rc = root.getChildren(); - return goDeep(rc, noseCones); - } - - /** * Recurse through the given rocket component. * * @param theRc an array of rocket components; all children will be visited recursively - * @param noseCones nose cones are a special form of a transition; if true, then print nose cones * * @return true if a transition/nosecone was rendered */ - protected boolean goDeep(final List theRc, boolean noseCones) { + protected Boolean goDeep(final List theRc) { boolean result = false; for (RocketComponent rocketComponent : theRc) { if (rocketComponent instanceof NoseCone) { - if (noseCones) { + if (printNoseCones) { result |= render((Transition) rocketComponent); } } - else if (rocketComponent instanceof Transition && !noseCones) { + else if (rocketComponent instanceof Transition && !printNoseCones) { result |= render((Transition) rocketComponent); } else if (rocketComponent.getChildCount() > 0) { - result |= goDeep(rocketComponent.getChildren(), noseCones); + result |= goDeep(rocketComponent.getChildren()); } } return result; } /** - * The core behavior of this visitor. + * The core behavior of this strategy. * - * @param component the object to extract info about; a graphical image of the transition shape is drawn to the - * document + * @param component the object to extract info about; a graphical image of the transition shape is drawn to the document * * @return true, always */ private boolean render(final Transition component) { try { - AbstractPrintable pfs; + AbstractPrintable pfs; if (component instanceof NoseCone) { pfs = new PrintableNoseCone((NoseCone) component); } @@ -121,19 +83,7 @@ public class TransitionStrategy { pfs = new PrintableTransition(component); } - java.awt.Dimension size = pfs.getSize(); - final Dimension pageSize = getPageSize(); - if (fitsOnOnePage(pageSize, size.getWidth(), size.getHeight())) { - pageFitPrint.addComponent(pfs); - } - else { - int off = (int) (PrintUnit.POINTS_PER_INCH * 0.3f); - pfs.setPrintOffset(off, off); - BufferedImage image = (BufferedImage) pfs.createImage(); - ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()), - document, writer, image); - document.newPage(); - } + render(pfs); } catch (DocumentException e) { log.error("Could not render the transition.", e); @@ -141,75 +91,4 @@ public class TransitionStrategy { return true; } - /** - * Determine if the image will fit on the given page. - * - * @param pageSize the page size - * @param wImage the width of the thing to be printed - * @param hImage the height of the thing to be printed - * - * @return true if the thing to be printed will fit on a single page - */ - private boolean fitsOnOnePage(Dimension pageSize, double wImage, double hImage) { - double wPage = pageSize.getWidth(); - double hPage = pageSize.getHeight(); - - int wRatio = (int) Math.ceil(wImage / wPage); - int hRatio = (int) Math.ceil(hImage / hPage); - - return wRatio <= 1.0d && hRatio <= 1.0d; - } - - /** - * Get the dimensions of the paper page. - * - * @return an internal Dimension - */ - protected Dimension getPageSize() { - return new Dimension(document.getPageSize().getWidth(), - document.getPageSize().getHeight()); - } - - /** - * Convenience class to model a dimension. - */ - class Dimension { - /** - * Width, in points. - */ - public float width; - /** - * Height, in points. - */ - public float height; - - /** - * Constructor. - * - * @param w width - * @param h height - */ - public Dimension(float w, float h) { - width = w; - height = h; - } - - /** - * Get the width. - * - * @return the width - */ - public float getWidth() { - return width; - } - - /** - * Get the height. - * - * @return the height - */ - public float getHeight() { - return height; - } - } } diff --git a/core/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java b/core/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java index 5e37831c4..09ff97aee 100644 --- a/core/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java +++ b/core/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java @@ -112,12 +112,12 @@ public abstract class AbstractScaleFigure extends JPanel implements ScaleFigure @Override - public void addChangeListener(EventListener listener) { + public void addChangeListener(StateChangeListener listener) { listeners.add(0, listener); } @Override - public void removeChangeListener(EventListener listener) { + public void removeChangeListener(StateChangeListener listener) { listeners.remove(listener); } diff --git a/core/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/core/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index bd63d8ba1..b864de545 100644 --- a/core/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/core/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -347,7 +347,7 @@ public class RocketFigure extends AbstractScaleFigure { // Draw motors - String motorID = configuration.getMotorConfigurationID(); + String motorID = configuration.getFlightConfigurationID(); Color fillColor = ((SwingPreferences)Application.getPreferences()).getMotorFillColor(); Color borderColor = ((SwingPreferences)Application.getPreferences()).getMotorBorderColor(); Iterator iterator = configuration.motorIterator(); diff --git a/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 2cef70809..e5ff449b9 100644 --- a/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -6,6 +6,7 @@ import java.awt.Dimension; import java.awt.Font; import java.awt.Point; import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.util.ArrayList; @@ -19,12 +20,14 @@ import java.util.concurrent.ThreadFactory; import javax.swing.AbstractAction; import javax.swing.Action; -import javax.swing.ButtonGroup; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; import javax.swing.JComboBox; +import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; -import javax.swing.JToggleButton; import javax.swing.JViewport; import javax.swing.SwingUtilities; import javax.swing.event.TreeSelectionEvent; @@ -40,11 +43,12 @@ import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.gui.adaptors.DoubleModel; -import net.sf.openrocket.gui.adaptors.MotorConfigurationModel; +import net.sf.openrocket.gui.adaptors.FlightConfigurationModel; import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.StageSelector; import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.configdialog.ComponentConfigDialog; +import net.sf.openrocket.gui.dialogs.flightconfiguration.FlightConfigurationDialog; import net.sf.openrocket.gui.figure3d.RocketFigure3d; import net.sf.openrocket.gui.figureelements.CGCaret; import net.sf.openrocket.gui.figureelements.CPCaret; @@ -57,6 +61,8 @@ import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.masscalc.MassCalculator.MassCalcType; +import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; +import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -83,54 +89,76 @@ import net.sf.openrocket.util.StateChangeListener; */ public class RocketPanel extends JPanel implements TreeSelectionListener, ChangeSource { private static final long serialVersionUID = 1L; - + private static final Translator trans = Application.getTranslator(); - + + public static enum VIEW_TYPE { + Sideview(false, RocketFigure.TYPE_SIDE), + Backview(false, RocketFigure.TYPE_BACK), + Figure3D(true, RocketFigure3d.TYPE_FIGURE), + Unfinished(true, RocketFigure3d.TYPE_UNFINISHED), + Finished(true, RocketFigure3d.TYPE_FINISHED); + + public final boolean is3d; + private final int type; + + private VIEW_TYPE(final boolean is3d, final int type) { + this.is3d = is3d; + this.type = type; + }; + + @Override + public String toString() { + return trans.get("RocketPanel.FigTypeAct." + super.toString()); + } + + } + private boolean is3d; private final RocketFigure figure; private final RocketFigure3d figure3d; - - + + private final ScaleScrollPane scrollPane; - + private final JPanel figureHolder; - + private JLabel infoMessage; - + private TreeSelectionModel selectionModel = null; - + private BasicSlider rotationSlider; - ScaleSelector scaleSelector; - - + private ScaleSelector scaleSelector; + + /* Calculation of CP and CG */ private AerodynamicCalculator aerodynamicCalculator; private MassCalculator massCalculator; - - + + private final OpenRocketDocument document; private final Configuration configuration; - + private Caret extraCP = null; private Caret extraCG = null; private RocketInfo extraText = null; - - + + private double cpAOA = Double.NaN; private double cpTheta = Double.NaN; private double cpMach = Double.NaN; private double cpRoll = Double.NaN; - + // The functional ID of the rocket that was simulated private int flightDataFunctionalID = -1; private String flightDataMotorID = null; - - + + private SimulationWorker backgroundSimulationWorker = null; - + private List listeners = new ArrayList(); - - + + /** * The executor service used for running the background simulations. * This uses a fixed-sized thread pool for all background simulations @@ -140,37 +168,37 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change static { backgroundSimulationExecutor = Executors.newFixedThreadPool(SwingPreferences.getMaxThreadCount(), new ThreadFactory() { - private ThreadFactory factory = Executors.defaultThreadFactory(); - - @Override - public Thread newThread(Runnable r) { - Thread t = factory.newThread(r); - t.setDaemon(true); - t.setPriority(Thread.MIN_PRIORITY); - return t; - } - }); + private ThreadFactory factory = Executors.defaultThreadFactory(); + + @Override + public Thread newThread(Runnable r) { + Thread t = factory.newThread(r); + t.setDaemon(true); + t.setPriority(Thread.MIN_PRIORITY); + return t; + } + }); } - - + + public RocketPanel(OpenRocketDocument document) { - + this.document = document; configuration = document.getDefaultConfiguration(); - + // TODO: FUTURE: calculator selection aerodynamicCalculator = new BarrowmanCalculator(); massCalculator = new BasicMassCalculator(); - + // Create figure and custom scroll pane figure = new RocketFigure(configuration); - figure3d = new RocketFigure3d(configuration); - + figure3d = new RocketFigure3d(document, configuration); + figureHolder = new JPanel(new BorderLayout()); - + scrollPane = new ScaleScrollPane(figure) { private static final long serialVersionUID = 1L; - + @Override public void mouseClicked(MouseEvent event) { handleMouseClick(event); @@ -178,21 +206,34 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change }; scrollPane.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE); scrollPane.setFitting(true); - + createPanel(); - + is3d = true; go2D(); - + configuration.addChangeListener(new StateChangeListener() { @Override public void stateChanged(EventObject e) { - // System.out.println("Configuration changed, calling updateFigure"); updateExtras(); updateFigures(); } }); - + + document.getRocket().addComponentChangeListener(new ComponentChangeListener() { + @Override + public void componentChanged(ComponentChangeEvent e) { + // System.out.println("Configuration changed, calling updateFigure"); + if (is3d) { + if (e instanceof ComponentChangeEvent) { + if (((ComponentChangeEvent) e).isTextureChange()) { + figure3d.flushTextureCaches(); + } + } + } + } + }); + figure3d.addComponentSelectionListener(new RocketFigure3d.ComponentSelectionListener() { @Override public void componentClicked(RocketComponent clicked[], MouseEvent event) { @@ -200,14 +241,14 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change } }); } - + private void updateFigures() { if (!is3d) figure.updateFigure(); else figure3d.updateFigure(); } - + private void go3D() { if (is3d) return; @@ -216,13 +257,13 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change figureHolder.add(figure3d, BorderLayout.CENTER); rotationSlider.setEnabled(false); scaleSelector.setEnabled(false); - + revalidate(); figureHolder.revalidate(); - + figure3d.repaint(); } - + private void go2D() { if (!is3d) return; @@ -235,126 +276,112 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change figureHolder.revalidate(); figure.repaint(); } - + /** * Creates the layout and components of the panel. */ private void createPanel() { setLayout(new MigLayout("", "[shrink][grow]", "[shrink][shrink][grow][shrink]")); - + setPreferredSize(new Dimension(800, 300)); - - - //// Create toolbar - - ButtonGroup bg = new ButtonGroup(); - - // Side/back buttons - FigureTypeAction action = new FigureTypeAction(RocketFigure.TYPE_SIDE); - //// Side view - action.putValue(Action.NAME, trans.get("RocketPanel.FigTypeAct.Sideview")); - //// Side view - action.putValue(Action.SHORT_DESCRIPTION, trans.get("RocketPanel.FigTypeAct.ttip.Sideview")); - JToggleButton toggle = new JToggleButton(action); - bg.add(toggle); - add(toggle, "spanx, split"); - - action = new FigureTypeAction(RocketFigure.TYPE_BACK); - //// Back view - action.putValue(Action.NAME, trans.get("RocketPanel.FigTypeAct.Backview")); - //// Back view - action.putValue(Action.SHORT_DESCRIPTION, trans.get("RocketPanel.FigTypeAct.ttip.Backview")); - toggle = new JToggleButton(action); - bg.add(toggle); - add(toggle, "gap rel"); - - //// 3d Toggle - final JToggleButton toggle3d = new JToggleButton(new AbstractAction("3D") { - private static final long serialVersionUID = 1L; - { - putValue(Action.NAME, "3D");//TODO - putValue(Action.SHORT_DESCRIPTION, "3D"); //TODO - } + + + // View Type Dropdown + ComboBoxModel cm = new DefaultComboBoxModel(VIEW_TYPE.values()) { @Override - public void actionPerformed(ActionEvent e) { - if ( ((JToggleButton)e.getSource()).isSelected() ){ + public void setSelectedItem(Object o) { + super.setSelectedItem(o); + VIEW_TYPE v = (VIEW_TYPE) o; + if (v.is3d) { + figure3d.setType(v.type); go3D(); } else { + figure.setType(v.type); go2D(); } } - }); - bg.add(toggle3d); - toggle3d.setEnabled(RocketFigure3d.is3dEnabled()); - add(toggle3d, "gap rel"); - + }; + add(new JLabel("View Type:"), "spanx, split"); + add(new JComboBox(cm)); + + // Zoom level selector scaleSelector = new ScaleSelector(scrollPane); add(scaleSelector); - - - + + + // Stage selector StageSelector stageSelector = new StageSelector(configuration); - add(stageSelector, ""); - - - - // Motor configuration selector - //// Motor configuration: - JLabel label = new JLabel(trans.get("RocketPanel.lbl.Motorcfg")); + add(stageSelector); + + + + // Flight configuration selector + //// Flight configuration: + JLabel label = new JLabel(trans.get("RocketPanel.lbl.Flightcfg")); label.setHorizontalAlignment(JLabel.RIGHT); add(label, "growx, right"); - add(new JComboBox(new MotorConfigurationModel(configuration)), "wrap"); - - - - - + add(new JComboBox(new FlightConfigurationModel(configuration)), ""); + + //// Edit button + JButton button = new JButton(trans.get("RocketPanel.but.FlightcfgEdit")); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JDialog configDialog = new FlightConfigurationDialog(document.getRocket(), SwingUtilities.windowForComponent(RocketPanel.this)); + configDialog.show(); + } + }); + add(button, "wrap"); + + + + // Create slider and scroll pane - + DoubleModel theta = new DoubleModel(figure, "Rotation", UnitGroup.UNITS_ANGLE, 0, 2 * Math.PI); UnitSelector us = new UnitSelector(theta, true); us.setHorizontalAlignment(JLabel.CENTER); add(us, "alignx 50%, growx"); - + // Add the rocket figure add(figureHolder, "grow, spany 2, wmin 300lp, hmin 100lp, wrap"); - - + + // Add rotation slider // Minimum size to fit "360deg" JLabel l = new JLabel("360" + Chars.DEGREE); Dimension d = l.getPreferredSize(); - + add(rotationSlider = new BasicSlider(theta.getSliderModel(0, 2 * Math.PI), JSlider.VERTICAL, true), "ax 50%, wrap, width " + (d.width + 6) + "px:null:null, growy"); - - + + //// Click to select    Shift+click to select other    Double-click to edit    Click+drag to move infoMessage = new JLabel(trans.get("RocketPanel.lbl.infoMessage")); infoMessage.setFont(new Font("Sans Serif", Font.PLAIN, 9)); add(infoMessage, "skip, span, gapleft 25, wrap"); - - + + addExtras(); } - - - + + + public RocketFigure getFigure() { return figure; } - + public AerodynamicCalculator getAerodynamicCalculator() { return aerodynamicCalculator; } - + public Configuration getConfiguration() { return configuration; } - + /** * Get the center of pressure figure element. * @@ -363,7 +390,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change public Caret getExtraCP() { return extraCP; } - + /** * Get the center of gravity figure element. * @@ -372,7 +399,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change public Caret getExtraCG() { return extraCG; } - + /** * Get the extra text figure element. * @@ -381,7 +408,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change public RocketInfo getExtraText() { return extraText; } - + public void setSelectionModel(TreeSelectionModel m) { if (selectionModel != null) { selectionModel.removeTreeSelectionListener(this); @@ -390,9 +417,9 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change selectionModel.addTreeSelectionListener(this); valueChanged((TreeSelectionEvent) null); // updates FigureParameters } - - - + + + /** * Return the angle of attack used in CP calculation. NaN signifies the default value * of zero. @@ -401,7 +428,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change public double getCPAOA() { return cpAOA; } - + /** * Set the angle of attack to be used in CP calculation. A value of NaN signifies that * the default AOA (zero) should be used. @@ -416,11 +443,11 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change updateFigures(); fireChangeEvent(); } - + public double getCPTheta() { return cpTheta; } - + public void setCPTheta(double theta) { if (MathUtil.equals(theta, cpTheta) || (Double.isNaN(theta) && Double.isNaN(cpTheta))) @@ -432,11 +459,11 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change updateFigures(); fireChangeEvent(); } - + public double getCPMach() { return cpMach; } - + public void setCPMach(double mach) { if (MathUtil.equals(mach, cpMach) || (Double.isNaN(mach) && Double.isNaN(cpMach))) @@ -446,11 +473,11 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change updateFigures(); fireChangeEvent(); } - + public double getCPRoll() { return cpRoll; } - + public void setCPRoll(double roll) { if (MathUtil.equals(roll, cpRoll) || (Double.isNaN(roll) && Double.isNaN(cpRoll))) @@ -460,31 +487,31 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change updateFigures(); fireChangeEvent(); } - - - + + + @Override - public void addChangeListener(EventListener listener) { + public void addChangeListener(StateChangeListener listener) { listeners.add(0, listener); } - + @Override - public void removeChangeListener(EventListener listener) { + public void removeChangeListener(StateChangeListener listener) { listeners.remove(listener); } - + protected void fireChangeEvent() { EventObject e = new EventObject(this); for (EventListener l : listeners) { - if ( l instanceof StateChangeListener ) { - ((StateChangeListener)l).stateChanged(e); + if (l instanceof StateChangeListener) { + ((StateChangeListener) l).stateChanged(e); } } } - - - - + + + + /** * Handle clicking on figure shapes. The functioning is the following: * @@ -495,7 +522,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change * the next component. Otherwise select the first component in the list. */ public static final int CYCLE_SELECTION_MODIFIER = InputEvent.SHIFT_DOWN_MASK; - + private void handleMouseClick(MouseEvent event) { if (event.getButton() != MouseEvent.BUTTON1) return; @@ -503,18 +530,20 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change Point p1 = scrollPane.getViewport().getViewPosition(); int x = p0.x + p1.x; int y = p0.y + p1.y; - + RocketComponent[] clicked = figure.getComponentsByPoint(x, y); - + handleComponentClick(clicked, event); } - - private void handleComponentClick(RocketComponent[] clicked, MouseEvent event){ - + + private void handleComponentClick(RocketComponent[] clicked, MouseEvent event) { + // If no component is clicked, do nothing - if (clicked.length == 0) + if (clicked.length == 0) { + selectionModel.setSelectionPath(null); return; - + } + // Check whether the currently selected component is in the clicked components. TreePath path = selectionModel.getSelectionPath(); if (path != null) { @@ -531,7 +560,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change } } } - + // Currently selected component not clicked if (path == null) { if (event.isShiftDown() && event.getClickCount() == 1 && clicked.length > 1) { @@ -540,34 +569,34 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change path = ComponentTreeModel.makeTreePath(clicked[0]); } } - + // Set selection and check for double-click selectionModel.setSelectionPath(path); if (event.getClickCount() == 2) { RocketComponent component = (RocketComponent) path.getLastPathComponent(); - + ComponentConfigDialog.showDialog(SwingUtilities.getWindowAncestor(this), document, component); } } - - - - + + + + /** * Updates the extra data included in the figure. Currently this includes * the CP and CG carets. */ private WarningSet warnings = new WarningSet(); - + private void updateExtras() { Coordinate cp, cg; double cpx, cgx; - + // TODO: MEDIUM: User-definable conditions FlightConditions conditions = new FlightConditions(configuration); warnings.clear(); - + if (!Double.isNaN(cpMach)) { conditions.setMach(cpMach); extraText.setMach(cpMach); @@ -575,20 +604,20 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change conditions.setMach(Application.getPreferences().getDefaultMach()); extraText.setMach(Application.getPreferences().getDefaultMach()); } - + if (!Double.isNaN(cpAOA)) { conditions.setAOA(cpAOA); } else { conditions.setAOA(0); } extraText.setAOA(cpAOA); - + if (!Double.isNaN(cpRoll)) { conditions.setRollRate(cpRoll); } else { conditions.setRollRate(0); } - + if (!Double.isNaN(cpTheta)) { conditions.setTheta(cpTheta); cp = aerodynamicCalculator.getCP(configuration, conditions, warnings); @@ -596,24 +625,24 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change cp = aerodynamicCalculator.getWorstCP(configuration, conditions, warnings); } extraText.setTheta(cpTheta); - - + + cg = massCalculator.getCG(configuration, MassCalcType.LAUNCH_MASS); // System.out.println("CG computed as "+cg+ " CP as "+cp); - + if (cp.weight > 0.000001) cpx = cp.x; else cpx = Double.NaN; - + if (cg.weight > 0.000001) cgx = cg.x; else cgx = Double.NaN; - + figure3d.setCG(cg); figure3d.setCP(cp); - + // Length bound is assumed to be tight double length = 0, diameter = 0; Collection bounds = configuration.getBounds(); @@ -627,7 +656,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change } length = maxX - minX; } - + for (RocketComponent c : configuration) { if (c instanceof SymmetricComponent) { double d1 = ((SymmetricComponent) c).getForeRadius() * 2; @@ -635,31 +664,31 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change diameter = MathUtil.max(diameter, d1, d2); } } - + extraText.setCG(cgx); extraText.setCP(cpx); extraText.setLength(length); extraText.setDiameter(diameter); extraText.setMass(cg.weight); extraText.setWarnings(warnings); - - + + if (figure.getType() == RocketFigure.TYPE_SIDE && length > 0) { - + // TODO: LOW: Y-coordinate and rotation extraCP.setPosition(cpx * RocketFigure.EXTRA_SCALE, 0); extraCG.setPosition(cgx * RocketFigure.EXTRA_SCALE, 0); - + } else { - + extraCP.setPosition(Double.NaN, Double.NaN); extraCG.setPosition(Double.NaN, Double.NaN); - + } - - + + //////// Flight simulation in background - + // Check whether to compute or not if (!((SwingPreferences) Application.getPreferences()).computeFlightInBackground()) { extraText.setFlightData(null); @@ -667,38 +696,38 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change stopBackgroundSimulation(); return; } - + // Check whether data is already up to date if (flightDataFunctionalID == configuration.getRocket().getFunctionalModID() && - flightDataMotorID == configuration.getMotorConfigurationID()) { + flightDataMotorID == configuration.getFlightConfigurationID()) { return; } - + flightDataFunctionalID = configuration.getRocket().getFunctionalModID(); - flightDataMotorID = configuration.getMotorConfigurationID(); - + flightDataMotorID = configuration.getFlightConfigurationID(); + // Stop previous computation (if any) stopBackgroundSimulation(); - + // Check that configuration has motors if (!configuration.hasMotors()) { extraText.setFlightData(FlightData.NaN_DATA); extraText.setCalculatingData(false); return; } - + // Start calculation process extraText.setCalculatingData(true); - + Rocket duplicate = (Rocket) configuration.getRocket().copy(); - Simulation simulation = ((SwingPreferences)Application.getPreferences()).getBackgroundSimulation(duplicate); + Simulation simulation = ((SwingPreferences) Application.getPreferences()).getBackgroundSimulation(duplicate); simulation.getOptions().setMotorConfigurationID( - configuration.getMotorConfigurationID()); - + configuration.getFlightConfigurationID()); + backgroundSimulationWorker = new BackgroundSimulationWorker(document, simulation); backgroundSimulationExecutor.execute(backgroundSimulationWorker); } - + /** * Cancels the current background simulation worker, if any. */ @@ -708,26 +737,26 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change backgroundSimulationWorker = null; } } - - + + /** * A SimulationWorker that simulates the rocket flight in the background and * sets the results to the extra text when finished. The worker can be cancelled * if necessary. */ private class BackgroundSimulationWorker extends SimulationWorker { - + private final CustomExpressionSimulationListener exprListener; - + public BackgroundSimulationWorker(OpenRocketDocument doc, Simulation sim) { super(sim); List exprs = doc.getCustomExpressions(); exprListener = new CustomExpressionSimulationListener(exprs); } - + @Override protected FlightData doInBackground() { - + // Pause a little while to allow faster UI reaction try { Thread.sleep(300); @@ -735,38 +764,38 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change } if (isCancelled() || backgroundSimulationWorker != this) return null; - + return super.doInBackground(); } - + @Override protected void simulationDone() { // Do nothing if cancelled if (isCancelled() || backgroundSimulationWorker != this) return; - + backgroundSimulationWorker = null; extraText.setFlightData(simulation.getSimulatedData()); extraText.setCalculatingData(false); figure.repaint(); figure3d.repaint(); } - + @Override protected SimulationListener[] getExtraListeners() { return new SimulationListener[] { InterruptListener.INSTANCE, ApogeeEndListener.INSTANCE, - exprListener}; - + exprListener }; + } - + @Override protected void simulationInterrupted(Throwable t) { // Do nothing on cancel, set N/A data otherwise if (isCancelled() || backgroundSimulationWorker != this) // Double-check return; - + backgroundSimulationWorker = null; extraText.setFlightData(FlightData.NaN_DATA); extraText.setCalculatingData(false); @@ -774,9 +803,9 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change figure3d.repaint(); } } - - - + + + /** * Adds the extra data to the figure. Currently this includes the CP and CG carets. */ @@ -785,21 +814,21 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change extraCP = new CPCaret(0, 0); extraText = new RocketInfo(configuration); updateExtras(); - + figure.clearRelativeExtra(); figure.addRelativeExtra(extraCP); figure.addRelativeExtra(extraCG); figure.addAbsoluteExtra(extraText); - - + + figure3d.clearRelativeExtra(); //figure3d.addRelativeExtra(extraCP); //figure3d.addRelativeExtra(extraCG); figure3d.addAbsoluteExtra(extraText); - + } - - + + /** * Updates the selection in the FigureParameters and repaints the figure. * Ignores the event itself. @@ -809,19 +838,20 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change TreePath[] paths = selectionModel.getSelectionPaths(); if (paths == null) { figure.setSelection(null); + figure3d.setSelection(null); return; } - + RocketComponent[] components = new RocketComponent[paths.length]; for (int i = 0; i < paths.length; i++) components[i] = (RocketComponent) paths[i].getLastPathComponent(); figure.setSelection(components); - + figure3d.setSelection(components); } - - - + + + /** * An Action that shows whether the figure type is the type * given in the constructor. @@ -831,13 +861,13 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change private class FigureTypeAction extends AbstractAction implements StateChangeListener { private static final long serialVersionUID = 1L; private final int type; - + public FigureTypeAction(int type) { this.type = type; stateChanged(null); figure.addChangeListener(this); } - + @Override public void actionPerformed(ActionEvent e) { boolean state = (Boolean) getValue(Action.SELECTED_KEY); @@ -849,11 +879,11 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change } stateChanged(null); } - + @Override public void stateChanged(EventObject e) { putValue(Action.SELECTED_KEY, figure.getType() == type && !is3d); } } - + } diff --git a/core/src/net/sf/openrocket/gui/util/EditDecalHelper.java b/core/src/net/sf/openrocket/gui/util/EditDecalHelper.java new file mode 100644 index 000000000..f67722ad1 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/util/EditDecalHelper.java @@ -0,0 +1,157 @@ +package net.sf.openrocket.gui.util; + +import java.awt.Desktop; +import java.awt.Window; +import java.io.File; +import java.io.IOException; +import java.text.MessageFormat; + +import net.sf.openrocket.appearance.AppearanceBuilder; +import net.sf.openrocket.appearance.DecalImage; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.gui.dialogs.EditDecalDialog; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; + +public class EditDecalHelper { + + public static class EditDecalHelperException extends Exception { + + private String extraMessage = ""; + + public EditDecalHelperException(String message, Throwable cause) { + super(message, cause); + } + + public EditDecalHelperException(String message, String extraMessage, Throwable cause) { + super(message, cause); + this.extraMessage = extraMessage; + } + + @Override + public String getMessage() { + if (extraMessage == null || extraMessage.isEmpty()) { + return super.getMessage(); + } + return super.getMessage() + "\n" + getExtraMessage(); + } + + public String getExtraMessage() { + return extraMessage; + } + + } + + private static final Translator trans = Application.getTranslator(); + + private static final SwingPreferences prefs = ((SwingPreferences) Application.getPreferences()); + + public static void editDecal(Window parent, OpenRocketDocument doc, RocketComponent component, DecalImage decal) throws EditDecalHelperException { + + boolean sysPrefSet = prefs.isDecalEditorPreferenceSet(); + int usageCount = doc.countDecalUsage(decal); + + //First Check preferences + if (sysPrefSet && usageCount == 1) { + + launchEditor(prefs.isDecalEditorPreferenceSystem(), prefs.getDecalEditorCommandLine(), decal); + return; + } + + EditDecalDialog dialog = new EditDecalDialog(parent, !sysPrefSet, usageCount); + dialog.setVisible(true); + + if (dialog.isCancel()) { + return; + } + + // Do we use the System Preference Editor or from the dialog? + boolean useSystemEditor = false; + String commandLine = ""; + + if (sysPrefSet) { + useSystemEditor = prefs.isDecalEditorPreferenceSystem(); + commandLine = prefs.getDecalEditorCommandLine(); + } else { + useSystemEditor = dialog.isUseSystemEditor(); + commandLine = dialog.getCommandLine(); + // Do we need to save the preferences? + if (dialog.isSavePreferences()) { + prefs.setDecalEditorPreference(useSystemEditor, commandLine); + } + } + + if (dialog.isEditOne()) { + decal = makeDecalUnique(doc, component, decal); + } + + launchEditor(useSystemEditor, commandLine, decal); + + } + + private static DecalImage makeDecalUnique(OpenRocketDocument doc, RocketComponent component, DecalImage decal) { + + DecalImage newImage = doc.makeUniqueDecal(decal); + + AppearanceBuilder appearanceBuilder = new AppearanceBuilder(component.getAppearance()); + appearanceBuilder.setImage(newImage); + + component.setAppearance(appearanceBuilder.getAppearance()); + + return newImage; + } + + private static void launchEditor(boolean useSystemEditor, String commandTemplate, DecalImage decal) throws EditDecalHelperException { + + String decalId = decal.getName(); + // Create Temp File. + int dotlocation = decalId.lastIndexOf('.'); + String extension = "tmp"; + if (dotlocation > 0 && dotlocation < decalId.length()) { + extension = decalId.substring(dotlocation); + } + File tmpFile = null; + + try { + tmpFile = File.createTempFile("OR_graphics", extension); + } catch (IOException ioex) { + String message = MessageFormat.format(trans.get("EditDecalHelper.createFileException"), tmpFile.getAbsoluteFile()); + throw new EditDecalHelperException(message, ioex); + } + + try { + decal.exportImage(tmpFile, true); + } catch (IOException ioex) { + String message = MessageFormat.format(trans.get("EditDecalHelper.createFileException"), tmpFile.getAbsoluteFile()); + throw new EditDecalHelperException(message, ioex); + } + + + if (useSystemEditor) { + try { + Desktop.getDesktop().edit(tmpFile); + } catch (IOException ioex) { + throw new EditDecalHelperException(trans.get("EditDecalHelper.launchSystemEditorException"), trans.get("EditDecalHelper.editPreferencesHelp"), ioex); + } + } else { + + String filename = tmpFile.getAbsolutePath(); + + String command; + if (commandTemplate.contains("%%")) { + command = commandTemplate.replace("%%", filename); + } else { + command = commandTemplate + " " + filename; + } + + try { + Runtime.getRuntime().exec(command); + } catch (IOException ioex) { + String message = MessageFormat.format(trans.get("EditDecalHelper.launchCustomEditorException"), command); + throw new EditDecalHelperException(message, trans.get("EditDecalHelper.editPreferencesHelp"), ioex); + } + + } + } +} diff --git a/core/src/net/sf/openrocket/gui/util/GUIUtil.java b/core/src/net/sf/openrocket/gui/util/GUIUtil.java index 4ac68dceb..cfbd81025 100644 --- a/core/src/net/sf/openrocket/gui/util/GUIUtil.java +++ b/core/src/net/sf/openrocket/gui/util/GUIUtil.java @@ -397,6 +397,19 @@ public class GUIUtil { } + /** + * Changes the style of the font of the specified label. + * + * @param label the component for which to change the font + * @param style the change in the font style + */ + public static void changeFontStyle(JLabel label, int style) { + Font font = label.getFont(); + font = font.deriveFont(style); + label.setFont(font); + } + + /** * Traverses recursively the component tree, and sets all applicable component diff --git a/core/src/net/sf/openrocket/gui/util/Icons.java b/core/src/net/sf/openrocket/gui/util/Icons.java index 0fa2b67c5..930a141d4 100644 --- a/core/src/net/sf/openrocket/gui/util/Icons.java +++ b/core/src/net/sf/openrocket/gui/util/Icons.java @@ -69,6 +69,7 @@ public class Icons { public static final Icon ZOOM_IN = loadImageIcon("pix/icons/zoom-in.png", "Zoom in"); public static final Icon ZOOM_OUT = loadImageIcon("pix/icons/zoom-out.png", "Zoom out"); + public static final Icon ZOOM_RESET = loadImageIcon("pix/icons/zoom-reset.png", "Reset Zoom & Pan"); public static final Icon PREFERENCES = loadImageIcon("pix/icons/preferences.png", "Preferences"); diff --git a/core/src/net/sf/openrocket/gui/util/OpenFileWorker.java b/core/src/net/sf/openrocket/gui/util/OpenFileWorker.java index da193c165..e67973516 100644 --- a/core/src/net/sf/openrocket/gui/util/OpenFileWorker.java +++ b/core/src/net/sf/openrocket/gui/util/OpenFileWorker.java @@ -7,12 +7,12 @@ import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; +import java.net.URL; import javax.swing.SwingWorker; import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.file.DatabaseMotorFinder; -import net.sf.openrocket.file.RocketLoader; +import net.sf.openrocket.file.GeneralRocketLoader; import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.MathUtil; @@ -27,23 +27,23 @@ public class OpenFileWorker extends SwingWorker { private static final LogHelper log = Application.getLogger(); private final File file; - private final InputStream stream; - private final RocketLoader loader; + private final URL jarURL; + private final GeneralRocketLoader loader; - public OpenFileWorker(File file, RocketLoader loader) { + public OpenFileWorker(File file) { this.file = file; - this.stream = null; - this.loader = loader; + this.jarURL = null; + loader = new GeneralRocketLoader(file); } - public OpenFileWorker(InputStream stream, RocketLoader loader) { - this.stream = stream; + public OpenFileWorker(URL fileURL) { + this.jarURL = fileURL; this.file = null; - this.loader = loader; + loader = new GeneralRocketLoader(fileURL); } - public RocketLoader getRocketLoader() { + public GeneralRocketLoader getRocketLoader() { return loader; } @@ -55,7 +55,7 @@ public class OpenFileWorker extends SwingWorker { if (file != null) { is = new FileInputStream(file); } else { - is = stream; + is = jarURL.openStream(); } // Buffer stream unless already buffered @@ -67,7 +67,13 @@ public class OpenFileWorker extends SwingWorker { is = new ProgressInputStream(is); try { - return loader.load(is, new DatabaseMotorFinder()); + OpenRocketDocument document = loader.load(is); + + // Set document state + document.setFile(file); + document.setSaved(true); + + return document; } finally { try { is.close(); diff --git a/core/src/net/sf/openrocket/gui/util/SaveFileWorker.java b/core/src/net/sf/openrocket/gui/util/SaveFileWorker.java index 3688c4ce5..fab4f7e70 100644 --- a/core/src/net/sf/openrocket/gui/util/SaveFileWorker.java +++ b/core/src/net/sf/openrocket/gui/util/SaveFileWorker.java @@ -1,22 +1,20 @@ package net.sf.openrocket.gui.util; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; import javax.swing.SwingWorker; import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.file.RocketSaver; -import net.sf.openrocket.startup.Application; +import net.sf.openrocket.file.GeneralRocketSaver; +import net.sf.openrocket.file.GeneralRocketSaver.SavingProgress; public class SaveFileWorker extends SwingWorker { - + private final OpenRocketDocument document; private final File file; - private final RocketSaver saver; + private final GeneralRocketSaver saver; - public SaveFileWorker(OpenRocketDocument document, File file, RocketSaver saver) { + public SaveFileWorker(OpenRocketDocument document, File file, GeneralRocketSaver saver) { this.document = document; this.file = file; this.saver = saver; @@ -26,31 +24,17 @@ public class SaveFileWorker extends SwingWorker { @Override protected Void doInBackground() throws Exception { - int estimate = (int)saver.estimateFileSize(document, - document.getDefaultStorageOptions()); - // Create the ProgressOutputStream that provides progress estimates - @SuppressWarnings("resource") - ProgressOutputStream os = new ProgressOutputStream( - new BufferedOutputStream(new FileOutputStream(file)), - estimate, this) { + SavingProgress progressCallback = new GeneralRocketSaver.SavingProgress() { @Override - protected void setProgress(int progress) { + public void setProgress(int progress) { SaveFileWorker.this.setProgress(progress); + } - }; - try { - saver.save(os, document); - } finally { - try { - os.close(); - } catch (Exception e) { - Application.getExceptionHandler().handleErrorCondition("Error closing file", e); - } - } + saver.save(file, document, progressCallback); return null; } diff --git a/core/src/net/sf/openrocket/gui/util/SwingPreferences.java b/core/src/net/sf/openrocket/gui/util/SwingPreferences.java index 125b13616..c1af84686 100644 --- a/core/src/net/sf/openrocket/gui/util/SwingPreferences.java +++ b/core/src/net/sf/openrocket/gui/util/SwingPreferences.java @@ -38,7 +38,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences { private static final List SUPPORTED_LOCALES; static { List list = new ArrayList(); - for (String lang : new String[] { "en", "de", "es", "fr", "it", "ru", "cs", "pl" }) { + for (String lang : new String[] { "en", "de", "es", "fr", "it", "ru", "cs", "pl", "ja", "pt" }) { list.add(new Locale(lang)); } SUPPORTED_LOCALES = Collections.unmodifiableList(list); @@ -225,17 +225,17 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences { } public File getDefaultUserComponentDirectory() { - + File compdir = new File(SystemInfo.getUserApplicationDirectory(), "Components"); - + if (!compdir.isDirectory()) { compdir.mkdirs(); } - if( !compdir.isDirectory() ) { + if (!compdir.isDirectory()) { return null; } - if( !compdir.canRead() ) { + if (!compdir.canRead()) { return null; } return compdir; @@ -573,7 +573,9 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences { return materials; } - + + //// Preset Component Favorites + @Override public void setComponentFavorite(ComponentPreset preset, ComponentPreset.Type type, boolean favorite) { Preferences prefs = PREFNODE.node("favoritePresets").node(type.name()); @@ -595,6 +597,35 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences { } return collection; } - //// Helper methods + + //// Decal Editor Setting + private final static String DECAL_EDITOR_PREFERNCE_NODE = "decalEditorPreference"; + private final static String DECAL_EDITOR_USE_SYSTEM_DEFAULT = ""; + + public void clearDecalEditorPreference( ) { + putString(DECAL_EDITOR_PREFERNCE_NODE,null); + } + public void setDecalEditorPreference(boolean useSystem, String commandLine) { + if ( useSystem ) { + putString(DECAL_EDITOR_PREFERNCE_NODE,DECAL_EDITOR_USE_SYSTEM_DEFAULT); + } else if ( commandLine != null ) { + putString(DECAL_EDITOR_PREFERNCE_NODE, commandLine); + } else { + clearDecalEditorPreference(); + } + } + + public boolean isDecalEditorPreferenceSet() { + String s = getString(DECAL_EDITOR_PREFERNCE_NODE,null); + return s != null; + } + + public boolean isDecalEditorPreferenceSystem() { + String s = getString(DECAL_EDITOR_PREFERNCE_NODE,null); + return DECAL_EDITOR_USE_SYSTEM_DEFAULT.equals(s); + } + public String getDecalEditorCommandLine() { + return getString(DECAL_EDITOR_PREFERNCE_NODE,null); + } } diff --git a/core/src/net/sf/openrocket/gui/watcher/FileWatcher.java b/core/src/net/sf/openrocket/gui/watcher/FileWatcher.java new file mode 100644 index 000000000..555df2b8b --- /dev/null +++ b/core/src/net/sf/openrocket/gui/watcher/FileWatcher.java @@ -0,0 +1,37 @@ +package net.sf.openrocket.gui.watcher; + +import java.io.File; + +public abstract class FileWatcher implements Watchable { + + private final File file; + private long lastModifiedTimestamp = 0L; + + public FileWatcher(File file) { + this.file = file; + } + + protected File getFile() { + return file; + } + + @Override + public WatchEvent monitor() { + + long modified = file.lastModified(); + if (modified == 0L) { + // check for removal? + return null; + } + if (modified > lastModifiedTimestamp) { + long oldTimestamp = lastModifiedTimestamp; + lastModifiedTimestamp = modified; + return (oldTimestamp == 0L) ? null : WatchEvent.MODIFIED; + } + return null; + } + + @Override + public abstract void handleEvent(WatchEvent evt); + +} diff --git a/core/src/net/sf/openrocket/gui/watcher/WatchEvent.java b/core/src/net/sf/openrocket/gui/watcher/WatchEvent.java new file mode 100644 index 000000000..dea1f45c0 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/watcher/WatchEvent.java @@ -0,0 +1,8 @@ +package net.sf.openrocket.gui.watcher; + +public enum WatchEvent { + + MODIFIED, + REMOVED; + +} diff --git a/core/src/net/sf/openrocket/gui/watcher/WatchKey.java b/core/src/net/sf/openrocket/gui/watcher/WatchKey.java new file mode 100644 index 000000000..d00b291e9 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/watcher/WatchKey.java @@ -0,0 +1,7 @@ +package net.sf.openrocket.gui.watcher; + +public interface WatchKey { + + public void cancel(); + +} diff --git a/core/src/net/sf/openrocket/gui/watcher/WatchService.java b/core/src/net/sf/openrocket/gui/watcher/WatchService.java new file mode 100644 index 000000000..df279d826 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/watcher/WatchService.java @@ -0,0 +1,7 @@ +package net.sf.openrocket.gui.watcher; + +public interface WatchService { + + public abstract WatchKey register(Watchable w); + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/gui/watcher/WatchServiceImpl.java b/core/src/net/sf/openrocket/gui/watcher/WatchServiceImpl.java new file mode 100644 index 000000000..03dd9bb36 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/watcher/WatchServiceImpl.java @@ -0,0 +1,72 @@ +package net.sf.openrocket.gui.watcher; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +public class WatchServiceImpl implements WatchService { + + private final static int INTERVAL_MS = 1000; + + private static AtomicInteger threadcount = new AtomicInteger(0); + + private ScheduledExecutorService executor = Executors.newScheduledThreadPool(2, new ThreadFactory() { + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("WatchService-" + threadcount.getAndIncrement()); + return t; + } + }); + + public WatchServiceImpl() { + } + + /* (non-Javadoc) + * @see net.sf.openrocket.gui.watcher.WatchService#register(net.sf.openrocket.gui.watcher.Watchable) + */ + @Override + public WatchKey register(Watchable w) { + ScheduledFuture future = executor.scheduleWithFixedDelay(new WatchableRunner(w), 0, INTERVAL_MS, TimeUnit.MILLISECONDS); + + return new WatchKeyImpl(future); + } + + public class WatchKeyImpl implements WatchKey { + + ScheduledFuture future; + + private WatchKeyImpl(ScheduledFuture future) { + this.future = future; + } + + @Override + public void cancel() { + future.cancel(true); + } + + } + + private class WatchableRunner implements Runnable { + + private Watchable w; + + private WatchableRunner(Watchable w) { + this.w = w; + } + + @Override + public void run() { + + WatchEvent evt = w.monitor(); + if (evt != null) { + w.handleEvent(evt); + } + } + } + +} diff --git a/core/src/net/sf/openrocket/gui/watcher/Watchable.java b/core/src/net/sf/openrocket/gui/watcher/Watchable.java new file mode 100644 index 000000000..9ac590910 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/watcher/Watchable.java @@ -0,0 +1,9 @@ +package net.sf.openrocket.gui.watcher; + +public interface Watchable { + + public WatchEvent monitor(); + + public void handleEvent(WatchEvent evt); + +} diff --git a/core/src/net/sf/openrocket/masscalc/BasicMassCalculator.java b/core/src/net/sf/openrocket/masscalc/BasicMassCalculator.java index fda87e0bb..90c7b3e3f 100644 --- a/core/src/net/sf/openrocket/masscalc/BasicMassCalculator.java +++ b/core/src/net/sf/openrocket/masscalc/BasicMassCalculator.java @@ -54,7 +54,7 @@ public class BasicMassCalculator extends AbstractMassCalculator { totalCG = Coordinate.NUL; // Add motor CGs - String motorId = configuration.getMotorConfigurationID(); + String motorId = configuration.getFlightConfigurationID(); if (type != MassCalcType.NO_MOTORS && motorId != null) { Iterator iterator = configuration.motorIterator(); while (iterator.hasNext()) { diff --git a/core/src/net/sf/openrocket/motor/DesignationComparator.java b/core/src/net/sf/openrocket/motor/DesignationComparator.java index 21489cb79..2c0e8f1f4 100644 --- a/core/src/net/sf/openrocket/motor/DesignationComparator.java +++ b/core/src/net/sf/openrocket/motor/DesignationComparator.java @@ -19,9 +19,25 @@ public class DesignationComparator implements Comparator { COLLATOR = Collator.getInstance(Locale.US); COLLATOR.setStrength(Collator.PRIMARY); } - - private Pattern pattern = - Pattern.compile("^([0-9][0-9]+|1/([1-8]))?([a-zA-Z])([0-9]+)(.*?)$"); + + /* + * Regexp to parse the multitude of designations. Supported types: + * + * 1/4A4... + * 1/2A4... + * A4... + * 132G100... + * 132-G100... + * + * Capture groups: + * 1 = garbage (stuff before impulse class) + * 2 = divisor number for 1/2A, 1/4A etc, otherwise null + * 3 = impulse class letter + * 4 = average thrust + * 5 = stuff after thrust number + */ + private Pattern pattern = + Pattern.compile("^([0-9]+-?|1/([1-8]))?([a-zA-Z])([0-9,]+)(.*?)$"); @Override public int compare(String o1, String o2) { @@ -32,13 +48,13 @@ public class DesignationComparator implements Comparator { m2 = pattern.matcher(o2); if (m1.find() && m2.find()) { - + String o1Class = m1.group(3); - int o1Thrust = Integer.parseInt(m1.group(4)); + int o1Thrust = Integer.parseInt(m1.group(4).replaceAll(",", "")); String o1Extra = m1.group(5); String o2Class = m2.group(3); - int o2Thrust = Integer.parseInt(m2.group(4)); + int o2Thrust = Integer.parseInt(m2.group(4).replaceAll(",", "")); String o2Extra = m2.group(5); // 1. Motor class @@ -46,18 +62,18 @@ public class DesignationComparator implements Comparator { // 1/2A and 1/4A comparison String sub1 = m1.group(2); String sub2 = m2.group(2); - + if (sub1 != null || sub2 != null) { if (sub1 == null) sub1 = "1"; if (sub2 == null) sub2 = "1"; - value = -COLLATOR.compare(sub1,sub2); + value = -COLLATOR.compare(sub1, sub2); if (value != 0) return value; } } - value = COLLATOR.compare(o1Class,o2Class); + value = COLLATOR.compare(o1Class, o2Class); if (value != 0) return value; diff --git a/core/src/net/sf/openrocket/motor/MotorInstanceConfiguration.java b/core/src/net/sf/openrocket/motor/MotorInstanceConfiguration.java index a3142028d..8c23ece43 100644 --- a/core/src/net/sf/openrocket/motor/MotorInstanceConfiguration.java +++ b/core/src/net/sf/openrocket/motor/MotorInstanceConfiguration.java @@ -5,6 +5,8 @@ import java.util.Collections; import java.util.List; import net.sf.openrocket.models.atmosphere.AtmosphericConditions; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration.IgnitionEvent; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Monitorable; @@ -20,11 +22,14 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable private final List ids = new ArrayList(); private final List unmodifiableIds = Collections.unmodifiableList(ids); private final List motors = new ArrayList(); + private final List ejectionDelays = new ArrayList(); private final List mounts = new ArrayList(); + private final List ignitionEvents = new ArrayList(); + private final List ignitionDelays = new ArrayList(); private final List positions = new ArrayList(); private final List ignitionTimes = new ArrayList(); - + private int modID = 0; @@ -32,20 +37,26 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable * Add a motor instance to this configuration. The motor is placed at * the specified position and with an infinite ignition time (never ignited). * - * @param id the ID of this motor instance. - * @param motor the motor instance. - * @param mount the motor mount containing this motor - * @param position the position of the motor in absolute coordinates. + * @param id the ID of this motor instance. + * @param motor the motor instance. + * @param mount the motor mount containing this motor + * @param ignitionEvent the ignition event for the motor + * @param ignitionDelay the ignition delay for the motor + * @param position the position of the motor in absolute coordinates. * @throws IllegalArgumentException if a motor with the specified ID already exists. */ - public void addMotor(MotorId id, MotorInstance motor, MotorMount mount, Coordinate position) { + public void addMotor(MotorId id, MotorInstance motor, double ejectionDelay, MotorMount mount, + IgnitionEvent ignitionEvent, double ignitionDelay, Coordinate position) { if (this.ids.contains(id)) { throw new IllegalArgumentException("MotorInstanceConfiguration already " + "contains a motor with id " + id); } this.ids.add(id); this.motors.add(motor); + this.ejectionDelays.add(ejectionDelay); this.mounts.add(mount); + this.ignitionEvents.add(ignitionEvent); + this.ignitionDelays.add(ignitionDelay); this.positions.add(position); this.ignitionTimes.add(Double.POSITIVE_INFINITY); modID++; @@ -62,6 +73,10 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable return motors.get(indexOf(id)); } + public double getEjectionDelay(MotorId id) { + return ejectionDelays.get(indexOf(id)); + } + public MotorMount getMotorMount(MotorId id) { return mounts.get(indexOf(id)); } @@ -84,8 +99,15 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable modID++; } + public double getMotorIgnitionDelay(MotorId id) { + return ignitionDelays.get(indexOf(id)); + } + + public IgnitionEvent getMotorIgnitionEvent(MotorId id) { + return ignitionEvents.get(indexOf(id)); + } + - private int indexOf(MotorId id) { int index = ids.indexOf(id); if (index < 0) { @@ -96,7 +118,7 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable } - + /** * Step all of the motor instances to the specified time minus their ignition time. * @param time the "global" time @@ -130,7 +152,10 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable clone.ids.addAll(this.ids); clone.mounts.addAll(this.mounts); clone.positions.addAll(this.positions); + clone.ejectionDelays.addAll(this.ejectionDelays); clone.ignitionTimes.addAll(this.ignitionTimes); + clone.ignitionEvents.addAll(this.ignitionEvents); + clone.ignitionDelays.addAll(this.ignitionDelays); for (MotorInstance motor : this.motors) { clone.motors.add(motor.clone()); } diff --git a/core/src/net/sf/openrocket/optimization/rocketoptimization/SimulationModifier.java b/core/src/net/sf/openrocket/optimization/rocketoptimization/SimulationModifier.java index 1989ea074..b85751ae3 100644 --- a/core/src/net/sf/openrocket/optimization/rocketoptimization/SimulationModifier.java +++ b/core/src/net/sf/openrocket/optimization/rocketoptimization/SimulationModifier.java @@ -89,7 +89,7 @@ public interface SimulationModifier extends ChangeSource { public double getCurrentScaledValue(Simulation simulation) throws OptimizationException; - + /** * Modify the specified simulation to the corresponding parameter value. * @@ -99,6 +99,13 @@ public interface SimulationModifier extends ChangeSource { */ public void modify(Simulation simulation, double scaledValue) throws OptimizationException; + /** + * Called once at the start of the optimization. + * This method can be used to ensure the simulation or rocket are properly configured. + * + * @param simulation + */ + public void initialize(Simulation simulation) throws OptimizationException; /** * Compare whether this SimulationModifier is equivalent to another simulation modifier. diff --git a/core/src/net/sf/openrocket/optimization/rocketoptimization/modifiers/AbstractSimulationModifier.java b/core/src/net/sf/openrocket/optimization/rocketoptimization/modifiers/AbstractSimulationModifier.java index 1d8c0fd18..32f237365 100644 --- a/core/src/net/sf/openrocket/optimization/rocketoptimization/modifiers/AbstractSimulationModifier.java +++ b/core/src/net/sf/openrocket/optimization/rocketoptimization/modifiers/AbstractSimulationModifier.java @@ -76,8 +76,12 @@ public abstract class AbstractSimulationModifier implements SimulationModifier { return toScaledValue(value); } + @Override + public void initialize(Simulation simulation) throws OptimizationException { + // Default is no-op. + } + - /** * Returns the scaled value (normally within [0...1]). If the min...max range is singular, * this method returns 0.0, 1.0 or 0.5 depending on whether the value is less than, @@ -110,7 +114,7 @@ public abstract class AbstractSimulationModifier implements SimulationModifier { } - + @Override public double getMinValue() { return minValue; @@ -148,12 +152,12 @@ public abstract class AbstractSimulationModifier implements SimulationModifier { @Override - public void addChangeListener(EventListener listener) { + public void addChangeListener(StateChangeListener listener) { listeners.add(listener); } @Override - public void removeChangeListener(EventListener listener) { + public void removeChangeListener(StateChangeListener listener) { listeners.remove(listener); } @@ -166,8 +170,8 @@ public abstract class AbstractSimulationModifier implements SimulationModifier { // Copy the list before iterating to prevent concurrent modification exceptions. EventListener[] list = listeners.toArray(new EventListener[0]); for (EventListener l : list) { - if ( l instanceof StateChangeListener ) { - ((StateChangeListener)l).stateChanged(event); + if (l instanceof StateChangeListener) { + ((StateChangeListener) l).stateChanged(event); } } } @@ -195,7 +199,7 @@ public abstract class AbstractSimulationModifier implements SimulationModifier { } - + @Override public int hashCode() { final int prime = 31; @@ -207,5 +211,5 @@ public abstract class AbstractSimulationModifier implements SimulationModifier { return result; } - + } diff --git a/core/src/net/sf/openrocket/optimization/rocketoptimization/modifiers/FlightConfigurationModifier.java b/core/src/net/sf/openrocket/optimization/rocketoptimization/modifiers/FlightConfigurationModifier.java new file mode 100644 index 000000000..d25ed55f0 --- /dev/null +++ b/core/src/net/sf/openrocket/optimization/rocketoptimization/modifiers/FlightConfigurationModifier.java @@ -0,0 +1,77 @@ +package net.sf.openrocket.optimization.rocketoptimization.modifiers; + +import java.util.Locale; + +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.optimization.general.OptimizationException; +import net.sf.openrocket.rocketcomponent.FlightConfigurableParameter; +import net.sf.openrocket.rocketcomponent.FlightConfiguration; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.BugException; +import net.sf.openrocket.util.Reflection.Method; + +public class FlightConfigurationModifier> extends GenericModifier { + + private final Class componentClass; + private final String componentId; + private final Method configGetter; + + + /** + * Sole constructor. + * + * @param modifierName the name of this modifier (returned by {@link #getName()}) + * @param modifierDescription the description of this modifier (returned by {@link #getDescription()}) + * @param relatedObject the related object (returned by {@link #getRelatedObject()}) + * @param unitGroup the unit group (returned by {@link #getUnitGroup()}) + * @param multiplier the multiplier by which the value returned by the getter is multiplied + * to obtain the desired value + * @param componentClass the RocketComponent class type that is being modified + * @param componentId the ID of the component to modify + * @param configName the name of the configuration object (base name of the getter) + * @param flightConfigClass the class of the FlightConfigurableParameter + * @param methodName the base name of the getter/setter methods (without "get"/"set") + */ + public FlightConfigurationModifier( + String modifierName, + String modifierDescription, + Object relatedObject, + UnitGroup unitGroup, + double multiplier, + Class componentClass, + String componentId, + String configName, + Class flightConfigClass, + String methodName) { + super(modifierName, modifierDescription, relatedObject, unitGroup, multiplier, flightConfigClass, methodName); + + this.componentClass = componentClass; + this.componentId = componentId; + + try { + configName = configName.substring(0, 1).toUpperCase(Locale.ENGLISH) + configName.substring(1); + configGetter = new Method(componentClass.getMethod("get" + configName)); + } catch (SecurityException e) { + throw new BugException("Trying to find method get/set" + configName + " in class " + componentClass, e); + } catch (NoSuchMethodException e) { + throw new BugException("Trying to find method get/set" + configName + " in class " + componentClass, e); + } + + } + + + @Override + protected E getModifiedObject(Simulation simulation) throws OptimizationException { + + RocketComponent c = simulation.getRocket().findComponent(componentId); + if (c == null) { + throw new OptimizationException("Could not find component of type " + componentClass.getSimpleName() + + " with correct ID"); + } + + FlightConfiguration configs = (FlightConfiguration) configGetter.invoke(c); + return configs.get(simulation.getConfiguration().getFlightConfigurationID()); + } + +} diff --git a/core/src/net/sf/openrocket/optimization/services/DefaultSimulationModifierService.java b/core/src/net/sf/openrocket/optimization/services/DefaultSimulationModifierService.java index cf77e3c57..d2c8f5524 100644 --- a/core/src/net/sf/openrocket/optimization/services/DefaultSimulationModifierService.java +++ b/core/src/net/sf/openrocket/optimization/services/DefaultSimulationModifierService.java @@ -12,11 +12,15 @@ import net.sf.openrocket.document.Simulation; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.optimization.general.OptimizationException; import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier; +import net.sf.openrocket.optimization.rocketoptimization.modifiers.FlightConfigurationModifier; import net.sf.openrocket.optimization.rocketoptimization.modifiers.GenericComponentModifier; import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; import net.sf.openrocket.rocketcomponent.EllipticalFinSet; import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.FreeformFinSet; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; import net.sf.openrocket.rocketcomponent.InternalComponent; import net.sf.openrocket.rocketcomponent.LaunchLug; import net.sf.openrocket.rocketcomponent.MassComponent; @@ -24,7 +28,6 @@ import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.Parachute; import net.sf.openrocket.rocketcomponent.RecoveryDevice; -import net.sf.openrocket.rocketcomponent.RecoveryDevice.DeployEvent; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Streamer; @@ -127,7 +130,7 @@ public class DefaultSimulationModifierService implements SimulationModifierServi // Simulation is used to calculate default min/max values Simulation simulation = new Simulation(rocket); - simulation.getConfiguration().setMotorConfigurationID(null); + simulation.getConfiguration().setFlightConfigurationID(null); for (RocketComponent c : rocket) { @@ -188,15 +191,20 @@ public class DefaultSimulationModifierService implements SimulationModifierServi setDefaultMinMax(mod, simulation); modifiers.add(mod); - mod = new GenericComponentModifier( + mod = new FlightConfigurationModifier( trans.get("optimization.modifier.motormount.delay"), trans.get("optimization.modifier.motormount.delay.desc"), c, UnitGroup.UNITS_SHORT_TIME, - 1.0, c.getClass(), c.getID(), "IgnitionDelay"); + 1.0, + c.getClass(), + c.getID(), + "IgnitionConfiguration", + IgnitionConfiguration.class, + "IgnitionDelay"); + mod.setMinValue(0); mod.setMaxValue(5); modifiers.add(mod); - } } @@ -247,24 +255,43 @@ public class DefaultSimulationModifierService implements SimulationModifierServi if (c instanceof RecoveryDevice) { RecoveryDevice device = (RecoveryDevice) c; - SimulationModifier mod = new GenericComponentModifier( + SimulationModifier mod = new FlightConfigurationModifier( trans.get("optimization.modifier.recoverydevice.deployDelay"), trans.get("optimization.modifier.recoverydevice.deployDelay.desc"), - c, UnitGroup.UNITS_SHORT_TIME, - 1.0, c.getClass(), c.getID(), "DeployDelay"); + c, + UnitGroup.UNITS_SHORT_TIME, + 1.0, + c.getClass(), + c.getID(), + "DeploymentConfiguration", + DeploymentConfiguration.class, + "DeployDelay"); + mod.setMinValue(0); mod.setMaxValue(10); modifiers.add(mod); - if (device.getDeployEvent() == DeployEvent.ALTITUDE) { - mod = new GenericComponentModifier( - trans.get("optimization.modifier.recoverydevice.deployAltitude"), - trans.get("optimization.modifier.recoverydevice.deployAltitude.desc"), - c, UnitGroup.UNITS_DISTANCE, - 1.0, c.getClass(), c.getID(), "DeployAltitude"); - setDefaultMinMax(mod, simulation); - modifiers.add(mod); - } + mod = new FlightConfigurationModifier( + trans.get("optimization.modifier.recoverydevice.deployAltitude"), + trans.get("optimization.modifier.recoverydevice.deployAltitude.desc"), + c, + UnitGroup.UNITS_DISTANCE, + 1.0, + c.getClass(), + c.getID(), + "DeploymentConfiguration", + DeploymentConfiguration.class, + "DeployAltitude") { + + @Override + public void initialize(Simulation simulation) throws OptimizationException { + DeploymentConfiguration config = getModifiedObject(simulation); + config.setDeployEvent(DeployEvent.APOGEE); + } + + }; + setDefaultMinMax(mod, simulation); + modifiers.add(mod); } @@ -288,7 +315,6 @@ public class DefaultSimulationModifierService implements SimulationModifierServi return modifiers; } - private void setDefaultMinMax(SimulationModifier mod, Simulation simulation) { try { double current = mod.getCurrentSIValue(simulation); diff --git a/core/src/net/sf/openrocket/plugin/AnnotationFinder.java b/core/src/net/sf/openrocket/plugin/AnnotationFinder.java new file mode 100644 index 000000000..6e689e321 --- /dev/null +++ b/core/src/net/sf/openrocket/plugin/AnnotationFinder.java @@ -0,0 +1,16 @@ +package net.sf.openrocket.plugin; + +import java.util.List; + +/** + * Interface for finding annotated classes from the class path. + */ +public interface AnnotationFinder { + + /** + * Return a list of all types (classes and interfaces) that are annotated + * with the provided annotation. + */ + public List> findAnnotatedTypes(Class annotation); + +} diff --git a/core/src/net/sf/openrocket/plugin/AnnotationFinderImpl.java b/core/src/net/sf/openrocket/plugin/AnnotationFinderImpl.java new file mode 100644 index 000000000..b4514e057 --- /dev/null +++ b/core/src/net/sf/openrocket/plugin/AnnotationFinderImpl.java @@ -0,0 +1,91 @@ +package net.sf.openrocket.plugin; + +import java.io.File; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import net.sf.openrocket.util.BugException; +import net.sf.openrocket.util.JarUtil; +import eu.infomas.annotation.AnnotationDetector; +import eu.infomas.annotation.AnnotationDetector.TypeReporter; + +/** + * An AnnotationFinder that uses annotation-detector library to scan + * the class path. Compatible with the JIJ loader. + */ +public class AnnotationFinderImpl implements AnnotationFinder { + + @Override + public List> findAnnotatedTypes(Class annotation) { + final List> classes = new ArrayList>(); + + TypeReporter reporter = new ListReporter(classes); + final AnnotationDetector cf = new AnnotationDetector(reporter); + try { + ClassLoader loader = this.getClass().getClassLoader(); + if (loader instanceof URLClassLoader) { + + /* + * In case of URLClassLoader (which may be our own instantiation) + * use the URLs from there, as java.class.path may not be up-to-date. + */ + + URLClassLoader urlClassLoader = (URLClassLoader) loader; + URL[] urls = urlClassLoader.getURLs(); + + List files = new ArrayList(); + for (URL url : urls) { + if (url.getProtocol().equals("file")) { + files.add(JarUtil.urlToFile(url)); + } + } + + cf.detect(files.toArray(new File[0])); + } else { + + /* + * If not using a URLClassLoader, just do the default. + */ + cf.detect(); + } + + } catch (IOException e) { + throw new BugException("Unable to search class path", e); + } + + return classes; + } + + + private static class ListReporter implements TypeReporter { + private final List> classes; + private final Set names = new HashSet(); + + public ListReporter(List> classes) { + this.classes = classes; + } + + @SuppressWarnings("unchecked") + @Override + public Class[] annotations() { + return new Class[] { Plugin.class }; + } + + @Override + public void reportTypeAnnotation(Class annotation, String className) { + if (names.add(className)) { + try { + classes.add(this.getClass().getClassLoader().loadClass(className)); + } catch (ClassNotFoundException e) { + // Ignore + } + } + } + } +} diff --git a/core/src/net/sf/openrocket/plugin/JIJ.java b/core/src/net/sf/openrocket/plugin/JIJ.java deleted file mode 100644 index 6d9fec052..000000000 --- a/core/src/net/sf/openrocket/plugin/JIJ.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.sf.openrocket.plugin; - -import java.io.File; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Arrays; - -public class JIJ { - - public static void main(String[] args) throws Exception { - String cp = System.getProperty("java.class.path"); - String[] cps = cp.split(File.pathSeparator); - - URL[] urls = new URL[cps.length + 1]; - for (int i = 0; i < cps.length; i++) { - urls[i] = new File(cps[i]).toURI().toURL(); - } - urls[cps.length] = new File("/home/sampo/Projects/OpenRocket/core/example.jar").toURI().toURL(); - - System.out.println("Classpath: " + Arrays.toString(urls)); - - URLClassLoader loader = new URLClassLoader(urls, null); - Class c = loader.loadClass("net.sf.openrocket.plugin.Test"); - Method m = c.getMethod("main", args.getClass()); - m.invoke(null, (Object) args); - } - -} diff --git a/core/src/net/sf/openrocket/plugin/Plugin.java b/core/src/net/sf/openrocket/plugin/Plugin.java index de8eb4cdd..2dc2aee27 100644 --- a/core/src/net/sf/openrocket/plugin/Plugin.java +++ b/core/src/net/sf/openrocket/plugin/Plugin.java @@ -6,8 +6,10 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Annotation that defines an interface to be a plugin interface. - * Plugin interfaces are automatically discovered from plugin JARs and + * Annotation that defines an interface to be a plugin interface and + * classes as plugin implementations. + *

+ * Plugin interfaces are automatically discovered from the classpath and * registered as plugins in Guice. * * @author Sampo Niskanen diff --git a/core/src/net/sf/openrocket/plugin/PluginModule.java b/core/src/net/sf/openrocket/plugin/PluginModule.java index 5aaa24404..c882f5cba 100644 --- a/core/src/net/sf/openrocket/plugin/PluginModule.java +++ b/core/src/net/sf/openrocket/plugin/PluginModule.java @@ -1,14 +1,9 @@ package net.sf.openrocket.plugin; -import java.io.File; -import java.io.IOException; import java.util.ArrayList; -import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import com.google.inject.AbstractModule; import com.google.inject.multibindings.Multibinder; @@ -21,63 +16,46 @@ import com.google.inject.multibindings.Multibinder; */ public class PluginModule extends AbstractModule { - private final List jars; - private final ClassLoader classLoader; private Map, Multibinder> binders = new HashMap, Multibinder>(); - - - /** - * Sole constructor. - * - * @param jars the JAR files to search for plugins - * @param classLoader the class loader used to load classes from the JAR files - */ - public PluginModule(List jars, ClassLoader classLoader) { - this.jars = jars; - this.classLoader = classLoader; - } - - - @Override - protected void configure() { - for (File jar : jars) { - List classNames = readClassNames(jar); - for (String className : classNames) { - checkForPlugin(className); - } - } - } - + private AnnotationFinder finder = new AnnotationFinderImpl(); @SuppressWarnings("unchecked") - private void checkForPlugin(String className) { - try { + @Override + protected void configure() { + + List> classes = finder.findAnnotatedTypes(Plugin.class); + List> interfaces = new ArrayList>(); + List> unusedInterfaces; + + // Find plugin interfaces + for (Class c : classes) { + if (c.isInterface()) { + interfaces.add(c); + } + } + unusedInterfaces = new ArrayList>(interfaces); + + // Find plugin implementations + for (Class c : classes) { + if (c.isInterface()) + continue; - Class c = classLoader.loadClass(className); for (Class intf : c.getInterfaces()) { - System.out.println("Testing class " + c + " interface " + intf); - if (isPluginInterface(intf)) { - System.out.println("BINDING"); + if (interfaces.contains(intf)) { // Ugly hack to enable dynamic binding... Can this be done type-safely? Multibinder binder = (Multibinder) findBinder(intf); binder.addBinding().to(c); + unusedInterfaces.remove(intf); } } - - } catch (ClassNotFoundException e) { - System.err.println("Could not load class " + className + ": " + e); } + + // TODO: Unused plugin interfaces should be bound to an empty set - how? } - - private boolean isPluginInterface(Class intf) { - return intf.isAnnotationPresent(Plugin.class); - } - - private Multibinder findBinder(Class intf) { Multibinder binder = binders.get(intf); if (binder == null) { @@ -87,36 +65,4 @@ public class PluginModule extends AbstractModule { return binder; } - - private List readClassNames(File jar) { - List classNames = new ArrayList(); - - JarFile file = null; - try { - file = new JarFile(jar); - Enumeration entries = file.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - String name = entry.getName(); - if (name.toLowerCase().endsWith(".class") && !name.contains("$")) { - name = name.substring(0, name.length() - 6); - name = name.replace('/', '.'); - classNames.add(name); - } - } - } catch (IOException e) { - System.err.println("Error reading JAR file " + jar); - } finally { - if (file != null) { - try { - file.close(); - } catch (IOException e) { - // Curse all checked exceptions... - } - } - } - - return classNames; - } - } diff --git a/core/src/net/sf/openrocket/plugin/Test.java b/core/src/net/sf/openrocket/plugin/Test.java deleted file mode 100644 index da2c0386c..000000000 --- a/core/src/net/sf/openrocket/plugin/Test.java +++ /dev/null @@ -1,47 +0,0 @@ -package net.sf.openrocket.plugin; - -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Arrays; -import java.util.List; -import java.util.Set; - -import com.google.inject.Guice; -import com.google.inject.Inject; -import com.google.inject.Injector; - -public class Test { - - @Inject - private Set impls; - - - public void run() { - System.out.println("Plugin count: " + impls.size()); - for (ExamplePlugin i : impls) { - i.doit(); - } - } - - - public static void main(String[] args) throws MalformedURLException { - // Properties p = System.getProperties(); - // Enumeration e = p.keys(); - // while (e.hasMoreElements()) { - // Object key = e.nextElement(); - // Object value = p.get(key); - // System.out.println(key + " = " + value); - // } - - List jars = Arrays.asList(new File("/home/sampo/Projects/OpenRocket/core/example.jar")); - URL[] urls = { new File("/home/sampo/Projects/OpenRocket/core/example.jar").toURI().toURL() }; - ClassLoader classLoader = new URLClassLoader(urls); - - classLoader = Test.class.getClassLoader(); - - Injector injector = Guice.createInjector(new PluginModule(jars, classLoader)); - injector.getInstance(Test.class).run(); - } -} diff --git a/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java b/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java index 6b33ff3a1..ccfc387f8 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java +++ b/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java @@ -2,7 +2,6 @@ package net.sf.openrocket.rocketcomponent; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.motor.Motor; @@ -27,19 +26,20 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial // When changing the inner radius, thickness is modified private boolean motorMount = false; - private HashMap ejectionDelays = new HashMap(); - private HashMap motors = new HashMap(); - private IgnitionEvent ignitionEvent = IgnitionEvent.AUTOMATIC; - private double ignitionDelay = 0; private double overhang = 0; + private FlightConfigurationImpl motorConfigurations; + private FlightConfigurationImpl ignitionConfigurations; + - public BodyTube() { super(); this.length = 8 * DEFAULT_RADIUS; this.outerRadius = DEFAULT_RADIUS; this.autoRadius = true; + + this.motorConfigurations = new MotorFlightConfigurationImpl(this, ComponentChangeEvent.MOTOR_CHANGE, MotorConfiguration.NO_MOTORS); + this.ignitionConfigurations = new FlightConfigurationImpl(this, ComponentChangeEvent.EVENT_CHANGE, new IgnitionConfiguration()); } public BodyTube(double length, double radius) { @@ -67,7 +67,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial public ComponentPreset.Type getPresetType() { return ComponentPreset.Type.BODY_TUBE; } - + /** * Return the outer radius of the body tube. * @@ -142,17 +142,17 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial @Override protected void loadFromPreset(ComponentPreset preset) { this.autoRadius = false; - if ( preset.has(ComponentPreset.OUTER_DIAMETER) ) { + if (preset.has(ComponentPreset.OUTER_DIAMETER)) { double outerDiameter = preset.get(ComponentPreset.OUTER_DIAMETER); - this.outerRadius = outerDiameter/2.0; - if ( preset.has(ComponentPreset.INNER_DIAMETER) ) { + this.outerRadius = outerDiameter / 2.0; + if (preset.has(ComponentPreset.INNER_DIAMETER)) { double innerDiameter = preset.get(ComponentPreset.INNER_DIAMETER); - this.thickness = (outerDiameter-innerDiameter) / 2.0; + this.thickness = (outerDiameter - innerDiameter) / 2.0; } } - + super.loadFromPreset(preset); - + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } @@ -177,7 +177,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial } - + @Override protected double getFrontAutoRadius() { if (isOuterRadiusAutomatic()) { @@ -207,9 +207,9 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial } - - - + + + @Override public double getInnerRadius() { if (filled) @@ -223,8 +223,8 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial } - - + + /** * Return the component name. */ @@ -292,8 +292,8 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial } - - + + /** * Helper function for cylinder volume. */ @@ -318,7 +318,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial } - + /** * Check whether the given type can be added to this component. BodyTubes allow any * InternalComponents or ExternalComponents, excluding BodyComponents, to be added. @@ -338,11 +338,33 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial //////////////// Motor mount ///////////////// + + @Override + public FlightConfiguration getMotorConfiguration() { + return motorConfigurations; + } + + + @Override + public FlightConfiguration getIgnitionConfiguration() { + return ignitionConfigurations; + } + + + + @Override + public void cloneFlightConfiguration(String oldConfigId, String newConfigId) { + motorConfigurations.cloneFlightConfiguration(oldConfigId, newConfigId); + ignitionConfigurations.cloneFlightConfiguration(oldConfigId, newConfigId); + } + + @Override public boolean isMotorMount() { return motorMount; } + @Override public void setMotorMount(boolean mount) { if (motorMount == mount) @@ -351,50 +373,24 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); } + + + @SuppressWarnings("deprecation") + @Deprecated @Override public Motor getMotor(String id) { - if (id == null) - return null; - - // Check whether the id is valid for the current rocket - RocketComponent root = this.getRoot(); - if (!(root instanceof Rocket)) - return null; - if (!((Rocket) root).isMotorConfigurationID(id)) - return null; - - return motors.get(id); + return this.motorConfigurations.get(id).getMotor(); } - @Override - public void setMotor(String id, Motor motor) { - if (id == null) { - if (motor != null) { - throw new IllegalArgumentException("Cannot set non-null motor for id null"); - } - } - Motor current = motors.get(id); - if ((motor == null && current == null) || - (motor != null && motor.equals(current))) - return; - motors.put(id, motor); - fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); - } + @SuppressWarnings("deprecation") + @Deprecated @Override public double getMotorDelay(String id) { - Double delay = ejectionDelays.get(id); - if (delay == null) - return Motor.PLUGGED; - return delay; - } - - @Override - public void setMotorDelay(String id, double delay) { - ejectionDelays.put(id, delay); - fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); + return this.motorConfigurations.get(id).getEjectionDelay(); } + @SuppressWarnings("deprecation") @Deprecated @Override public int getMotorCount() { @@ -406,33 +402,6 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial return getInnerRadius() * 2; } - @Override - public IgnitionEvent getIgnitionEvent() { - return ignitionEvent; - } - - @Override - public void setIgnitionEvent(IgnitionEvent event) { - if (ignitionEvent == event) - return; - ignitionEvent = event; - fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); - } - - - @Override - public double getIgnitionDelay() { - return ignitionDelay; - } - - @Override - public void setIgnitionDelay(double delay) { - if (MathUtil.equals(delay, ignitionDelay)) - return; - ignitionDelay = delay; - fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); - } - @Override public double getMotorOverhang() { @@ -450,7 +419,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial @Override public Coordinate getMotorPosition(String id) { - Motor motor = motors.get(id); + Motor motor = getMotor(id); if (motor == null) { throw new IllegalArgumentException("No motor with id " + id + " defined."); } @@ -459,20 +428,12 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial } - - - /* - * (non-Javadoc) - * Copy the motor and ejection delay HashMaps. - * - * @see rocketcomponent.RocketComponent#copy() - */ - @SuppressWarnings("unchecked") + @Override protected RocketComponent copyWithOriginalID() { - RocketComponent c = super.copyWithOriginalID(); - ((BodyTube) c).motors = (HashMap) motors.clone(); - ((BodyTube) c).ejectionDelays = (HashMap) ejectionDelays.clone(); - return c; + BodyTube copy = (BodyTube) super.copyWithOriginalID(); + copy.motorConfigurations = new FlightConfigurationImpl(motorConfigurations, copy, ComponentChangeEvent.MOTOR_CHANGE); + copy.ignitionConfigurations = new FlightConfigurationImpl(ignitionConfigurations, copy, ComponentChangeEvent.EVENT_CHANGE); + return copy; } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/ComponentChangeEvent.java b/core/src/net/sf/openrocket/rocketcomponent/ComponentChangeEvent.java index 5a2a2e24f..ff5d6c8b1 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/ComponentChangeEvent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/ComponentChangeEvent.java @@ -5,7 +5,7 @@ import java.util.EventObject; public class ComponentChangeEvent extends EventObject { private static final long serialVersionUID = 1L; - + /** A change that does not affect simulation results in any way (name, color, etc.) */ public static final int NONFUNCTIONAL_CHANGE = 1; /** A change that affects the mass properties of the rocket */ @@ -23,6 +23,8 @@ public class ComponentChangeEvent extends EventObject { public static final int MOTOR_CHANGE = 32; /** A change that affects the events occurring during flight. */ public static final int EVENT_CHANGE = 64; + /** A change to the 3D texture assigned to a component*/ + public static final int TEXTURE_CHANGE = 128; /** A bit-field that contains all possible change types. */ public static final int ALL_CHANGE = 0xFFFFFFFF; @@ -47,6 +49,9 @@ public class ComponentChangeEvent extends EventObject { return (RocketComponent) super.getSource(); } + public boolean isTextureChange() { + return (type & TEXTURE_CHANGE) != 0; + } public boolean isAerodynamicChange() { return (type & AERODYNAMIC_CHANGE) != 0; diff --git a/core/src/net/sf/openrocket/rocketcomponent/Configuration.java b/core/src/net/sf/openrocket/rocketcomponent/Configuration.java index 4a76df715..4cad9533a 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Configuration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Configuration.java @@ -31,11 +31,11 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi private Rocket rocket; private BitSet stages = new BitSet(); - private String motorConfiguration = null; + private String flightConfigurationId = null; private List listenerList = new ArrayList(); - + /* Cached data */ private int boundsModID = -1; private ArrayList cachedBounds = new ArrayList(); @@ -44,7 +44,7 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi private int refLengthModID = -1; private double cachedRefLength = -1; - + private int modID = 0; @@ -61,7 +61,7 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi } - + public Rocket getRocket() { return rocket; } @@ -87,6 +87,11 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi fireChangeEvent(); } + public void setOnlyStage(int stage) { + stages.clear(); + stages.set(stage, stage + 1, true); + fireChangeEvent(); + } /** * Check whether the up-most stage of the rocket is in this configuration. @@ -98,7 +103,7 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi } - + /** * Check whether the stage specified by the index is active. */ @@ -163,27 +168,21 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi } - public String getMotorConfigurationID() { - return motorConfiguration; + public String getFlightConfigurationID() { + return flightConfigurationId; } - public void setMotorConfigurationID(String id) { - if ((motorConfiguration == null && id == null) || - (id != null && id.equals(motorConfiguration))) + public void setFlightConfigurationID(String id) { + if ((flightConfigurationId == null && id == null) || + (id != null && id.equals(flightConfigurationId))) return; - motorConfiguration = id; + flightConfigurationId = id; fireChangeEvent(); } - public String getMotorConfigurationDescription() { - return rocket.getMotorConfigurationNameOrDescription(motorConfiguration); - } - - - /** * Removes the listener connection to the rocket and listeners of this object. * This configuration may not be used after a call to this method! @@ -198,12 +197,12 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi //////////////// Listeners //////////////// @Override - public void addChangeListener(EventListener listener) { + public void addChangeListener(StateChangeListener listener) { listenerList.add(listener); } @Override - public void removeChangeListener(EventListener listener) { + public void removeChangeListener(StateChangeListener listener) { listenerList.remove(listener); } @@ -213,12 +212,12 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi this.modID++; boundsModID = -1; refLengthModID = -1; - + // Copy the list before iterating to prevent concurrent modification exceptions. EventListener[] listeners = listenerList.toArray(new EventListener[0]); for (EventListener l : listeners) { - if ( l instanceof StateChangeListener ) { - ((StateChangeListener)l).stateChanged(e); + if (l instanceof StateChangeListener) { + ((StateChangeListener) l).stateChanged(e); } } } @@ -243,7 +242,7 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi MotorMount mount = (MotorMount) c; if (!mount.isMotorMount()) continue; - if (mount.getMotor(this.motorConfiguration) != null) { + if (mount.getMotor(this.flightConfigurationId) != null) { return true; } } @@ -308,8 +307,8 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi } - - + + /** * Return an iterator that iterates over the currently active components. * The Rocket and Stage components are not returned, @@ -458,7 +457,7 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi RocketComponent c = iterator.next(); if (c instanceof MotorMount) { MotorMount mount = (MotorMount) c; - if (mount.isMotorMount() && mount.getMotor(motorConfiguration) != null) { + if (mount.isMotorMount() && mount.getMotor(flightConfigurationId) != null) { next = mount; return; } diff --git a/core/src/net/sf/openrocket/rocketcomponent/DeploymentConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/DeploymentConfiguration.java new file mode 100644 index 000000000..5e1d6d99d --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/DeploymentConfiguration.java @@ -0,0 +1,184 @@ +package net.sf.openrocket.rocketcomponent; + +import java.util.EventObject; +import java.util.List; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.ArrayList; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.Pair; +import net.sf.openrocket.util.StateChangeListener; + +public class DeploymentConfiguration implements FlightConfigurableParameter { + + + public static enum DeployEvent { + LAUNCH(trans.get("RecoveryDevice.DeployEvent.LAUNCH")) { + @Override + public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) { + return e.getType() == FlightEvent.Type.LAUNCH; + } + }, + EJECTION(trans.get("RecoveryDevice.DeployEvent.EJECTION")) { + @Override + public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) { + if (e.getType() != FlightEvent.Type.EJECTION_CHARGE) + return false; + RocketComponent charge = e.getSource(); + return charge.getStageNumber() == source.getStageNumber(); + } + }, + APOGEE(trans.get("RecoveryDevice.DeployEvent.APOGEE")) { + @Override + public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) { + return e.getType() == FlightEvent.Type.APOGEE; + } + }, + ALTITUDE(trans.get("RecoveryDevice.DeployEvent.ALTITUDE")) { + @SuppressWarnings("unchecked") + @Override + public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) { + if (e.getType() != FlightEvent.Type.ALTITUDE) + return false; + + double alt = config.deployAltitude; + Pair altitude = (Pair) e.getData(); + + return (altitude.getU() >= alt) && (altitude.getV() <= alt); + } + }, + LOWER_STAGE_SEPARATION(trans.get("RecoveryDevice.DeployEvent.LOWER_STAGE_SEPARATION")) { + @Override + public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) { + if (e.getType() != FlightEvent.Type.STAGE_SEPARATION) + return false; + + int separation = e.getSource().getStageNumber(); + int current = source.getStageNumber(); + return (current + 1 == separation); + } + }, + NEVER(trans.get("RecoveryDevice.DeployEvent.NEVER")) { + @Override + public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) { + return false; + } + }; + + private final String description; + + DeployEvent(String description) { + this.description = description; + } + + public abstract boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source); + + @Override + public String toString() { + return description; + } + + } + + + private static final Translator trans = Application.getTranslator(); + + private final List listeners = new ArrayList(); + + private DeployEvent deployEvent = DeployEvent.EJECTION; + private double deployAltitude = 200; + private double deployDelay = 0; + + public boolean isActivationEvent(FlightEvent e, RocketComponent source) { + return deployEvent.isActivationEvent(this, e, source); + } + + public DeployEvent getDeployEvent() { + return deployEvent; + } + + public void setDeployEvent(DeployEvent deployEvent) { + if (this.deployEvent == deployEvent) { + return; + } + if (deployEvent == null) { + throw new NullPointerException("deployEvent is null"); + } + this.deployEvent = deployEvent; + fireChangeEvent(); + } + + public double getDeployAltitude() { + return deployAltitude; + } + + public void setDeployAltitude(double deployAltitude) { + if (MathUtil.equals(this.deployAltitude, deployAltitude)) { + return; + } + this.deployAltitude = deployAltitude; + fireChangeEvent(); + } + + public double getDeployDelay() { + return deployDelay; + } + + public void setDeployDelay(double deployDelay) { + if (MathUtil.equals(this.deployDelay, deployDelay)) { + return; + } + this.deployDelay = deployDelay; + fireChangeEvent(); + } + + @Override + public String toString() { + String description = deployEvent.toString(); + if (deployDelay > 0) { + description += " + " + deployDelay + "s"; + } + if (deployEvent == DeployEvent.ALTITUDE && deployAltitude != 0) { + description += " " + UnitGroup.UNITS_DISTANCE.toString(deployAltitude); + } + return description; + } + + + + + @Override + public void addChangeListener(StateChangeListener listener) { + listeners.add(listener); + } + + @Override + public void removeChangeListener(StateChangeListener listener) { + listeners.remove(listener); + } + + + + private void fireChangeEvent() { + EventObject event = new EventObject(this); + Object[] list = listeners.toArray(); + for (Object l : list) { + ((StateChangeListener) l).stateChanged(event); + } + } + + + @Override + public DeploymentConfiguration clone() { + DeploymentConfiguration that = new DeploymentConfiguration(); + that.deployAltitude = this.deployAltitude; + that.deployDelay = this.deployDelay; + that.deployEvent = this.deployEvent; + return that; + } + + +} \ No newline at end of file diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index 99bb9e036..55cf23095 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -674,12 +674,19 @@ public abstract class FinSet extends ExternalComponent { double x2 = getTabTrailingEdge(); double y = -getTabHeight(); + boolean add1 = x1 != points[0].x; + boolean add2 = x2 != points[points.length - 1].x; + int n = points.length; - points = ArrayUtils.copyOf(points, points.length + 4); - points[n] = new Coordinate(x2, 0); - points[n + 1] = new Coordinate(x2, y); - points[n + 2] = new Coordinate(x1, y); - points[n + 3] = new Coordinate(x1, 0); + points = ArrayUtils.copyOf(points, points.length + 2 + (add1 ? 1 : 0) + (add2 ? 1 : 0)); + + if (add2) + points[n++] = new Coordinate(x2, 0); + points[n++] = new Coordinate(x2, y); + points[n++] = new Coordinate(x1, y); + if (add1) + points[n++] = new Coordinate(x1, 0); + return points; } diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurableComponent.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurableComponent.java new file mode 100644 index 000000000..25a8dea5b --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurableComponent.java @@ -0,0 +1,19 @@ +package net.sf.openrocket.rocketcomponent; + +/** + * An interface implemented by components supporting + * per flight configuration configurable parameters. + */ +public interface FlightConfigurableComponent { + + /** + * Clone a flight configuration to a new flight configuration ID. + * This functions also in case there is no overridden values for the + * configuration ID. + * + * @param oldConfigId the old configuration ID + * @param newConfigId the new configuration ID + */ + public void cloneFlightConfiguration(String oldConfigId, String newConfigId); + +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurableParameter.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurableParameter.java new file mode 100644 index 000000000..f6e32efe7 --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurableParameter.java @@ -0,0 +1,19 @@ +package net.sf.openrocket.rocketcomponent; + +import net.sf.openrocket.util.ChangeSource; + +/** + * Interface for a parameter object that can be configured per + * flight configuration. + * + * @param the parameter type + */ +public interface FlightConfigurableParameter extends ChangeSource { + + /** + * Return a copy of this object. The listeners must not be copied + * to the new object. + */ + public E clone(); + +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java new file mode 100644 index 000000000..d99c81b9e --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java @@ -0,0 +1,74 @@ +package net.sf.openrocket.rocketcomponent; + +import net.sf.openrocket.util.ChangeSource; + +/** + * Represents a value or parameter that can vary based on the + * flight configuration ID. + *

+ * The parameter value is always defined, and null is not a valid + * parameter value. + * + * @param the parameter type + */ +public interface FlightConfiguration extends FlightConfigurableComponent { + + /** + * Return the default parameter value for this FlightConfiguration. + * This is used in case a per-flight configuration override + * has not been defined. + * + * @return the default parameter value (never null) + */ + public E getDefault(); + + /** + * Set the default parameter value for this FlightConfiguration. + * + * @param value the parameter value (null not allowed) + */ + public void setDefault(E value); + + + /** + * Return the parameter value for the provided flight configuration ID. + * This returns either the value specified for this flight config ID, + * or the default value. + * + * @param id the flight configuration ID + * @return the parameter to use (never null) + */ + public E get(String id); + + /** + * Set the parameter value for the provided flight configuration ID. + * This sets the override for this flight configuration ID. + * + * @param id the flight configuration ID + * @param value the parameter value (null not allowed) + */ + public void set(String id, E value); + + + /** + * Return whether a specific flight configuration ID is using the + * default value. + * + * @param id the flight configuration ID + * @return whether the default is being used + */ + public boolean isDefault(String id); + + /** + * Reset a specific flight configuration ID to use the default parameter value. + * + * @param id the flight configuration ID + */ + public void resetDefault(String id); + + /** + * Return the number of specific flight configurations other than the default. + * @return + */ + public int size(); +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurationImpl.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurationImpl.java new file mode 100644 index 000000000..f69fa6b22 --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfigurationImpl.java @@ -0,0 +1,163 @@ +package net.sf.openrocket.rocketcomponent; + +import java.util.EventObject; +import java.util.HashMap; + +import net.sf.openrocket.util.StateChangeListener; +import net.sf.openrocket.util.Utils; + +/** + * An implementation of FlightConfiguration that fires off events + * to the rocket components when the parameter value is changed. + * + * @param the parameter type + */ +class FlightConfigurationImpl> implements FlightConfiguration { + + private final HashMap map = new HashMap(); + private E defaultValue = null; + + private final RocketComponent component; + private final int eventType; + + private final Listener listener = new Listener(); + + + /** + * Construct a FlightConfiguration that has no overrides. + * + * @param component the rocket component on which events are fired when the parameter values are changed + * @param eventType the event type that will be fired on changes + * @param defaultValue the default value (null not allowed) + */ + public FlightConfigurationImpl(RocketComponent component, int eventType, E defaultValue) { + this.component = component; + this.eventType = eventType; + this.defaultValue = defaultValue; + + if (defaultValue == null) { + throw new NullPointerException("defaultValue is null"); + } + add(defaultValue); + } + + + /** + * Construct a copy of an existing FlightConfigurationImpl. + * + * @param component the rocket component on which events are fired when the parameter values are changed + * @param eventType the event type that will be fired on changes + */ + public FlightConfigurationImpl(FlightConfigurationImpl flightConfiguration, RocketComponent component, int eventType) { + this.component = component; + this.eventType = eventType; + + this.defaultValue = flightConfiguration.defaultValue.clone(); + for (String key : flightConfiguration.map.keySet()) { + this.map.put(key, flightConfiguration.map.get(key).clone()); + } + } + + + + @Override + public E getDefault() { + return defaultValue; + } + + @Override + public void setDefault(E value) { + if (value == null) { + throw new NullPointerException("value is null"); + } + if (Utils.equals(this.defaultValue, value)) { + return; + } + remove(this.defaultValue); + this.defaultValue = value; + add(value); + fireEvent(); + } + + + + @Override + public int size() { + return map.size(); + } + + + @Override + public E get(String id) { + if (map.containsKey(id)) { + return map.get(id); + } else { + return defaultValue; + } + } + + @Override + public void set(String id, E value) { + if (value == null) { + throw new NullPointerException("value is null"); + } + E previous = map.put(id, value); + remove(previous); + add(value); + fireEvent(); + } + + @Override + public boolean isDefault(String id) { + return !map.containsKey(id); + } + + @Override + public void resetDefault(String id) { + E previous = map.remove(id); + remove(previous); + fireEvent(); + } + + + + private void fireEvent() { + component.fireComponentChangeEvent(eventType); + } + + + @Override + public void cloneFlightConfiguration(String oldConfigId, String newConfigId) { + if (isDefault(oldConfigId)) { + this.resetDefault(newConfigId); + } else { + E original = this.get(oldConfigId); + this.set(newConfigId, original.clone()); + } + } + + + + private void add(E value) { + if (value != null) { + value.addChangeListener(listener); + } + } + + + private void remove(E value) { + if (value != null) { + value.removeChangeListener(listener); + } + } + + + private class Listener implements StateChangeListener { + @Override + public void stateChanged(EventObject e) { + fireEvent(); + } + } + + +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java index ac52a0f88..5ac246842 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java @@ -100,6 +100,8 @@ public class FreeformFinSet extends FinSet { name.substring(componentTypeName.length())); } + freeform.setAppearance(finset.getAppearance()); + // Add freeform fin set to parent if (parent != null) { parent.addChild(freeform, position); diff --git a/core/src/net/sf/openrocket/rocketcomponent/IgnitionConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/IgnitionConfiguration.java new file mode 100644 index 000000000..984c18059 --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/IgnitionConfiguration.java @@ -0,0 +1,138 @@ +package net.sf.openrocket.rocketcomponent; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.AbstractChangeSource; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.StateChangeListener; + +public class IgnitionConfiguration implements FlightConfigurableParameter { + + public enum IgnitionEvent { + //// Automatic (launch or ejection charge) + AUTOMATIC("MotorMount.IgnitionEvent.AUTOMATIC") { + @Override + public boolean isActivationEvent(FlightEvent e, RocketComponent source) { + int count = source.getRocket().getStageCount(); + int stage = source.getStageNumber(); + + if (stage == count - 1) { + return LAUNCH.isActivationEvent(e, source); + } else { + return EJECTION_CHARGE.isActivationEvent(e, source); + } + } + }, + //// Launch + LAUNCH("MotorMount.IgnitionEvent.LAUNCH") { + @Override + public boolean isActivationEvent(FlightEvent e, RocketComponent source) { + return (e.getType() == FlightEvent.Type.LAUNCH); + } + }, + //// First ejection charge of previous stage + EJECTION_CHARGE("MotorMount.IgnitionEvent.EJECTION_CHARGE") { + @Override + public boolean isActivationEvent(FlightEvent e, RocketComponent source) { + if (e.getType() != FlightEvent.Type.EJECTION_CHARGE) + return false; + + int charge = e.getSource().getStageNumber(); + int mount = source.getStageNumber(); + return (mount + 1 == charge); + } + }, + //// First burnout of previous stage + BURNOUT("MotorMount.IgnitionEvent.BURNOUT") { + @Override + public boolean isActivationEvent(FlightEvent e, RocketComponent source) { + if (e.getType() != FlightEvent.Type.BURNOUT) + return false; + + int charge = e.getSource().getStageNumber(); + int mount = source.getStageNumber(); + return (mount + 1 == charge); + } + }, + //// Never + NEVER("MotorMount.IgnitionEvent.NEVER") { + @Override + public boolean isActivationEvent(FlightEvent e, RocketComponent source) { + return false; + } + }, + ; + + + private static final Translator trans = Application.getTranslator(); + private final String description; + + IgnitionEvent(String description) { + this.description = description; + } + + public abstract boolean isActivationEvent(FlightEvent e, RocketComponent source); + + @Override + public String toString() { + return trans.get(description); + } + } + + + private IgnitionEvent ignitionEvent = IgnitionEvent.AUTOMATIC; + private double delay = 0; + + private final AbstractChangeSource listeners = new AbstractChangeSource(); + + + + public IgnitionEvent getIgnitionEvent() { + return ignitionEvent; + } + + public void setIgnitionEvent(IgnitionEvent ignitionEvent) { + if (ignitionEvent == null) { + throw new NullPointerException("ignitionEvent is null"); + } + if (ignitionEvent == this.ignitionEvent) { + return; + } + this.ignitionEvent = ignitionEvent; + listeners.fireChangeEvent(this); + } + + + public double getIgnitionDelay() { + return delay; + } + + public void setIgnitionDelay(double delay) { + if (MathUtil.equals(delay, this.delay)) { + return; + } + this.delay = delay; + listeners.fireChangeEvent(this); + } + + @Override + public IgnitionConfiguration clone() { + IgnitionConfiguration copy = new IgnitionConfiguration(); + copy.ignitionEvent = this.ignitionEvent; + copy.delay = this.delay; + return copy; + } + + + @Override + public void addChangeListener(StateChangeListener listener) { + listeners.addChangeListener(listener); + } + + + @Override + public void removeChangeListener(StateChangeListener listener) { + listeners.removeChangeListener(listener); + } +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java b/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java index 937edbf5e..2f9ab99d2 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java +++ b/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java @@ -1,7 +1,6 @@ package net.sf.openrocket.rocketcomponent; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import net.sf.openrocket.l10n.Translator; @@ -19,22 +18,18 @@ import net.sf.openrocket.util.MathUtil; * * @author Sampo Niskanen */ -public class InnerTube extends ThicknessRingComponent - implements Clusterable, RadialParent, MotorMount { +public class InnerTube extends ThicknessRingComponent implements Clusterable, RadialParent, MotorMount { private static final Translator trans = Application.getTranslator(); private ClusterConfiguration cluster = ClusterConfiguration.SINGLE; private double clusterScale = 1.0; private double clusterRotation = 0.0; - private boolean motorMount = false; - private HashMap ejectionDelays = new HashMap(); - private HashMap motors = new HashMap(); - private IgnitionEvent ignitionEvent = IgnitionEvent.AUTOMATIC; - private double ignitionDelay = 0; private double overhang = 0; + private FlightConfigurationImpl motorConfigurations; + private FlightConfigurationImpl ignitionConfigurations; /** * Main constructor. @@ -44,6 +39,9 @@ public class InnerTube extends ThicknessRingComponent this.setOuterRadius(0.019 / 2); this.setInnerRadius(0.018 / 2); this.setLength(0.070); + + this.motorConfigurations = new MotorFlightConfigurationImpl(this, ComponentChangeEvent.MOTOR_CHANGE, MotorConfiguration.NO_MOTORS); + this.ignitionConfigurations = new FlightConfigurationImpl(this, ComponentChangeEvent.EVENT_CHANGE, new IgnitionConfiguration()); } @@ -82,24 +80,24 @@ public class InnerTube extends ThicknessRingComponent public ComponentPreset.Type getPresetType() { return ComponentPreset.Type.BODY_TUBE; } - + @Override protected void loadFromPreset(ComponentPreset preset) { - if ( preset.has(ComponentPreset.OUTER_DIAMETER) ) { + if (preset.has(ComponentPreset.OUTER_DIAMETER)) { double outerDiameter = preset.get(ComponentPreset.OUTER_DIAMETER); - this.outerRadius = outerDiameter/2.0; - if ( preset.has(ComponentPreset.INNER_DIAMETER) ) { + this.outerRadius = outerDiameter / 2.0; + if (preset.has(ComponentPreset.INNER_DIAMETER)) { double innerDiameter = preset.get(ComponentPreset.INNER_DIAMETER); - this.thickness = (outerDiameter-innerDiameter) / 2.0; + this.thickness = (outerDiameter - innerDiameter) / 2.0; } } - + super.loadFromPreset(preset); - + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + ///////////// Cluster methods ////////////// /** @@ -152,7 +150,7 @@ public class InnerTube extends ThicknessRingComponent } - + /** * @return the clusterRotation */ @@ -217,16 +215,34 @@ public class InnerTube extends ThicknessRingComponent return newArray; } - - - //////////////// Motor mount ///////////////// + + @Override + public FlightConfiguration getMotorConfiguration() { + return motorConfigurations; + } + + + @Override + public FlightConfiguration getIgnitionConfiguration() { + return ignitionConfigurations; + } + + + @Override + public void cloneFlightConfiguration(String oldConfigId, String newConfigId) { + motorConfigurations.cloneFlightConfiguration(oldConfigId, newConfigId); + ignitionConfigurations.cloneFlightConfiguration(oldConfigId, newConfigId); + } + + @Override public boolean isMotorMount() { return motorMount; } + @Override public void setMotorMount(boolean mount) { if (motorMount == mount) @@ -235,89 +251,33 @@ public class InnerTube extends ThicknessRingComponent fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); } - @Override - public Motor getMotor(String id) { - if (id == null) - return null; - - // Check whether the id is valid for the current rocket - RocketComponent root = this.getRoot(); - if (!(root instanceof Rocket)) - return null; - if (!((Rocket) root).isMotorConfigurationID(id)) - return null; - - return motors.get(id); - } - - @Override - public void setMotor(String id, Motor motor) { - if (id == null) { - if (motor != null) { - throw new IllegalArgumentException("Cannot set non-null motor for id null"); - } - } - Motor current = motors.get(id); - if ((motor == null && current == null) || - (motor != null && motor.equals(current))) - return; - motors.put(id, motor); - fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); - } - - @Override - public double getMotorDelay(String id) { - Double delay = ejectionDelays.get(id); - if (delay == null) - return Motor.PLUGGED; - return delay; - } - - @Override - public void setMotorDelay(String id, double delay) { - ejectionDelays.put(id, delay); - fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); - } - - @Deprecated - @Override - public int getMotorCount() { - return getClusterCount(); - } @Override public double getMotorMountDiameter() { return getInnerRadius() * 2; } + @SuppressWarnings("deprecation") + @Deprecated @Override - public IgnitionEvent getIgnitionEvent() { - return ignitionEvent; + public int getMotorCount() { + return getClusterCount(); } + @SuppressWarnings("deprecation") + @Deprecated @Override - public void setIgnitionEvent(IgnitionEvent event) { - if (ignitionEvent == event) - return; - ignitionEvent = event; - fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); + public Motor getMotor(String id) { + return this.motorConfigurations.get(id).getMotor(); } - + @SuppressWarnings("deprecation") + @Deprecated @Override - public double getIgnitionDelay() { - return ignitionDelay; + public double getMotorDelay(String id) { + return this.motorConfigurations.get(id).getEjectionDelay(); } - @Override - public void setIgnitionDelay(double delay) { - if (MathUtil.equals(delay, ignitionDelay)) - return; - ignitionDelay = delay; - fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); - } - - @Override public double getMotorOverhang() { return overhang; @@ -331,10 +291,9 @@ public class InnerTube extends ThicknessRingComponent fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - @Override public Coordinate getMotorPosition(String id) { - Motor motor = motors.get(id); + Motor motor = getMotor(id); if (motor == null) { throw new IllegalArgumentException("No motor with id " + id + " defined."); } @@ -342,19 +301,14 @@ public class InnerTube extends ThicknessRingComponent return new Coordinate(this.getLength() - motor.getLength() + this.getMotorOverhang()); } - /* - * (non-Javadoc) - * Copy the motor and ejection delay HashMaps. - * - * @see rocketcomponent.RocketComponent#copy() - */ - @SuppressWarnings("unchecked") @Override protected RocketComponent copyWithOriginalID() { - RocketComponent c = super.copyWithOriginalID(); - ((InnerTube) c).motors = (HashMap) motors.clone(); - ((InnerTube) c).ejectionDelays = (HashMap) ejectionDelays.clone(); - return c; + InnerTube copy = (InnerTube) super.copyWithOriginalID(); + copy.motorConfigurations = new FlightConfigurationImpl(motorConfigurations, copy, ComponentChangeEvent.MOTOR_CHANGE); + copy.ignitionConfigurations = new FlightConfigurationImpl(ignitionConfigurations, copy, ComponentChangeEvent.EVENT_CHANGE); + return copy; } + + } \ No newline at end of file diff --git a/core/src/net/sf/openrocket/rocketcomponent/MotorConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/MotorConfiguration.java new file mode 100644 index 000000000..b04e82ca3 --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/MotorConfiguration.java @@ -0,0 +1,89 @@ +package net.sf.openrocket.rocketcomponent; + +import java.util.EventObject; +import java.util.List; + +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.util.ArrayList; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.StateChangeListener; +import net.sf.openrocket.util.Utils; + +/** + * A single motor configuration. This includes the selected motor + * and the ejection charge delay. + */ +public class MotorConfiguration implements FlightConfigurableParameter { + + /** Immutable configuration with no motor and zero delay. */ + public static final MotorConfiguration NO_MOTORS = new MotorConfiguration() { + @Override + public void setMotor(Motor motor) { + throw new UnsupportedOperationException("Trying to modify immutable no-motors configuration"); + }; + + @Override + public void setEjectionDelay(double delay) { + throw new UnsupportedOperationException("Trying to modify immutable no-motors configuration"); + }; + }; + + private final List listeners = new ArrayList(); + + private Motor motor; + private double ejectionDelay; + + + public Motor getMotor() { + return motor; + } + + public void setMotor(Motor motor) { + if (Utils.equals(this.motor, motor)) { + return; + } + this.motor = motor; + fireChangeEvent(); + } + + public double getEjectionDelay() { + return ejectionDelay; + } + + public void setEjectionDelay(double delay) { + if (MathUtil.equals(ejectionDelay, delay)) { + return; + } + this.ejectionDelay = delay; + fireChangeEvent(); + } + + + @Override + public MotorConfiguration clone() { + MotorConfiguration copy = new MotorConfiguration(); + copy.motor = this.motor; + copy.ejectionDelay = this.ejectionDelay; + return copy; + } + + + @Override + public void addChangeListener(StateChangeListener listener) { + listeners.add(listener); + } + + @Override + public void removeChangeListener(StateChangeListener listener) { + listeners.remove(listener); + } + + private void fireChangeEvent() { + EventObject event = new EventObject(this); + Object[] list = listeners.toArray(); + for (Object l : list) { + ((StateChangeListener) l).stateChanged(event); + } + } + +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/MotorFlightConfigurationImpl.java b/core/src/net/sf/openrocket/rocketcomponent/MotorFlightConfigurationImpl.java new file mode 100644 index 000000000..8be3a841a --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/MotorFlightConfigurationImpl.java @@ -0,0 +1,18 @@ +package net.sf.openrocket.rocketcomponent; + +/** + * FlightConfiguration implementation that prevents changing the default value. + * This is used for motors, where the default value is always no motor. + */ +public class MotorFlightConfigurationImpl> extends FlightConfigurationImpl { + + public MotorFlightConfigurationImpl(RocketComponent component, int eventType, E defaultValue) { + super(component, eventType, defaultValue); + } + + @Override + public void setDefault(E value) { + throw new UnsupportedOperationException("Cannot change default value of motor configuration"); + } + +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/MotorMount.java b/core/src/net/sf/openrocket/rocketcomponent/MotorMount.java index 1c849d4d2..d11c32b92 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/MotorMount.java +++ b/core/src/net/sf/openrocket/rocketcomponent/MotorMount.java @@ -1,85 +1,10 @@ package net.sf.openrocket.rocketcomponent; -import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.simulation.FlightEvent; -import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.ChangeSource; import net.sf.openrocket.util.Coordinate; -public interface MotorMount extends ChangeSource { - - public static enum IgnitionEvent { - //// Automatic (launch or ejection charge) - AUTOMATIC("MotorMount.IgnitionEvent.AUTOMATIC") { - @Override - public boolean isActivationEvent(FlightEvent e, RocketComponent source) { - int count = source.getRocket().getStageCount(); - int stage = source.getStageNumber(); - - if (stage == count - 1) { - return LAUNCH.isActivationEvent(e, source); - } else { - return EJECTION_CHARGE.isActivationEvent(e, source); - } - } - }, - //// Launch - LAUNCH("MotorMount.IgnitionEvent.LAUNCH") { - @Override - public boolean isActivationEvent(FlightEvent e, RocketComponent source) { - return (e.getType() == FlightEvent.Type.LAUNCH); - } - }, - //// First ejection charge of previous stage - EJECTION_CHARGE("MotorMount.IgnitionEvent.EJECTION_CHARGE") { - @Override - public boolean isActivationEvent(FlightEvent e, RocketComponent source) { - if (e.getType() != FlightEvent.Type.EJECTION_CHARGE) - return false; - - int charge = e.getSource().getStageNumber(); - int mount = source.getStageNumber(); - return (mount + 1 == charge); - } - }, - //// First burnout of previous stage - BURNOUT("MotorMount.IgnitionEvent.BURNOUT") { - @Override - public boolean isActivationEvent(FlightEvent e, RocketComponent source) { - if (e.getType() != FlightEvent.Type.BURNOUT) - return false; - - int charge = e.getSource().getStageNumber(); - int mount = source.getStageNumber(); - return (mount + 1 == charge); - } - }, - //// Never - NEVER("MotorMount.IgnitionEvent.NEVER") { - @Override - public boolean isActivationEvent(FlightEvent e, RocketComponent source) { - return false; - } - }, - ; - - - private static final Translator trans = Application.getTranslator(); - private final String description; - - IgnitionEvent(String description) { - this.description = description; - } - - public abstract boolean isActivationEvent(FlightEvent e, RocketComponent source); - - @Override - public String toString() { - return trans.get(description); - } - }; - +public interface MotorMount extends ChangeSource, FlightConfigurableComponent { /** * Is the component currently a motor mount. @@ -94,6 +19,17 @@ public interface MotorMount extends ChangeSource { public void setMotorMount(boolean mount); + /** + * Return the motor configurations for this motor mount. + */ + public FlightConfiguration getMotorConfiguration(); + + /** + * Return the ignition configurations for this motor mount. + */ + public FlightConfiguration getIgnitionConfiguration(); + + /** * Return the motor for the motor configuration. May return null * if no motor has been set. This method must return null if ID @@ -102,18 +38,11 @@ public interface MotorMount extends ChangeSource { * * @param id the motor configuration ID * @return the motor, or null if not set. + * @deprecated Use getMotorConfiguration().get(id).getMotor() instead. */ + @Deprecated public Motor getMotor(String id); - /** - * Set the motor for the motor configuration. May be set to null - * to remove the motor. - * - * @param id the motor configuration ID - * @param motor the motor, or null. - */ - public void setMotor(String id, Motor motor); - /** * Get the number of similar motors clustered. * @@ -125,7 +54,7 @@ public interface MotorMount extends ChangeSource { public int getMotorCount(); - + /** * Return the ejection charge delay of given motor configuration. * A "plugged" motor without an ejection charge is given by @@ -133,48 +62,11 @@ public interface MotorMount extends ChangeSource { * * @param id the motor configuration ID * @return the ejection charge delay. + * @deprecated Use getMotorConfiguration().get(id).getMotor() instead. */ + @Deprecated public double getMotorDelay(String id); - /** - * Set the ejection change delay of the given motor configuration. - * The ejection charge is disable (a "plugged" motor) is set by - * {@link Motor#PLUGGED} (Double.POSITIVE_INFINITY). - * - * @param id the motor configuration ID - * @param delay the ejection charge delay. - */ - public void setMotorDelay(String id, double delay); - - - /** - * Return the event that ignites this motor. - * - * @return the {@link IgnitionEvent} that ignites this motor. - */ - public IgnitionEvent getIgnitionEvent(); - - /** - * Sets the event that ignites this motor. - * - * @param event the {@link IgnitionEvent} that ignites this motor. - */ - public void setIgnitionEvent(IgnitionEvent event); - - - /** - * Returns the ignition delay of this motor. - * - * @return the ignition delay - */ - public double getIgnitionDelay(); - - /** - * Sets the ignition delay of this motor. - * - * @param delay the ignition delay. - */ - public void setIgnitionDelay(double delay); /** @@ -192,7 +84,7 @@ public interface MotorMount extends ChangeSource { public void setMotorOverhang(double overhang); - + /** * Return the inner diameter of the motor mount. * diff --git a/core/src/net/sf/openrocket/rocketcomponent/RecoveryDevice.java b/core/src/net/sf/openrocket/rocketcomponent/RecoveryDevice.java index 3c5a88f1b..c9318b28b 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RecoveryDevice.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RecoveryDevice.java @@ -1,12 +1,9 @@ package net.sf.openrocket.rocketcomponent; -import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.material.Material; import net.sf.openrocket.preset.ComponentPreset; -import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.MathUtil; -import net.sf.openrocket.util.Pair; /** @@ -21,102 +18,22 @@ import net.sf.openrocket.util.Pair; * * @author Sampo Niskanen */ -public abstract class RecoveryDevice extends MassObject { - private static final Translator trans = Application.getTranslator(); - - public static enum DeployEvent { - LAUNCH(trans.get("RecoveryDevice.DeployEvent.LAUNCH")) { - @Override - public boolean isActivationEvent(FlightEvent e, RocketComponent source) { - return e.getType() == FlightEvent.Type.LAUNCH; - } - }, - EJECTION(trans.get("RecoveryDevice.DeployEvent.EJECTION")) { - @Override - public boolean isActivationEvent(FlightEvent e, RocketComponent source) { - if (e.getType() != FlightEvent.Type.EJECTION_CHARGE) - return false; - RocketComponent charge = e.getSource(); - return charge.getStageNumber() == source.getStageNumber(); - } - }, - APOGEE(trans.get("RecoveryDevice.DeployEvent.APOGEE")) { - @Override - public boolean isActivationEvent(FlightEvent e, RocketComponent source) { - return e.getType() == FlightEvent.Type.APOGEE; - } - }, - ALTITUDE(trans.get("RecoveryDevice.DeployEvent.ALTITUDE")) { - @SuppressWarnings("unchecked") - @Override - public boolean isActivationEvent(FlightEvent e, RocketComponent source) { - if (e.getType() != FlightEvent.Type.ALTITUDE) - return false; - - double alt = ((RecoveryDevice) source).getDeployAltitude(); - Pair altitude = (Pair) e.getData(); - - return (altitude.getU() >= alt) && (altitude.getV() <= alt); - } - }, - LOWER_STAGE_SEPARATION(trans.get("RecoveryDevice.DeployEvent.LOWER_STAGE_SEPARATION")) { - @Override - public boolean isActivationEvent(FlightEvent e, RocketComponent source) { - if (e.getType() != FlightEvent.Type.STAGE_SEPARATION) - return false; - - int separation = e.getSource().getStageNumber(); - int current = source.getStageNumber(); - return (current + 1 == separation); - } - }, - NEVER(trans.get("RecoveryDevice.DeployEvent.NEVER")) { - @Override - public boolean isActivationEvent(FlightEvent e, RocketComponent source) { - return false; - } - }; - - private final String description; - - DeployEvent(String description) { - this.description = description; - } - - public abstract boolean isActivationEvent(FlightEvent e, RocketComponent source); - - @Override - public String toString() { - return description; - } - - } - - - private DeployEvent deployEvent = DeployEvent.EJECTION; - private double deployAltitude = 200; - private double deployDelay = 0; +public abstract class RecoveryDevice extends MassObject implements FlightConfigurableComponent { private double cd = Parachute.DEFAULT_CD; private boolean cdAutomatic = true; - private Material.Surface material; + private FlightConfigurationImpl deploymentConfigurations; + + public RecoveryDevice() { - this(Application.getPreferences().getDefaultComponentMaterial(RecoveryDevice.class, Material.Type.SURFACE)); + this.deploymentConfigurations = new FlightConfigurationImpl(this, ComponentChangeEvent.EVENT_CHANGE, new DeploymentConfiguration()); + setMaterial(Application.getPreferences().getDefaultComponentMaterial(RecoveryDevice.class, Material.Type.SURFACE)); } - public RecoveryDevice(Material material) { - super(); - setMaterial(material); - } - - public RecoveryDevice(double length, double radius, Material material) { - super(length, radius); - setMaterial(material); - } @@ -175,63 +92,37 @@ public abstract class RecoveryDevice extends MassObject { } - - - public DeployEvent getDeployEvent() { - return deployEvent; - } - - public void setDeployEvent(DeployEvent deployEvent) { - if (this.deployEvent == deployEvent) - return; - this.deployEvent = deployEvent; - fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); + public FlightConfiguration getDeploymentConfiguration() { + return deploymentConfigurations; } - public double getDeployAltitude() { - return deployAltitude; + @Override + public void cloneFlightConfiguration(String oldConfigId, String newConfigId) { + deploymentConfigurations.cloneFlightConfiguration(oldConfigId, newConfigId); } - public void setDeployAltitude(double deployAltitude) { - if (MathUtil.equals(this.deployAltitude, deployAltitude)) - return; - this.deployAltitude = deployAltitude; - if (getDeployEvent() == DeployEvent.ALTITUDE) - fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); - else - fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); - } - - - public double getDeployDelay() { - return deployDelay; - } - - public void setDeployDelay(double delay) { - delay = MathUtil.max(delay, 0); - if (MathUtil.equals(this.deployDelay, delay)) - return; - this.deployDelay = delay; - fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); - } - - @Override public double getComponentMass() { return getArea() * getMaterial().getDensity(); } - + @Override protected void loadFromPreset(ComponentPreset preset) { - if ( preset.has(ComponentPreset.MATERIAL)) { + if (preset.has(ComponentPreset.MATERIAL)) { Material m = preset.get(ComponentPreset.MATERIAL); - this.material = (Material.Surface)m; + this.material = (Material.Surface) m; } super.loadFromPreset(preset); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); - } - + + @Override + protected RocketComponent copyWithOriginalID() { + RecoveryDevice copy = (RecoveryDevice) super.copyWithOriginalID(); + copy.deploymentConfigurations = new FlightConfigurationImpl(deploymentConfigurations, + copy, ComponentChangeEvent.EVENT_CHANGE); + return copy; + } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java index d493ca493..1584813f5 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -11,10 +11,8 @@ import java.util.UUID; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.LogHelper; -import net.sf.openrocket.motor.Motor; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.ArrayList; -import net.sf.openrocket.util.Chars; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.StateChangeListener; @@ -35,53 +33,54 @@ public class Rocket extends RocketComponent { private static final LogHelper log = Application.getLogger(); private static final Translator trans = Application.getTranslator(); + public static final String DEFAULT_NAME = "[{motors}]"; public static final double DEFAULT_REFERENCE_LENGTH = 0.01; - + /** * List of component change listeners. */ private List listenerList = new ArrayList(); - + /** * When freezeList != null, events are not dispatched but stored in the list. * When the structure is thawed, a single combined event will be fired. */ private List freezeList = null; - + private int modID; private int massModID; private int aeroModID; private int treeModID; private int functionalModID; - + private ReferenceType refType = ReferenceType.MAXIMUM; // Set in constructor private double customReferenceLength = DEFAULT_REFERENCE_LENGTH; - + // The default configuration used in dialogs private final Configuration defaultConfiguration; - + private String designer = ""; private String revision = ""; - - // Motor configuration list - private ArrayList motorConfigurationIDs = new ArrayList(); - private HashMap motorConfigurationNames = new HashMap(); + + // Flight configuration list + private ArrayList flightConfigurationIDs = new ArrayList(); + private HashMap flightConfigurationNames = new HashMap(); { - motorConfigurationIDs.add(null); + flightConfigurationIDs.add(null); } - + // Does the rocket have a perfect finish (a notable amount of laminar flow) private boolean perfectFinish = false; - + ///////////// Constructor ///////////// public Rocket() { @@ -95,7 +94,7 @@ public class Rocket extends RocketComponent { } - + public String getDesigner() { checkState(); return designer; @@ -195,8 +194,8 @@ public class Rocket extends RocketComponent { } - - + + public ReferenceType getReferenceType() { checkState(); return refType; @@ -227,9 +226,9 @@ public class Rocket extends RocketComponent { } - - - + + + /** * Set whether the rocket has a perfect finish. This will affect whether the * boundary layer is assumed to be fully turbulent or not. @@ -244,7 +243,7 @@ public class Rocket extends RocketComponent { } - + /** * Get whether the rocket has a perfect finish. * @@ -255,9 +254,9 @@ public class Rocket extends RocketComponent { } - - - + + + /** * Make a deep copy of the Rocket structure. This method is exposed as public to allow * for undo/redo system functionality. @@ -266,9 +265,9 @@ public class Rocket extends RocketComponent { @Override public Rocket copyWithOriginalID() { Rocket copy = (Rocket) super.copyWithOriginalID(); - copy.motorConfigurationIDs = this.motorConfigurationIDs.clone(); - copy.motorConfigurationNames = - (HashMap) this.motorConfigurationNames.clone(); + copy.flightConfigurationIDs = this.flightConfigurationIDs.clone(); + copy.flightConfigurationNames = + (HashMap) this.flightConfigurationNames.clone(); copy.resetListeners(); return copy; @@ -306,14 +305,14 @@ public class Rocket extends RocketComponent { this.refType = r.refType; this.customReferenceLength = r.customReferenceLength; - this.motorConfigurationIDs = r.motorConfigurationIDs.clone(); - this.motorConfigurationNames = - (HashMap) r.motorConfigurationNames.clone(); + this.flightConfigurationIDs = r.flightConfigurationIDs.clone(); + this.flightConfigurationNames = + (HashMap) r.flightConfigurationNames.clone(); this.perfectFinish = r.perfectFinish; - String id = defaultConfiguration.getMotorConfigurationID(); - if (!this.motorConfigurationIDs.contains(id)) - defaultConfiguration.setMotorConfigurationID(null); + String id = defaultConfiguration.getFlightConfigurationID(); + if (!this.flightConfigurationIDs.contains(id)) + defaultConfiguration.setFlightConfigurationID(null); this.checkComponentStructure(); @@ -326,8 +325,8 @@ public class Rocket extends RocketComponent { } - - + + /////// Implement the ComponentChangeListener lists /** @@ -342,8 +341,8 @@ public class Rocket extends RocketComponent { public void printListeners() { System.out.println("" + this + " has " + listenerList.size() + " listeners:"); - int i =0; - for( EventListener l : listenerList ) { + int i = 0; + for (EventListener l : listenerList) { System.out.println(" " + (i) + ": " + l); i++; } @@ -365,21 +364,6 @@ public class Rocket extends RocketComponent { } - @Override - public void addChangeListener(EventListener l) { - checkState(); - listenerList.add(l); - log.verbose("Added ChangeListener " + l + ", current number of listeners is " + - listenerList.size()); - } - - @Override - public void removeChangeListener(EventListener l) { - listenerList.remove(l); - log.verbose("Removed ChangeListener " + l + ", current number of listeners is " + - listenerList.size()); - } - @Override protected void fireComponentChangeEvent(ComponentChangeEvent e) { @@ -417,12 +401,12 @@ public class Rocket extends RocketComponent { // Notify all listeners // Copy the list before iterating to prevent concurrent modification exceptions. - EventListener[] list = listenerList.toArray( new EventListener[0] ); - for ( EventListener l : list ) { - if ( l instanceof ComponentChangeListener ) { - ((ComponentChangeListener) l ).componentChanged(e); - } else if ( l instanceof StateChangeListener ) { - ((StateChangeListener) l ).stateChanged(e); + EventListener[] list = listenerList.toArray(new EventListener[0]); + for (EventListener l : list) { + if (l instanceof ComponentChangeListener) { + ((ComponentChangeListener) l).componentChanged(e); + } else if (l instanceof StateChangeListener) { + ((StateChangeListener) l).stateChanged(e); } } } finally { @@ -493,11 +477,11 @@ public class Rocket extends RocketComponent { } - - + + //////// Motor configurations //////// - + /** * Return the default configuration. This should be used in the user interface * to ensure a consistent rocket configuration between dialogs. It should NOT @@ -512,26 +496,26 @@ public class Rocket extends RocketComponent { /** - * Return an array of the motor configuration IDs. This array is guaranteed + * Return an array of the flight configuration IDs. This array is guaranteed * to contain the null ID as the first element. * - * @return an array of the motor configuration IDs. + * @return an array of the flight configuration IDs. */ - public String[] getMotorConfigurationIDs() { + public String[] getFlightConfigurationIDs() { checkState(); - return motorConfigurationIDs.toArray(new String[0]); + return flightConfigurationIDs.toArray(new String[0]); } /** - * Add a new motor configuration ID to the motor configurations. The new ID + * Add a new flight configuration ID to the flight configurations. The new ID * is returned. * - * @return the new motor configuration ID. + * @return the new flight configuration ID. */ - public String newMotorConfigurationID() { + public String newFlightConfigurationID() { checkState(); String id = UUID.randomUUID().toString(); - motorConfigurationIDs.add(id); + flightConfigurationIDs.add(id); fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); return id; } @@ -544,24 +528,24 @@ public class Rocket extends RocketComponent { */ public boolean addMotorConfigurationID(String id) { checkState(); - if (id == null || motorConfigurationIDs.contains(id)) + if (id == null || flightConfigurationIDs.contains(id)) return false; - motorConfigurationIDs.add(id); + flightConfigurationIDs.add(id); fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); return true; } /** - * Remove a motor configuration ID from the configuration IDs. The null + * Remove a flight configuration ID from the configuration IDs. The null * ID cannot be removed, and an attempt to remove it will be silently ignored. * - * @param id the motor configuration ID to remove + * @param id the flight configuration ID to remove */ - public void removeMotorConfigurationID(String id) { + public void removeFlightConfigurationID(String id) { checkState(); if (id == null) return; - motorConfigurationIDs.remove(id); + flightConfigurationIDs.remove(id); fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); } @@ -572,13 +556,13 @@ public class Rocket extends RocketComponent { * @param id the configuration ID. * @return whether a motor configuration with that ID exists. */ - public boolean isMotorConfigurationID(String id) { + public boolean isFlightConfigurationID(String id) { checkState(); - return motorConfigurationIDs.contains(id); + return flightConfigurationIDs.contains(id); } - + /** * Check whether the given motor configuration ID has motors defined for it. * @@ -598,7 +582,7 @@ public class Rocket extends RocketComponent { MotorMount mount = (MotorMount) c; if (!mount.isMotorMount()) continue; - if (mount.getMotor(id) != null) { + if (mount.getMotorConfiguration().get(id).getMotor() != null) { return true; } } @@ -608,175 +592,47 @@ public class Rocket extends RocketComponent { /** - * Return the user-set name of the motor configuration. If no name has been set, - * returns an empty string (not null). + * Return the user-set name of the flight configuration. If no name has been set, + * returns the default name ({@link #DEFAULT_NAME}). * - * @param id the motor configuration id + * @param id the flight configuration id * @return the configuration name */ - public String getMotorConfigurationName(String id) { + // FIXME: Check references to this method adhere to the new returning of DEFAULT_NAME + public String getFlightConfigurationName(String id) { checkState(); - if (!isMotorConfigurationID(id)) - return ""; - String s = motorConfigurationNames.get(id); + if (!isFlightConfigurationID(id)) + return DEFAULT_NAME; + String s = flightConfigurationNames.get(id); if (s == null) - return ""; + return DEFAULT_NAME; return s; } /** - * Set the name of the motor configuration. A name can be unset by passing + * Set the name of the flight configuration. A name can be unset by passing * null or an empty string. * - * @param id the motor configuration id - * @param name the name for the motor configuration + * @param id the flight configuration id + * @param name the name for the flight configuration */ - public void setMotorConfigurationName(String id, String name) { + public void setFlightConfigurationName(String id, String name) { checkState(); - motorConfigurationNames.put(id, name); + if (name == null || name.equals("") || DEFAULT_NAME.equals(name)) { + flightConfigurationNames.remove(id); + } else { + flightConfigurationNames.put(id, name); + } fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - /** - * Return either the motor configuration name (if set) or its description. - * - * @param id the motor configuration ID. - * @return a textual representation of the configuration - */ - public String getMotorConfigurationNameOrDescription(String id) { - checkState(); - String name; - - name = getMotorConfigurationName(id); - if (name != null && !name.equals("")) - return name; - - return getMotorConfigurationDescription(id); - } - - /** - * Return a description for the motor configuration, generated from the motor - * designations of the components. - * - * @param id the motor configuration ID. - * @return a textual representation of the configuration - */ - @SuppressWarnings("null") - public String getMotorConfigurationDescription(String id) { - checkState(); - String name; - int motorCount = 0; - - // Generate the description - - // First iterate over each stage and store the designations of each motor - List> list = new ArrayList>(); - List currentList = null; - - Iterator iterator = this.iterator(); - while (iterator.hasNext()) { - RocketComponent c = iterator.next(); - - if (c instanceof Stage) { - - currentList = new ArrayList(); - list.add(currentList); - - } else if (c instanceof MotorMount) { - - MotorMount mount = (MotorMount) c; - Motor motor = mount.getMotor(id); - - if (mount.isMotorMount() && motor != null) { - String designation = motor.getDesignation(mount.getMotorDelay(id)); - - for (int i = 0; i < mount.getMotorCount(); i++) { - currentList.add(designation); - motorCount++; - } - } - - } - } - - if (motorCount == 0) { - //// [No motors] - return trans.get("Rocket.motorCount.Nomotor"); - } - - // Change multiple occurrences of a motor to n x motor - List stages = new ArrayList(); - - for (List stage : list) { - String stageName = ""; - String previous = null; - int count = 0; - - Collections.sort(stage); - for (String current : stage) { - if (current.equals(previous)) { - - count++; - - } else { - - if (previous != null) { - String s = ""; - if (count > 1) { - s = "" + count + Chars.TIMES + previous; - } else { - s = previous; - } - - if (stageName.equals("")) - stageName = s; - else - stageName = stageName + "," + s; - } - - previous = current; - count = 1; - - } - } - if (previous != null) { - String s = ""; - if (count > 1) { - s = "" + count + Chars.TIMES + previous; - } else { - s = previous; - } - - if (stageName.equals("")) - stageName = s; - else - stageName = stageName + "," + s; - } - - stages.add(stageName); - } - - name = "["; - for (int i = 0; i < stages.size(); i++) { - String s = stages.get(i); - if (s.equals("")) - s = "None"; - if (i == 0) - name = name + s; - else - name = name + "; " + s; - } - name += "]"; - return name; - } - //////// Obligatory component information - + @Override public String getComponentName() { //// Rocket diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 704290654..fd56b2821 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -1,11 +1,13 @@ package net.sf.openrocket.rocketcomponent; import java.util.Collection; -import java.util.EventListener; +import java.util.EventObject; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.startup.Application; @@ -13,16 +15,18 @@ import net.sf.openrocket.util.ArrayList; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.ChangeSource; import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.ComponentChangeAdapter; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Invalidator; import net.sf.openrocket.util.LineStyle; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.SafetyMutex; import net.sf.openrocket.util.SimpleStack; +import net.sf.openrocket.util.StateChangeListener; import net.sf.openrocket.util.UniqueID; -public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable { +public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable, Visitable { private static final Translator trans = Application.getTranslator(); /* @@ -123,6 +127,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab // Preset component this component is based upon private ComponentPreset presetComponent = null; + // The realistic appearance of this component + private Appearance appearance = null; + /** * Used to invalidate the component after calling {@link #copyFrom(RocketComponent)}. @@ -401,6 +408,38 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab ////////// Common parameter setting/getting ////////// + /** + * Get the realistic appearance of this component. + * null = use the default for this material + * + * @return The component's realistic appearance, or null + */ + public Appearance getAppearance() { + return appearance; + } + + /** + * Set the realistic appearance of this component. + * Use null for default. + * + * @param appearance + */ + public void setAppearance(Appearance appearance) { + this.appearance = appearance; + Decal d = this.appearance.getTexture(); + if (d != null) { + d.getImage().addChangeListener(new StateChangeListener() { + + @Override + public void stateChanged(EventObject e) { + fireComponentChangeEvent(ComponentChangeEvent.TEXTURE_CHANGE); + } + + }); + } + fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); + } + /** * Return the color of the object to use in 2D figures, or null * to use the default color. @@ -1489,9 +1528,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * @throws IllegalStateException - if the root component is not a Rocket */ @Override - public void addChangeListener(EventListener l) { - checkState(); - getRocket().addChangeListener(l); + public final void addChangeListener(StateChangeListener l) { + addComponentChangeListener(new ComponentChangeAdapter(l)); } /** @@ -1503,10 +1541,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * @param l Listener to remove */ @Override - public void removeChangeListener(EventListener l) { - if (this.parent != null) { - getRoot().removeChangeListener(l); - } + public final void removeChangeListener(StateChangeListener l) { + removeComponentChangeListener(new ComponentChangeAdapter(l)); } @@ -1688,7 +1724,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return id.hashCode(); } - + ///////////// Visitor pattern implementation + @Override + public void accept(RocketComponentVisitor visitor) { + visitor.visit(this); + } //////////// Helper methods for subclasses diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponentVisitor.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponentVisitor.java new file mode 100644 index 000000000..0a28982e1 --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponentVisitor.java @@ -0,0 +1,5 @@ +package net.sf.openrocket.rocketcomponent; + +public interface RocketComponentVisitor extends Visitor { + +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/Stage.java b/core/src/net/sf/openrocket/rocketcomponent/Stage.java index 6ea9c2979..a3885117b 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Stage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Stage.java @@ -1,102 +1,18 @@ package net.sf.openrocket.rocketcomponent; import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.MathUtil; -public class Stage extends ComponentAssembly { +public class Stage extends ComponentAssembly implements FlightConfigurableComponent { - private static final Translator trans = Application.getTranslator(); + static final Translator trans = Application.getTranslator(); + + private FlightConfigurationImpl separationConfigurations; - public static enum SeparationEvent { - //// Upper stage motor ignition - UPPER_IGNITION("Stage.SeparationEvent.UPPER_IGNITION") { - @Override - public boolean isSeparationEvent(FlightEvent e, Stage stage) { - if (e.getType() != FlightEvent.Type.IGNITION) - return false; - - int ignition = e.getSource().getStageNumber(); - int mount = stage.getStageNumber(); - return (mount == ignition + 1); - } - }, - //// Current stage motor ignition - IGNITION("Stage.SeparationEvent.IGNITION") { - @Override - public boolean isSeparationEvent(FlightEvent e, Stage stage) { - if (e.getType() != FlightEvent.Type.IGNITION) - return false; - - int ignition = e.getSource().getStageNumber(); - int mount = stage.getStageNumber(); - return (mount == ignition); - } - }, - //// Current stage motor burnout - BURNOUT("Stage.SeparationEvent.BURNOUT") { - @Override - public boolean isSeparationEvent(FlightEvent e, Stage stage) { - if (e.getType() != FlightEvent.Type.BURNOUT) - return false; - - int ignition = e.getSource().getStageNumber(); - int mount = stage.getStageNumber(); - return (mount == ignition); - } - }, - //// Current stage ejection charge - EJECTION("Stage.SeparationEvent.EJECTION") { - @Override - public boolean isSeparationEvent(FlightEvent e, Stage stage) { - if (e.getType() != FlightEvent.Type.EJECTION_CHARGE) - return false; - - int ignition = e.getSource().getStageNumber(); - int mount = stage.getStageNumber(); - return (mount == ignition); - } - }, - //// Launch - LAUNCH("Stage.SeparationEvent.LAUNCH") { - @Override - public boolean isSeparationEvent(FlightEvent e, Stage stage) { - return e.getType() == FlightEvent.Type.LAUNCH; - } - }, - //// Never - NEVER("Stage.SeparationEvent.NEVER") { - @Override - public boolean isSeparationEvent(FlightEvent e, Stage stage) { - return false; - } - }, - ; - - - private final String description; - - SeparationEvent(String description) { - this.description = description; - } - - /** - * Test whether a specific event is a stage separation event. - */ - public abstract boolean isSeparationEvent(FlightEvent e, Stage stage); - - @Override - public String toString() { - return trans.get(description); - } - }; - - - private SeparationEvent separationEvent = SeparationEvent.UPPER_IGNITION; - private double separationDelay = 0; - + public Stage() { + this.separationConfigurations = new FlightConfigurationImpl(this, ComponentChangeEvent.EVENT_CHANGE, new StageSeparationConfiguration()); + } @Override public String getComponentName() { @@ -105,31 +21,11 @@ public class Stage extends ComponentAssembly { } - public SeparationEvent getSeparationEvent() { - return separationEvent; + public FlightConfiguration getStageSeparationConfiguration() { + return separationConfigurations; } - public void setSeparationEvent(SeparationEvent separationEvent) { - if (separationEvent == this.separationEvent) - return; - this.separationEvent = separationEvent; - fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); - } - - - public double getSeparationDelay() { - return separationDelay; - } - - - public void setSeparationDelay(double separationDelay) { - if (MathUtil.equals(separationDelay, this.separationDelay)) - return; - this.separationDelay = separationDelay; - fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); - } - @Override @@ -149,4 +45,20 @@ public class Stage extends ComponentAssembly { public boolean isCompatible(Class type) { return BodyComponent.class.isAssignableFrom(type); } + + + + @Override + public void cloneFlightConfiguration(String oldConfigId, String newConfigId) { + separationConfigurations.cloneFlightConfiguration(oldConfigId, newConfigId); + } + + @Override + protected RocketComponent copyWithOriginalID() { + Stage copy = (Stage) super.copyWithOriginalID(); + copy.separationConfigurations = new FlightConfigurationImpl(separationConfigurations, + copy, ComponentChangeEvent.EVENT_CHANGE); + return copy; + } + } diff --git a/core/src/net/sf/openrocket/rocketcomponent/StageSeparationConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/StageSeparationConfiguration.java new file mode 100644 index 000000000..a7a2a13d9 --- /dev/null +++ b/core/src/net/sf/openrocket/rocketcomponent/StageSeparationConfiguration.java @@ -0,0 +1,169 @@ +package net.sf.openrocket.rocketcomponent; + +import java.util.EventObject; +import java.util.List; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.ArrayList; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.StateChangeListener; + +public class StageSeparationConfiguration implements FlightConfigurableParameter { + + public static enum SeparationEvent { + //// Upper stage motor ignition + UPPER_IGNITION(trans.get("Stage.SeparationEvent.UPPER_IGNITION")) { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + if (e.getType() != FlightEvent.Type.IGNITION) + return false; + + int ignition = e.getSource().getStageNumber(); + int mount = stage.getStageNumber(); + return (mount == ignition + 1); + } + }, + //// Current stage motor ignition + IGNITION(trans.get("Stage.SeparationEvent.IGNITION")) { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + if (e.getType() != FlightEvent.Type.IGNITION) + return false; + + int ignition = e.getSource().getStageNumber(); + int mount = stage.getStageNumber(); + return (mount == ignition); + } + }, + //// Current stage motor burnout + BURNOUT(trans.get("Stage.SeparationEvent.BURNOUT")) { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + if (e.getType() != FlightEvent.Type.BURNOUT) + return false; + + int ignition = e.getSource().getStageNumber(); + int mount = stage.getStageNumber(); + return (mount == ignition); + } + }, + //// Current stage ejection charge + EJECTION(trans.get("Stage.SeparationEvent.EJECTION")) { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + if (e.getType() != FlightEvent.Type.EJECTION_CHARGE) + return false; + + int ignition = e.getSource().getStageNumber(); + int mount = stage.getStageNumber(); + return (mount == ignition); + } + }, + //// Launch + LAUNCH(trans.get("Stage.SeparationEvent.LAUNCH")) { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + return e.getType() == FlightEvent.Type.LAUNCH; + } + }, + //// Never + NEVER(trans.get("Stage.SeparationEvent.NEVER")) { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + return false; + } + }, + ; + + + private final String description; + + SeparationEvent(String description) { + this.description = description; + } + + /** + * Test whether a specific event is a stage separation event. + */ + public abstract boolean isSeparationEvent(FlightEvent e, Stage stage); + + @Override + public String toString() { + return description; + } + } + + + private static final Translator trans = Application.getTranslator(); + + private final List listeners = new ArrayList(); + + private SeparationEvent separationEvent = SeparationEvent.UPPER_IGNITION; + private double separationDelay = 0; + + + public SeparationEvent getSeparationEvent() { + return separationEvent; + } + + public void setSeparationEvent(SeparationEvent separationEvent) { + if (separationEvent == null) { + throw new NullPointerException("separationEvent is null"); + } + if (this.separationEvent == separationEvent) { + return; + } + this.separationEvent = separationEvent; + fireChangeEvent(); + } + + public double getSeparationDelay() { + return separationDelay; + } + + public void setSeparationDelay(double separationDelay) { + if (MathUtil.equals(this.separationDelay, separationDelay)) { + return; + } + this.separationDelay = separationDelay; + fireChangeEvent(); + } + + @Override + public String toString() { + if (separationDelay > 0) { + return separationEvent.toString() + " + " + separationDelay + "s"; + } else { + return separationEvent.toString(); + } + } + + @Override + public StageSeparationConfiguration clone() { + StageSeparationConfiguration clone = new StageSeparationConfiguration(); + clone.separationEvent = this.separationEvent; + clone.separationDelay = this.separationDelay; + return clone; + } + + @Override + public void addChangeListener(StateChangeListener listener) { + listeners.add(listener); + } + + @Override + public void removeChangeListener(StateChangeListener listener) { + listeners.remove(listener); + } + + private void fireChangeEvent() { + EventObject event = new EventObject(this); + Object[] list = listeners.toArray(); + for (Object l : list) { + ((StateChangeListener) l).stateChanged(event); + } + } + +} diff --git a/core/src/net/sf/openrocket/rocketvisitors/BredthFirstRecusiveVisitor.java b/core/src/net/sf/openrocket/rocketvisitors/BredthFirstRecusiveVisitor.java new file mode 100644 index 000000000..ad9f9681d --- /dev/null +++ b/core/src/net/sf/openrocket/rocketvisitors/BredthFirstRecusiveVisitor.java @@ -0,0 +1,26 @@ +package net.sf.openrocket.rocketvisitors; + +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.RocketComponentVisitor; + +public abstract class BredthFirstRecusiveVisitor implements RocketComponentVisitor { + + @Override + public final void visit(RocketComponent visitable) { + + this.doAction(visitable); + + for ( RocketComponent child: visitable.getChildren() ) { + this.doAction(child); + } + + for ( RocketComponent child: visitable.getChildren() ) { + this.visit(child); + } + + + } + + protected abstract void doAction( RocketComponent visitable ); + +} diff --git a/core/src/net/sf/openrocket/rocketvisitors/CopyFlightConfigurationVisitor.java b/core/src/net/sf/openrocket/rocketvisitors/CopyFlightConfigurationVisitor.java new file mode 100644 index 000000000..2bc35e084 --- /dev/null +++ b/core/src/net/sf/openrocket/rocketvisitors/CopyFlightConfigurationVisitor.java @@ -0,0 +1,26 @@ +package net.sf.openrocket.rocketvisitors; + +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.FlightConfigurableComponent; + +public class CopyFlightConfigurationVisitor extends DepthFirstRecusiveVisitor { + + private final String oldConfigId; + private final String newConfigId; + + public CopyFlightConfigurationVisitor(String oldConfigId, String newConfigId) { + super(); + this.oldConfigId = oldConfigId; + this.newConfigId = newConfigId; + } + + @Override + public void doAction(RocketComponent visitable) { + + if ( visitable instanceof FlightConfigurableComponent ) { + ((FlightConfigurableComponent)visitable).cloneFlightConfiguration(oldConfigId, newConfigId); + } + } + + +} diff --git a/core/src/net/sf/openrocket/rocketvisitors/DepthFirstRecusiveVisitor.java b/core/src/net/sf/openrocket/rocketvisitors/DepthFirstRecusiveVisitor.java new file mode 100644 index 000000000..f08d65080 --- /dev/null +++ b/core/src/net/sf/openrocket/rocketvisitors/DepthFirstRecusiveVisitor.java @@ -0,0 +1,21 @@ +package net.sf.openrocket.rocketvisitors; + +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.RocketComponentVisitor; + +public abstract class DepthFirstRecusiveVisitor implements RocketComponentVisitor { + + @Override + public final void visit(RocketComponent visitable) { + + this.doAction(visitable); + + for ( RocketComponent child: visitable.getChildren() ) { + this.visit(child); + } + + } + + protected abstract void doAction( RocketComponent visitable ); + +} diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index 009d3c0a2..f38541be3 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -1,10 +1,7 @@ package net.sf.openrocket.simulation; -import java.util.HashSet; import java.util.Iterator; -import java.util.Set; -import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.LogHelper; @@ -13,11 +10,14 @@ import net.sf.openrocket.motor.MotorId; import net.sf.openrocket.motor.MotorInstance; import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; +import net.sf.openrocket.rocketcomponent.MotorConfiguration; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.RecoveryDevice; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; import net.sf.openrocket.simulation.exception.MotorIgnitionException; import net.sf.openrocket.simulation.exception.SimulationException; import net.sf.openrocket.simulation.exception.SimulationLaunchException; @@ -27,57 +27,101 @@ import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.Pair; -import net.sf.openrocket.util.Quaternion; +import net.sf.openrocket.util.SimpleStack; public class BasicEventSimulationEngine implements SimulationEngine { - + private static final Translator trans = Application.getTranslator(); private static final LogHelper log = Application.getLogger(); - + // TODO: MEDIUM: Allow selecting steppers private SimulationStepper flightStepper = new RK4SimulationStepper(); private SimulationStepper landingStepper = new BasicLandingStepper(); - + private SimulationStepper tumbleStepper = new BasicTumbleStepper(); + + // Constant holding 30 degress in radians. This is the AOA condition + // necessary to transistion to tumbling. + private final static double AOA_TUMBLE_CONDITION = Math.PI / 3.0; + private SimulationStepper currentStepper; - + private SimulationStatus status; - - + + private String flightConfigurationId; + + private SimpleStack stages = new SimpleStack(); + + @Override public FlightData simulate(SimulationConditions simulationConditions) throws SimulationException { - Set motorBurntOut = new HashSet(); - + // Set up flight data FlightData flightData = new FlightData(); - + // Set up rocket configuration Configuration configuration = setupConfiguration(simulationConditions); + flightConfigurationId = configuration.getFlightConfigurationID(); MotorInstanceConfiguration motorConfiguration = setupMotorConfiguration(configuration); if (motorConfiguration.getMotorIDs().isEmpty()) { throw new MotorIgnitionException("No motors defined in the simulation."); } - + + status = new SimulationStatus(configuration, motorConfiguration, simulationConditions); + status.getEventQueue().add(new FlightEvent(FlightEvent.Type.LAUNCH, 0, simulationConditions.getRocket())); + { + // main sustainer stage + RocketComponent sustainer = configuration.getRocket().getChild(0); + status.setFlightData(new FlightDataBranch(sustainer.getName(), FlightDataType.TYPE_TIME)); + } + stages.add(status); + + SimulationListenerHelper.fireStartSimulation(status); + + while (true) { + if (stages.size() == 0) { + break; + } + SimulationStatus stageStatus = stages.pop(); + if (stageStatus == null) { + break; + } + status = stageStatus; + FlightDataBranch dataBranch = simulateLoop(); + flightData.addBranch(dataBranch); + flightData.getWarningSet().addAll(status.getWarnings()); + } + + SimulationListenerHelper.fireEndSimulation(status, null); + + configuration.release(); + + if (!flightData.getWarningSet().isEmpty()) { + log.info("Warnings at the end of simulation: " + flightData.getWarningSet()); + } + + return flightData; + } + + private FlightDataBranch simulateLoop() throws SimulationException { + // Initialize the simulation currentStepper = flightStepper; - status = initialStatus(configuration, motorConfiguration, simulationConditions, flightData); status = currentStepper.initialize(status); - - - SimulationListenerHelper.fireStartSimulation(status); + // Get originating position (in case listener has modified launch position) Coordinate origin = status.getRocketPosition(); Coordinate originVelocity = status.getRocketVelocity(); - + try { double maxAlt = Double.NEGATIVE_INFINITY; - + // Start the simulation while (handleEvents()) { - + // Take the step double oldAlt = status.getRocketPosition().z; - + if (SimulationListenerHelper.firePreStep(status)) { // Step at most to the next event double maxStepTime = Double.MAX_VALUE; @@ -89,27 +133,27 @@ public class BasicEventSimulationEngine implements SimulationEngine { currentStepper.step(status, maxStepTime); } SimulationListenerHelper.firePostStep(status); - - + + // Check for NaN values in the simulation status checkNaN(); - + // Add altitude event addEvent(new FlightEvent(FlightEvent.Type.ALTITUDE, status.getSimulationTime(), status.getConfiguration().getRocket(), new Pair(oldAlt, status.getRocketPosition().z))); - + if (status.getRocketPosition().z > maxAlt) { maxAlt = status.getRocketPosition().z; } - - + + // Position relative to start location Coordinate relativePosition = status.getRocketPosition().sub(origin); - + // Add appropriate events if (!status.isLiftoff()) { - + // Avoid sinking into ground before liftoff if (relativePosition.z < 0) { status.setRocketPosition(origin); @@ -119,138 +163,69 @@ public class BasicEventSimulationEngine implements SimulationEngine { if (relativePosition.z > 0.02) { addEvent(new FlightEvent(FlightEvent.Type.LIFTOFF, status.getSimulationTime())); } - + } else { - + // Check ground hit after liftoff if (status.getRocketPosition().z < 0) { status.setRocketPosition(status.getRocketPosition().setZ(0)); addEvent(new FlightEvent(FlightEvent.Type.GROUND_HIT, status.getSimulationTime())); addEvent(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime())); } - + } - + // Check for launch guide clearance if (!status.isLaunchRodCleared() && relativePosition.length() > status.getSimulationConditions().getLaunchRodLength()) { addEvent(new FlightEvent(FlightEvent.Type.LAUNCHROD, status.getSimulationTime(), null)); } - - + + // Check for apogee if (!status.isApogeeReached() && status.getRocketPosition().z < maxAlt - 0.01) { addEvent(new FlightEvent(FlightEvent.Type.APOGEE, status.getSimulationTime(), status.getConfiguration().getRocket())); } - - + + // Check for burnt out motors for (MotorId motorId : status.getMotorConfiguration().getMotorIDs()) { MotorInstance motor = status.getMotorConfiguration().getMotorInstance(motorId); - if (!motor.isActive() && motorBurntOut.add(motorId)) { + if (!motor.isActive() && status.addBurntOutMotor(motorId)) { addEvent(new FlightEvent(FlightEvent.Type.BURNOUT, status.getSimulationTime(), (RocketComponent) status.getMotorConfiguration().getMotorMount(motorId), motorId)); } } - + + // Check for Tumbling + // Conditions for transision are: + // apogee reached + // and is not already tumbling + // and not stable (cg > cp) + // and aoa > 30 + + if (status.isApogeeReached() && !status.isTumbling()) { + double cp = status.getFlightData().getLast(FlightDataType.TYPE_CP_LOCATION); + double cg = status.getFlightData().getLast(FlightDataType.TYPE_CG_LOCATION); + double aoa = status.getFlightData().getLast(FlightDataType.TYPE_AOA); + if (cg > cp && aoa > AOA_TUMBLE_CONDITION) { + addEvent(new FlightEvent(FlightEvent.Type.TUMBLE, status.getSimulationTime())); + status.setTumbling(true); + } + + } + } - + } catch (SimulationException e) { SimulationListenerHelper.fireEndSimulation(status, e); throw e; } - - SimulationListenerHelper.fireEndSimulation(status, null); - - flightData.addBranch(status.getFlightData()); - - if (!flightData.getWarningSet().isEmpty()) { - log.info("Warnings at the end of simulation: " + flightData.getWarningSet()); - } - - configuration.release(); - // TODO: HIGH: Simulate branches - return flightData; + + return status.getFlightData(); } - - - - private SimulationStatus initialStatus(Configuration configuration, - MotorInstanceConfiguration motorConfiguration, - SimulationConditions simulationConditions, FlightData flightData) { - - SimulationStatus init = new SimulationStatus(); - init.setSimulationConditions(simulationConditions); - init.setConfiguration(configuration); - init.setMotorConfiguration(motorConfiguration); - - init.setSimulationTime(0); - init.setPreviousTimeStep(simulationConditions.getTimeStep()); - init.setRocketPosition(Coordinate.NUL); - init.setRocketVelocity(Coordinate.NUL); - init.setRocketWorldPosition(simulationConditions.getLaunchSite()); - - // Initialize to roll angle with least stability w.r.t. the wind - Quaternion o; - FlightConditions cond = new FlightConditions(configuration); - simulationConditions.getAerodynamicCalculator().getWorstCP(configuration, cond, null); - double angle = -cond.getTheta() - simulationConditions.getLaunchRodDirection(); - o = Quaternion.rotation(new Coordinate(0, 0, angle)); - - // Launch rod angle and direction - o = o.multiplyLeft(Quaternion.rotation(new Coordinate(0, simulationConditions.getLaunchRodAngle(), 0))); - o = o.multiplyLeft(Quaternion.rotation(new Coordinate(0, 0, simulationConditions.getLaunchRodDirection()))); - - init.setRocketOrientationQuaternion(o); - init.setRocketRotationVelocity(Coordinate.NUL); - - - /* - * Calculate the effective launch rod length taking into account launch lugs. - * If no lugs are found, assume a tower launcher of full length. - */ - double length = simulationConditions.getLaunchRodLength(); - double lugPosition = Double.NaN; - for (RocketComponent c : configuration) { - if (c instanceof LaunchLug) { - double pos = c.toAbsolute(new Coordinate(c.getLength()))[0].x; - if (Double.isNaN(lugPosition) || pos > lugPosition) { - lugPosition = pos; - } - } - } - if (!Double.isNaN(lugPosition)) { - double maxX = 0; - for (Coordinate c : configuration.getBounds()) { - if (c.x > maxX) - maxX = c.x; - } - if (maxX >= lugPosition) { - length = Math.max(0, length - (maxX - lugPosition)); - } - } - init.setEffectiveLaunchRodLength(length); - - - - init.setSimulationStartWallTime(System.nanoTime()); - - init.setMotorIgnited(false); - init.setLiftoff(false); - init.setLaunchRodCleared(false); - init.setApogeeReached(false); - - init.getEventQueue().add(new FlightEvent(FlightEvent.Type.LAUNCH, 0, simulationConditions.getRocket())); - - init.setFlightData(new FlightDataBranch("MAIN", FlightDataType.TYPE_TIME)); - init.setWarnings(flightData.getWarningSet()); - - return init; - } - - - + /** * Create a rocket configuration from the launch conditions. * @@ -260,13 +235,13 @@ public class BasicEventSimulationEngine implements SimulationEngine { private Configuration setupConfiguration(SimulationConditions simulation) { Configuration configuration = new Configuration(simulation.getRocket()); configuration.setAllStages(); - configuration.setMotorConfigurationID(simulation.getMotorConfigurationID()); - + configuration.setFlightConfigurationID(simulation.getMotorConfigurationID()); + return configuration; } - - - + + + /** * Create a new motor instance configuration for the rocket configuration. * @@ -275,26 +250,29 @@ public class BasicEventSimulationEngine implements SimulationEngine { */ private MotorInstanceConfiguration setupMotorConfiguration(Configuration configuration) { MotorInstanceConfiguration motors = new MotorInstanceConfiguration(); - final String motorId = configuration.getMotorConfigurationID(); - + final String flightConfigId = configuration.getFlightConfigurationID(); + Iterator iterator = configuration.motorIterator(); while (iterator.hasNext()) { MotorMount mount = iterator.next(); RocketComponent component = (RocketComponent) mount; - Motor motor = mount.getMotor(motorId); - + MotorConfiguration motorConfig = mount.getMotorConfiguration().get(flightConfigId); + IgnitionConfiguration ignitionConfig = mount.getIgnitionConfiguration().get(flightConfigId); + Motor motor = motorConfig.getMotor(); + if (motor != null) { - Coordinate[] positions = component.toAbsolute(mount.getMotorPosition(motorId)); + Coordinate[] positions = component.toAbsolute(mount.getMotorPosition(flightConfigId)); for (int i = 0; i < positions.length; i++) { Coordinate position = positions[i]; MotorId id = new MotorId(component.getID(), i + 1); - motors.addMotor(id, motor.getInstance(), mount, position); + motors.addMotor(id, motor.getInstance(), motorConfig.getEjectionDelay(), mount, + ignitionConfig.getIgnitionEvent(), ignitionConfig.getIgnitionDelay(), position); } } } return motors; } - + /** * Handles events occurring during the flight from the event queue. * Each event that has occurred before or at the current simulation time is @@ -303,24 +281,26 @@ public class BasicEventSimulationEngine implements SimulationEngine { private boolean handleEvents() throws SimulationException { boolean ret = true; FlightEvent event; - + + log.verbose("HandleEvents: current branch = " + status.getFlightData().getBranchName()); + log.verbose("EventQueue = " + status.getEventQueue().toString()); for (event = nextEvent(); event != null; event = nextEvent()) { - + // Ignore events for components that are no longer attached to the rocket if (event.getSource() != null && event.getSource().getParent() != null && !status.getConfiguration().isStageActive(event.getSource().getStageNumber())) { continue; } - + // Call simulation listeners, allow aborting event handling if (!SimulationListenerHelper.fireHandleFlightEvent(status, event)) { continue; } - + if (event.getType() != FlightEvent.Type.ALTITUDE) { log.verbose("BasicEventSimulationEngine: Handling event " + event); } - + if (event.getType() == FlightEvent.Type.IGNITION) { MotorMount mount = (MotorMount) event.getSource(); MotorId motorId = (MotorId) event.getData(); @@ -329,101 +309,101 @@ public class BasicEventSimulationEngine implements SimulationEngine { continue; } } - + if (event.getType() == FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT) { RecoveryDevice device = (RecoveryDevice) event.getSource(); if (!SimulationListenerHelper.fireRecoveryDeviceDeployment(status, device)) { continue; } } - - - + + + // Check for motor ignition events, add ignition events to queue for (MotorId id : status.getMotorConfiguration().getMotorIDs()) { + IgnitionConfiguration.IgnitionEvent ignitionEvent = status.getMotorConfiguration().getMotorIgnitionEvent(id); MotorMount mount = status.getMotorConfiguration().getMotorMount(id); RocketComponent component = (RocketComponent) mount; - - if (mount.getIgnitionEvent().isActivationEvent(event, component)) { + + if (ignitionEvent.isActivationEvent(event, component)) { + double ignitionDelay = status.getMotorConfiguration().getMotorIgnitionDelay(id); addEvent(new FlightEvent(FlightEvent.Type.IGNITION, - status.getSimulationTime() + mount.getIgnitionDelay(), + status.getSimulationTime() + ignitionDelay, component, id)); } } - - + + // Check for stage separation event for (int stageNo : status.getConfiguration().getActiveStages()) { if (stageNo == 0) continue; - + Stage stage = (Stage) status.getConfiguration().getRocket().getChild(stageNo); - if (stage.getSeparationEvent().isSeparationEvent(event, stage)) { + StageSeparationConfiguration separationConfig = stage.getStageSeparationConfiguration().get(flightConfigurationId); + if (separationConfig.getSeparationEvent().isSeparationEvent(event, stage)) { addEvent(new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, - event.getTime() + stage.getSeparationDelay(), stage)); + event.getTime() + separationConfig.getSeparationDelay(), stage)); } } - - + + // Check for recovery device deployment, add events to queue Iterator rci = status.getConfiguration().iterator(); while (rci.hasNext()) { RocketComponent c = rci.next(); if (!(c instanceof RecoveryDevice)) continue; - if (((RecoveryDevice) c).getDeployEvent().isActivationEvent(event, c)) { + DeploymentConfiguration deployConfig = ((RecoveryDevice) c).getDeploymentConfiguration().get(flightConfigurationId); + if (deployConfig.isActivationEvent(event, c)) { // Delay event by at least 1ms to allow stage separation to occur first addEvent(new FlightEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, - event.getTime() + Math.max(0.001, ((RecoveryDevice) c).getDeployDelay()), c)); + event.getTime() + Math.max(0.001, deployConfig.getDeployDelay()), c)); } } - - + + // Handle event switch (event.getType()) { - + case LAUNCH: { status.getFlightData().addEvent(event); break; } - + case IGNITION: { // Ignite the motor - MotorMount mount = (MotorMount) event.getSource(); - @SuppressWarnings("unused") - RocketComponent component = (RocketComponent) mount; MotorId motorId = (MotorId) event.getData(); MotorInstanceConfiguration config = status.getMotorConfiguration(); config.setMotorIgnitionTime(motorId, event.getTime()); status.setMotorIgnited(true); status.getFlightData().addEvent(event); - + break; } - + case LIFTOFF: { // Mark lift-off as occurred status.setLiftoff(true); status.getFlightData().addEvent(event); break; } - + case LAUNCHROD: { // Mark launch rod as cleared status.setLaunchRodCleared(true); status.getFlightData().addEvent(event); break; } - + case BURNOUT: { // If motor burnout occurs without lift-off, abort if (!status.isLiftoff()) { throw new SimulationLaunchException("Motor burnout without liftoff."); } // Add ejection charge event - String id = status.getConfiguration().getMotorConfigurationID(); - MotorMount mount = (MotorMount) event.getSource(); - double delay = mount.getMotorDelay(id); + MotorId motorId = (MotorId) event.getData(); + double delay = status.getMotorConfiguration().getEjectionDelay(motorId); if (delay != Motor.PLUGGED) { addEvent(new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, status.getSimulationTime() + delay, event.getSource(), event.getData())); @@ -431,34 +411,46 @@ public class BasicEventSimulationEngine implements SimulationEngine { status.getFlightData().addEvent(event); break; } - + case EJECTION_CHARGE: { status.getFlightData().addEvent(event); break; } - + case STAGE_SEPARATION: { - // TODO: HIGH: Store lower stages to be simulated later + // Record the event. + status.getFlightData().addEvent(event); + RocketComponent stage = event.getSource(); int n = stage.getStageNumber(); + + // Prepare the booster status for simulation. + SimulationStatus boosterStatus = new SimulationStatus(status); + boosterStatus.setFlightData(new FlightDataBranch(stage.getName(), FlightDataType.TYPE_TIME)); + + stages.add(boosterStatus); + + // Mark the status as having dropped the booster status.getConfiguration().setToStage(n - 1); - status.getFlightData().addEvent(event); + + // Mark the booster status as only having the booster. + boosterStatus.getConfiguration().setOnlyStage(n); break; } - + case APOGEE: // Mark apogee as reached status.setApogeeReached(true); status.getFlightData().addEvent(event); break; - + case RECOVERY_DEVICE_DEPLOYMENT: RocketComponent c = event.getSource(); int n = c.getStageNumber(); // Ignore event if stage not active if (status.getConfiguration().isStageActive(n)) { // TODO: HIGH: Check stage activeness for other events as well? - + // Check whether any motor in the active stages is active anymore for (MotorId motorId : status.getMotorConfiguration().getMotorIDs()) { int stage = ((RocketComponent) status.getMotorConfiguration(). @@ -469,12 +461,12 @@ public class BasicEventSimulationEngine implements SimulationEngine { continue; status.getWarnings().add(Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING); } - + // Check for launch rod if (!status.isLaunchRodCleared()) { status.getWarnings().add(Warning.RECOVERY_LAUNCH_ROD); } - + // Check current velocity if (status.getRocketVelocity().length() > 20) { // TODO: LOW: Custom warning. @@ -483,41 +475,47 @@ public class BasicEventSimulationEngine implements SimulationEngine { + UnitGroup.UNITS_VELOCITY.toStringUnit(status.getRocketVelocity().length()) + ").")); } - + status.setLiftoff(true); status.getDeployedRecoveryDevices().add((RecoveryDevice) c); - + this.currentStepper = this.landingStepper; this.status = currentStepper.initialize(status); - + status.getFlightData().addEvent(event); } break; - + case GROUND_HIT: status.getFlightData().addEvent(event); break; - + case SIMULATION_END: ret = false; status.getFlightData().addEvent(event); break; - + case ALTITUDE: break; + + case TUMBLE: + this.currentStepper = this.tumbleStepper; + this.status = currentStepper.initialize(status); + status.getFlightData().addEvent(event); + break; } - + } - - + + // If no motor has ignited, abort if (!status.isMotorIgnited()) { throw new MotorIgnitionException("No motors ignited."); } - + return ret; } - + /** * Add a flight event to the event queue unless a listener aborts adding it. * @@ -528,9 +526,9 @@ public class BasicEventSimulationEngine implements SimulationEngine { status.getEventQueue().add(event); } } - - - + + + /** * Return the next flight event to handle, or null if no more events should be handled. * This method jumps the simulation time forward in case no motors have been ignited. @@ -543,7 +541,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { FlightEvent event = queue.peek(); if (event == null) return null; - + // Jump to event if no motors have been ignited if (!status.isMotorIgnited() && event.getTime() > status.getSimulationTime()) { status.setSimulationTime(event.getTime()); @@ -554,9 +552,9 @@ public class BasicEventSimulationEngine implements SimulationEngine { return null; } } - - - + + + private void checkNaN() throws SimulationException { double d = 0; boolean b = false; @@ -567,7 +565,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { b |= status.getRocketOrientationQuaternion().isNaN(); b |= status.getRocketRotationVelocity().isNaN(); d += status.getEffectiveLaunchRodLength(); - + if (Double.isNaN(d) || b) { log.error("Simulation resulted in NaN value:" + " simulationTime=" + status.getSimulationTime() + @@ -580,6 +578,6 @@ public class BasicEventSimulationEngine implements SimulationEngine { throw new SimulationException("Simulation resulted in not-a-number (NaN) value, please report a bug."); } } - - + + } diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java new file mode 100644 index 000000000..dfb44a1ad --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java @@ -0,0 +1,74 @@ +package net.sf.openrocket.simulation; + +import java.util.Iterator; + +import net.sf.openrocket.motor.MotorInstanceConfiguration; +import net.sf.openrocket.rocketcomponent.Configuration; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; + +public class BasicTumbleStatus extends SimulationStatus { + + // Magic constants from techdoc.pdf + private final static double cDFin = 1.42; + private final static double cDBt = 0.56; + // Fin efficiency. Index is number of fins. The 0th entry is arbitrary and used to + // offset the indexes so finEff[1] is the coefficient for one fin from the table in techdoc.pdf + private final static double[] finEff = { 0.0, 0.5, 1.0, 1.41, 1.81, 1.73, 1.90, 1.85 }; + + private double drag; + + public BasicTumbleStatus(Configuration configuration, + MotorInstanceConfiguration motorConfiguration, + SimulationConditions simulationConditions) { + super(configuration, motorConfiguration, simulationConditions); + computeTumbleDrag(); + } + + public BasicTumbleStatus(SimulationStatus orig) { + super(orig); + if (orig instanceof BasicTumbleStatus) { + this.drag = ((BasicTumbleStatus) orig).drag; + } + } + + public double getTumbleDrag() { + return drag; + } + + + public void computeTumbleDrag() { + + // Computed based on Sampo's experimentation as documented in the pdf. + + // compute the fin and body tube projected areas + double aFins = 0.0; + double aBt = 0.0; + Rocket r = this.getConfiguration().getRocket(); + Iterator componentIterator = r.iterator(); + while (componentIterator.hasNext()) { + RocketComponent component = componentIterator.next(); + if (!component.isAerodynamic()) { + continue; + } + if (component instanceof FinSet) { + + double finComponent = ((FinSet) component).getFinArea(); + int finCount = ((FinSet) component).getFinCount(); + // check bounds on finCount. + if (finCount >= finEff.length) { + finCount = finEff.length - 1; + } + + aFins += finComponent * finEff[finCount]; + + } else if (component instanceof SymmetricComponent) { + aBt += ((SymmetricComponent) component).getComponentPlanformArea(); + } + } + + drag = (cDFin * aFins + cDBt * aBt); + } +} diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java new file mode 100644 index 000000000..d0ec8da67 --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java @@ -0,0 +1,137 @@ +package net.sf.openrocket.simulation; + +import net.sf.openrocket.models.atmosphere.AtmosphericConditions; +import net.sf.openrocket.simulation.exception.SimulationException; +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.GeodeticComputationStrategy; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.WorldCoordinate; + +public class BasicTumbleStepper extends AbstractSimulationStepper { + + private static final double RECOVERY_TIME_STEP = 0.5; + + @Override + public SimulationStatus initialize(SimulationStatus status) throws SimulationException { + return new BasicTumbleStatus(status); + } + + @Override + public void step(SimulationStatus status, double maxTimeStep) throws SimulationException { + + // Get the atmospheric conditions + AtmosphericConditions atmosphere = modelAtmosphericConditions(status); + + //// Local wind speed and direction + Coordinate windSpeed = modelWindVelocity(status); + Coordinate airSpeed = status.getRocketVelocity().add(windSpeed); + + // Get total CD + double mach = airSpeed.length() / atmosphere.getMachSpeed(); + + double tumbleDrag = ((BasicTumbleStatus)status).getTumbleDrag(); + + // Compute drag force + double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2()); + double dragForce = tumbleDrag * dynP; + MassData massData = calculateMassData(status); + double mass = massData.getCG().weight; + + + // Compute drag acceleration + Coordinate linearAcceleration; + if (airSpeed.length() > 0.001) { + linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass); + } else { + linearAcceleration = Coordinate.NUL; + } + + // Add effect of gravity + double gravity = modelGravity(status); + linearAcceleration = linearAcceleration.sub(0, 0, gravity); + + + // Add coriolis acceleration + Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration( + status.getRocketWorldPosition(), status.getRocketVelocity()); + linearAcceleration = linearAcceleration.add(coriolisAcceleration); + + + + // Select time step + double timeStep = MathUtil.min(0.5 / linearAcceleration.length(), RECOVERY_TIME_STEP); + + // Perform Euler integration + status.setRocketPosition(status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)). + add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2))); + status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep))); + status.setSimulationTime(status.getSimulationTime() + timeStep); + + + // Update the world coordinate + WorldCoordinate w = status.getSimulationConditions().getLaunchSite(); + w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition()); + status.setRocketWorldPosition(w); + + + // Store data + FlightDataBranch data = status.getFlightData(); + boolean extra = status.getSimulationConditions().isCalculateExtras(); + data.addPoint(); + + data.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime()); + data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z); + data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x); + data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y); + if (extra) { + data.setValue(FlightDataType.TYPE_POSITION_XY, + MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y)); + data.setValue(FlightDataType.TYPE_POSITION_DIRECTION, + Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x)); + + data.setValue(FlightDataType.TYPE_VELOCITY_XY, + MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y)); + data.setValue(FlightDataType.TYPE_ACCELERATION_XY, + MathUtil.hypot(linearAcceleration.x, linearAcceleration.y)); + + data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length()); + + double Re = airSpeed.length() * + status.getConfiguration().getLength() / + atmosphere.getKinematicViscosity(); + data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re); + } + + + data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad()); + data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad()); + data.setValue(FlightDataType.TYPE_GRAVITY, gravity); + + if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) { + data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length()); + } + + + data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z); + data.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z); + + data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length()); + data.setValue(FlightDataType.TYPE_MACH_NUMBER, mach); + + data.setValue(FlightDataType.TYPE_MASS, mass); + data.setValue(FlightDataType.TYPE_PROPELLANT_MASS, 0.0); // Is this a reasonable assumption? Probably. + + data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0); + data.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce); + + data.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length()); + data.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature()); + data.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure()); + data.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed()); + + data.setValue(FlightDataType.TYPE_TIME_STEP, timeStep); + data.setValue(FlightDataType.TYPE_COMPUTATION_TIME, + (System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0); + } + +} diff --git a/core/src/net/sf/openrocket/simulation/FlightEvent.java b/core/src/net/sf/openrocket/simulation/FlightEvent.java index 92f125c67..a997f49ee 100644 --- a/core/src/net/sf/openrocket/simulation/FlightEvent.java +++ b/core/src/net/sf/openrocket/simulation/FlightEvent.java @@ -72,7 +72,12 @@ public class FlightEvent implements Comparable { * A change in altitude has occurred. Data is a Pair * which contains the old and new altitudes. */ - ALTITUDE(trans.get("FlightEvent.Type.ALTITUDE")); + ALTITUDE(trans.get("FlightEvent.Type.ALTITUDE")), + + /** + * The rocket begins to tumble. + */ + TUMBLE(trans.get("FlightEvent.Type.TUMBLE")); private final String name; diff --git a/core/src/net/sf/openrocket/simulation/RK4SimulationStatus.java b/core/src/net/sf/openrocket/simulation/RK4SimulationStatus.java index 505ff77ca..db366fbdc 100644 --- a/core/src/net/sf/openrocket/simulation/RK4SimulationStatus.java +++ b/core/src/net/sf/openrocket/simulation/RK4SimulationStatus.java @@ -1,9 +1,11 @@ package net.sf.openrocket.simulation; import net.sf.openrocket.models.atmosphere.AtmosphericConditions; +import net.sf.openrocket.motor.MotorInstanceConfiguration; +import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.util.Coordinate; -public class RK4SimulationStatus extends SimulationStatus { +public class RK4SimulationStatus extends SimulationStatus implements Cloneable { private Coordinate launchRodDirection; private double previousAcceleration = 0; @@ -13,7 +15,22 @@ public class RK4SimulationStatus extends SimulationStatus { private double maxZVelocity = 0; private double startWarningTime = -1; - + public RK4SimulationStatus(Configuration configuration, + MotorInstanceConfiguration motorConfiguration, + SimulationConditions simulationConditions ) { + super(configuration, motorConfiguration, simulationConditions); + } + + public RK4SimulationStatus( SimulationStatus other ) { + super(other); + if ( other instanceof RK4SimulationStatus ) { + this.launchRodDirection = ((RK4SimulationStatus) other).launchRodDirection; + this.previousAcceleration = ((RK4SimulationStatus) other).previousAcceleration; + this.maxZVelocity = ((RK4SimulationStatus) other).maxZVelocity; + this.startWarningTime = ((RK4SimulationStatus) other).startWarningTime; + this.previousAtmosphericConditions = ((RK4SimulationStatus) other).previousAtmosphericConditions; + } + } public void setLaunchRodDirection(Coordinate launchRodDirection) { this.launchRodDirection = launchRodDirection; } @@ -65,7 +82,6 @@ public class RK4SimulationStatus extends SimulationStatus { this.startWarningTime = startWarningTime; } - @Override public RK4SimulationStatus clone() { return (RK4SimulationStatus) super.clone(); diff --git a/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java b/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java index fdc4ba626..2f6fde315 100644 --- a/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java +++ b/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java @@ -66,9 +66,9 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { @Override public RK4SimulationStatus initialize(SimulationStatus original) { - RK4SimulationStatus status = new RK4SimulationStatus(); - - status.copyFrom(original); + RK4SimulationStatus status = new RK4SimulationStatus(original); + // Copy the existing warnings + status.setWarnings( original.getWarnings() ); SimulationConditions sim = original.getSimulationConditions(); diff --git a/core/src/net/sf/openrocket/simulation/SimulationOptions.java b/core/src/net/sf/openrocket/simulation/SimulationOptions.java index ff08766b3..c77ef1fa1 100644 --- a/core/src/net/sf/openrocket/simulation/SimulationOptions.java +++ b/core/src/net/sf/openrocket/simulation/SimulationOptions.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Random; import net.sf.openrocket.aerodynamics.BarrowmanCalculator; +import net.sf.openrocket.formatting.MotorDescriptionSubstitutor; import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.models.atmosphere.AtmosphericModel; import net.sf.openrocket.models.atmosphere.ExtendedISAModel; @@ -14,6 +15,7 @@ import net.sf.openrocket.models.gravity.GravityModel; import net.sf.openrocket.models.gravity.WGSGravityModel; import net.sf.openrocket.models.wind.PinkNoiseWindModel; import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.ChangeSource; import net.sf.openrocket.util.GeodeticComputationStrategy; @@ -38,16 +40,16 @@ public class SimulationOptions implements ChangeSource, Cloneable { */ private static final AtmosphericModel ISA_ATMOSPHERIC_MODEL = new ExtendedISAModel(); - + private final Rocket rocket; private String motorID = null; - + /* * NOTE: When adding/modifying parameters, they must also be added to the * equals and copyFrom methods!! */ - + // TODO: HIGH: Fetch default values from Prefs! private double launchRodLength = 1; @@ -58,11 +60,11 @@ public class SimulationOptions implements ChangeSource, Cloneable { /** Launch rod direction, 0 = upwind, PI = downwind. */ private double launchRodDirection = 0; - + private double windAverage = 2.0; private double windTurbulence = 0.1; - + /* * SimulationOptions maintains the launch site parameters as separate double values, * and converts them into a WorldCoordinate when converting to SimulationConditions. @@ -76,7 +78,7 @@ public class SimulationOptions implements ChangeSource, Cloneable { private double launchTemperature = ExtendedISAModel.STANDARD_TEMPERATURE; private double launchPressure = ExtendedISAModel.STANDARD_PRESSURE; - + private double timeStep = RK4SimulationStepper.RECOMMENDED_TIME_STEP; private double maximumAngle = RK4SimulationStepper.RECOMMENDED_ANGLE_STEP; @@ -84,11 +86,11 @@ public class SimulationOptions implements ChangeSource, Cloneable { private boolean calculateExtras = true; - + private List listeners = new ArrayList(); - + public SimulationOptions(Rocket rocket) { this.rocket = rocket; } @@ -111,7 +113,7 @@ public class SimulationOptions implements ChangeSource, Cloneable { public void setMotorConfigurationID(String id) { if (id != null) id = id.intern(); - if (!rocket.isMotorConfigurationID(id)) + if (!rocket.isFlightConfigurationID(id)) id = null; if (id == motorID) return; @@ -158,7 +160,7 @@ public class SimulationOptions implements ChangeSource, Cloneable { } - + public double getWindSpeedAverage() { return windAverage; } @@ -204,9 +206,9 @@ public class SimulationOptions implements ChangeSource, Cloneable { } - - - + + + public double getLaunchAltitude() { return launchAltitude; } @@ -276,7 +278,7 @@ public class SimulationOptions implements ChangeSource, Cloneable { } - + public void setLaunchTemperature(double launchTemperature) { if (MathUtil.equals(this.launchTemperature, launchTemperature)) return; @@ -285,13 +287,13 @@ public class SimulationOptions implements ChangeSource, Cloneable { } - + public double getLaunchPressure() { return launchPressure; } - + public void setLaunchPressure(double launchPressure) { if (MathUtil.equals(this.launchPressure, launchPressure)) return; @@ -338,13 +340,13 @@ public class SimulationOptions implements ChangeSource, Cloneable { } - + public boolean getCalculateExtras() { return calculateExtras; } - + public void setCalculateExtras(boolean calculateExtras) { if (this.calculateExtras == calculateExtras) return; @@ -353,7 +355,7 @@ public class SimulationOptions implements ChangeSource, Cloneable { } - + public int getRandomSeed() { return randomSeed; } @@ -380,7 +382,7 @@ public class SimulationOptions implements ChangeSource, Cloneable { } - + @Override public SimulationOptions clone() { try { @@ -402,18 +404,26 @@ public class SimulationOptions implements ChangeSource, Cloneable { } else { if (src.rocket.hasMotors(src.motorID)) { - // Try to find a matching motor ID - String motorDesc = src.rocket.getMotorConfigurationDescription(src.motorID); - String matchID = null; - - for (String id : this.rocket.getMotorConfigurationIDs()) { - if (motorDesc.equals(this.rocket.getMotorConfigurationDescription(id))) { - matchID = id; - break; + // First check for exact match: + if (this.rocket.isFlightConfigurationID(src.motorID)) { + this.motorID = src.motorID; + } else { + // Try to find a closely matching motor ID + MotorDescriptionSubstitutor formatter = Application.getInjector().getInstance(MotorDescriptionSubstitutor.class); + + String motorDesc = formatter.getMotorConfigurationDescription(src.rocket, src.motorID); + String matchID = null; + + for (String id : this.rocket.getFlightConfigurationIDs()) { + String motorDesc2 = formatter.getMotorConfigurationDescription(this.rocket, id); + if (motorDesc.equals(motorDesc2)) { + matchID = id; + break; + } } + + this.motorID = matchID; } - - this.motorID = matchID; } else { this.motorID = null; } @@ -438,7 +448,7 @@ public class SimulationOptions implements ChangeSource, Cloneable { } - + /** * Compares whether the two simulation conditions are equal. The two are considered * equal if the rocket, motor id and all variables are equal. @@ -476,12 +486,12 @@ public class SimulationOptions implements ChangeSource, Cloneable { } @Override - public void addChangeListener(EventListener listener) { + public void addChangeListener(StateChangeListener listener) { listeners.add(listener); } @Override - public void removeChangeListener(EventListener listener) { + public void removeChangeListener(StateChangeListener listener) { listeners.remove(listener); } @@ -492,8 +502,8 @@ public class SimulationOptions implements ChangeSource, Cloneable { // Copy the list before iterating to prevent concurrent modification exceptions. EventListener[] list = listeners.toArray(new EventListener[0]); for (EventListener l : list) { - if ( l instanceof StateChangeListener ) { - ((StateChangeListener)l).stateChanged(event); + if (l instanceof StateChangeListener) { + ((StateChangeListener) l).stateChanged(event); } } } diff --git a/core/src/net/sf/openrocket/simulation/SimulationStatus.java b/core/src/net/sf/openrocket/simulation/SimulationStatus.java index 9a39cffda..30fce5772 100644 --- a/core/src/net/sf/openrocket/simulation/SimulationStatus.java +++ b/core/src/net/sf/openrocket/simulation/SimulationStatus.java @@ -1,13 +1,18 @@ package net.sf.openrocket.simulation; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; +import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.motor.MotorId; import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.Configuration; +import net.sf.openrocket.rocketcomponent.LaunchLug; import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Monitorable; @@ -20,7 +25,7 @@ import net.sf.openrocket.util.WorldCoordinate; * * @author Sampo Niskanen */ -public class SimulationStatus implements Cloneable, Monitorable { +public class SimulationStatus implements Monitorable { /* * NOTE! All fields must be added to copyFrom() method!! @@ -44,6 +49,9 @@ public class SimulationStatus implements Cloneable, Monitorable { private double effectiveLaunchRodLength; + // Set of burnt out motors + Set motorBurntOut = new HashSet(); + /** Nanosecond time when the simulation was started. */ private long simulationStartWallTime = Long.MIN_VALUE; @@ -61,6 +69,9 @@ public class SimulationStatus implements Cloneable, Monitorable { /** Set to true when apogee has been detected. */ private boolean apogeeReached = false; + /** Set to true to indicate the rocket is tumbling. */ + private boolean tumbling = false; + /** Contains a list of deployed recovery devices. */ private MonitorableSet deployedRecoveryDevices = new MonitorableSet(); @@ -76,6 +87,119 @@ public class SimulationStatus implements Cloneable, Monitorable { private int modID = 0; private int modIDadd = 0; + public SimulationStatus( Configuration configuration, + MotorInstanceConfiguration motorConfiguration, + SimulationConditions simulationConditions ) { + + this.simulationConditions = simulationConditions; + this.configuration = configuration; + this.motorConfiguration = motorConfiguration; + + this.time = 0; + this.previousTimeStep = this.simulationConditions.getTimeStep(); + this.position = Coordinate.NUL; + this.velocity = Coordinate.NUL; + this.worldPosition = this.simulationConditions.getLaunchSite(); + + // Initialize to roll angle with least stability w.r.t. the wind + Quaternion o; + FlightConditions cond = new FlightConditions(this.configuration); + this.simulationConditions.getAerodynamicCalculator().getWorstCP(this.configuration, cond, null); + double angle = -cond.getTheta() - this.simulationConditions.getLaunchRodDirection(); + o = Quaternion.rotation(new Coordinate(0, 0, angle)); + + // Launch rod angle and direction + o = o.multiplyLeft(Quaternion.rotation(new Coordinate(0, this.simulationConditions.getLaunchRodAngle(), 0))); + o = o.multiplyLeft(Quaternion.rotation(new Coordinate(0, 0, this.simulationConditions.getLaunchRodDirection()))); + + this.orientation = o; + this.rotationVelocity = Coordinate.NUL; + + /* + * Calculate the effective launch rod length taking into account launch lugs. + * If no lugs are found, assume a tower launcher of full length. + */ + double length = this.simulationConditions.getLaunchRodLength(); + double lugPosition = Double.NaN; + for (RocketComponent c : this.configuration) { + if (c instanceof LaunchLug) { + double pos = c.toAbsolute(new Coordinate(c.getLength()))[0].x; + if (Double.isNaN(lugPosition) || pos > lugPosition) { + lugPosition = pos; + } + } + } + if (!Double.isNaN(lugPosition)) { + double maxX = 0; + for (Coordinate c : this.configuration.getBounds()) { + if (c.x > maxX) + maxX = c.x; + } + if (maxX >= lugPosition) { + length = Math.max(0, length - (maxX - lugPosition)); + } + } + this.effectiveLaunchRodLength = length; + + this.simulationStartWallTime = System.nanoTime(); + + this.motorIgnited = false; + this.liftoff = false; + this.launchRodCleared = false; + this.apogeeReached = false; + + this.warnings = new WarningSet(); + + } + + /** + * Performs a deep copy of the on SimulationStatus object. + * Most included object are deep-cloned, except for the flight data object (which is shallow copied) + * and the WarningSet (which is initialized to a new WarningSet). + * The intention of this constructor is to be used for conversion from one type + * of SimulationStatus to another, or when simulating multiple stages. + * When used for simulating multiple stages, a new FlightDataBranch object + * needs to be associated with the new object. + * + * @param orig the object from which to copy + */ + public SimulationStatus( SimulationStatus orig ) { + this.simulationConditions = orig.simulationConditions.clone(); + this.configuration = orig.configuration.clone(); + this.motorConfiguration = orig.motorConfiguration.clone(); + // FlightData is not cloned. + this.flightData = orig.flightData; + this.time = orig.time; + this.previousTimeStep = orig.previousTimeStep; + this.position = orig.position; + this.worldPosition = orig.worldPosition; + this.velocity = orig.velocity; + this.orientation = orig.orientation; + this.rotationVelocity = orig.rotationVelocity; + this.effectiveLaunchRodLength = orig.effectiveLaunchRodLength; + this.simulationStartWallTime = orig.simulationStartWallTime; + this.motorIgnited = orig.motorIgnited; + this.liftoff = orig.liftoff; + this.launchRodCleared = orig.launchRodCleared; + this.apogeeReached = orig.apogeeReached; + this.tumbling = orig.tumbling; + this.motorBurntOut = orig.motorBurntOut; + + this.deployedRecoveryDevices.clear(); + this.deployedRecoveryDevices.addAll(orig.deployedRecoveryDevices); + + this.eventQueue.clear(); + this.eventQueue.addAll(orig.eventQueue); + + // WarningSet is not cloned. + this.warnings = new WarningSet(); + + this.extraData.clear(); + this.extraData.putAll(orig.extraData); + + this.modID = orig.modID; + this.modIDadd = orig.modIDadd; + } public void setSimulationTime(double time) { this.time = time; @@ -168,7 +292,9 @@ public class SimulationStatus implements Cloneable, Monitorable { } - + public boolean addBurntOutMotor( MotorId motor ) { + return motorBurntOut.add(motor); + } public Quaternion getRocketOrientationQuaternion() { @@ -258,6 +384,15 @@ public class SimulationStatus implements Cloneable, Monitorable { } + public void setTumbling( boolean tumbling ) { + this.tumbling = tumbling; + this.modID++; + } + + public boolean isTumbling() { + return tumbling; + } + public Set getDeployedRecoveryDevices() { return deployedRecoveryDevices; } @@ -316,7 +451,6 @@ public class SimulationStatus implements Cloneable, Monitorable { return extraData.get(key); } - /** * Returns a copy of this object. The general purpose is that the conditions, * rocket configuration, flight data etc. point to the same objects. However, @@ -324,7 +458,6 @@ public class SimulationStatus implements Cloneable, Monitorable { * to the current orientation of the rocket. The purpose is to allow creating intermediate * copies of this object used during step computation. * - * TODO: HIGH: Deep cloning required for branch saving. */ @Override public SimulationStatus clone() { @@ -337,47 +470,6 @@ public class SimulationStatus implements Cloneable, Monitorable { } - /** - * Copies the data from the provided object to this object. Most included object are - * deep-cloned, except for the flight data object. - * - * @param orig the object from which to copy - */ - public void copyFrom(SimulationStatus orig) { - this.simulationConditions = orig.simulationConditions.clone(); - this.configuration = orig.configuration.clone(); - this.motorConfiguration = orig.motorConfiguration.clone(); - this.flightData = orig.flightData; - this.time = orig.time; - this.previousTimeStep = orig.previousTimeStep; - this.position = orig.position; - this.worldPosition = orig.worldPosition; - this.velocity = orig.velocity; - this.orientation = orig.orientation; - this.rotationVelocity = orig.rotationVelocity; - this.effectiveLaunchRodLength = orig.effectiveLaunchRodLength; - this.simulationStartWallTime = orig.simulationStartWallTime; - this.motorIgnited = orig.motorIgnited; - this.liftoff = orig.liftoff; - this.launchRodCleared = orig.launchRodCleared; - this.apogeeReached = orig.apogeeReached; - - this.deployedRecoveryDevices.clear(); - this.deployedRecoveryDevices.addAll(orig.deployedRecoveryDevices); - - this.eventQueue.clear(); - this.eventQueue.addAll(orig.eventQueue); - - this.warnings = orig.warnings; - - this.extraData.clear(); - this.extraData.putAll(orig.extraData); - - this.modID = orig.modID; - this.modIDadd = orig.modIDadd; - } - - @Override public int getModID() { return (modID + modIDadd + simulationConditions.getModID() + configuration.getModID() + diff --git a/core/src/net/sf/openrocket/startup/Application.java b/core/src/net/sf/openrocket/startup/Application.java index c4b206567..16bf4ed4a 100644 --- a/core/src/net/sf/openrocket/startup/Application.java +++ b/core/src/net/sf/openrocket/startup/Application.java @@ -3,6 +3,7 @@ package net.sf.openrocket.startup; import net.sf.openrocket.database.ComponentPresetDao; import net.sf.openrocket.database.motor.MotorDatabase; import net.sf.openrocket.database.motor.ThrustCurveMotorSetDatabase; +import net.sf.openrocket.gui.watcher.WatchService; import net.sf.openrocket.l10n.ClassBasedTranslator; import net.sf.openrocket.l10n.DebugTranslator; import net.sf.openrocket.l10n.ExceptionSuppressingTranslator; @@ -68,7 +69,9 @@ public final class Application { Application.logger = logger; } - + public static WatchService getWatchService() { + return Application.injector.getInstance(WatchService.class); + } /** * Return the log buffer. diff --git a/core/src/net/sf/openrocket/startup/ApplicationModule.java b/core/src/net/sf/openrocket/startup/ApplicationModule.java index 32d35290f..6e8cb948b 100644 --- a/core/src/net/sf/openrocket/startup/ApplicationModule.java +++ b/core/src/net/sf/openrocket/startup/ApplicationModule.java @@ -1,5 +1,9 @@ package net.sf.openrocket.startup; +import net.sf.openrocket.formatting.RocketDescriptor; +import net.sf.openrocket.formatting.RocketDescriptorImpl; +import net.sf.openrocket.gui.watcher.WatchService; +import net.sf.openrocket.gui.watcher.WatchServiceImpl; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.LogHelper; @@ -12,6 +16,8 @@ public class ApplicationModule extends AbstractModule { bind(LogHelper.class).toInstance(Application.getLogger()); bind(Preferences.class).toInstance(Application.getPreferences()); bind(Translator.class).toInstance(Application.getTranslator()); + bind(WatchService.class).to(WatchServiceImpl.class); + bind(RocketDescriptor.class).to(RocketDescriptorImpl.class); } } diff --git a/core/src/net/sf/openrocket/startup/GuiceStartup.java b/core/src/net/sf/openrocket/startup/GuiceStartup.java index 51ba42f8d..865955b4c 100644 --- a/core/src/net/sf/openrocket/startup/GuiceStartup.java +++ b/core/src/net/sf/openrocket/startup/GuiceStartup.java @@ -1,5 +1,7 @@ package net.sf.openrocket.startup; +import java.io.IOException; +import java.io.OutputStream; import java.io.PrintStream; import java.util.Locale; import java.util.prefs.Preferences; @@ -14,7 +16,6 @@ import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.logging.LogLevel; import net.sf.openrocket.logging.LogLevelBufferLogger; import net.sf.openrocket.logging.PrintStreamLogger; -import net.sf.openrocket.plugin.PluginHelper; import net.sf.openrocket.plugin.PluginModule; import com.google.inject.Guice; @@ -80,6 +81,7 @@ public class GuiceStartup { setPropertyIfNotSet("openrocket.debug.menu", "true"); setPropertyIfNotSet("openrocket.debug.mutexlocation", "true"); setPropertyIfNotSet("openrocket.debug.motordigest", "true"); + setPropertyIfNotSet("jogl.debug", "all"); } } @@ -131,6 +133,40 @@ public class GuiceStartup { str += " (" + LOG_STDOUT_PROPERTY + "=" + System.getProperty(LOG_STDOUT_PROPERTY) + " " + LOG_STDERR_PROPERTY + "=" + System.getProperty(LOG_STDERR_PROPERTY) + ")"; log.info(str); + + + + //Replace System.err with a PrintStream that logs lines to DEBUG, or VBOSE if they are indented. + //If debug info is not being output to the console then the data is both logged and written to + //stderr. + final boolean writeToStderr = !(printer.getOutput(LogLevel.DEBUG) == System.out || printer.getOutput(LogLevel.DEBUG) == System.err); + final PrintStream stdErr = System.err; + System.setErr(new PrintStream(new OutputStream() { + StringBuilder currentLine = new StringBuilder(); + + @Override + public synchronized void write(int b) throws IOException { + if (writeToStderr) { + //Write to real stderr + stdErr.write(b); + } + if (b == '\r' || b == '\n') { + //Line is complete, log it + if (currentLine.toString().trim().length() > 0) { + String s = currentLine.toString(); + if (Character.isWhitespace(s.charAt(0))) { + log.verbose(currentLine.toString()); + } else { + log.debug(currentLine.toString()); + } + } + currentLine = new StringBuilder(); + } else { + //append to the line being built + currentLine.append((char) b); + } + } + })); } private static boolean setLogOutput(PrintStreamLogger logger, PrintStream stream, String level, LogLevel defaultLevel) { @@ -211,7 +247,7 @@ public class GuiceStartup { private static Injector initializeGuice() { Module applicationModule = new ApplicationModule(); - Module pluginModule = new PluginModule(PluginHelper.getPluginJars(), GuiceStartup.class.getClassLoader()); + Module pluginModule = new PluginModule(); return Guice.createInjector(applicationModule, pluginModule); } diff --git a/core/src/net/sf/openrocket/unit/UnitGroup.java b/core/src/net/sf/openrocket/unit/UnitGroup.java index 432398dfb..8c140ae98 100644 --- a/core/src/net/sf/openrocket/unit/UnitGroup.java +++ b/core/src/net/sf/openrocket/unit/UnitGroup.java @@ -1,6 +1,12 @@ package net.sf.openrocket.unit; -import static net.sf.openrocket.util.Chars.*; +import static net.sf.openrocket.util.Chars.CUBED; +import static net.sf.openrocket.util.Chars.DEGREE; +import static net.sf.openrocket.util.Chars.DOT; +import static net.sf.openrocket.util.Chars.MICRO; +import static net.sf.openrocket.util.Chars.PERMILLE; +import static net.sf.openrocket.util.Chars.SQUARED; +import static net.sf.openrocket.util.Chars.ZWSP; import static net.sf.openrocket.util.MathUtil.pow2; import java.util.ArrayList; @@ -86,6 +92,7 @@ public class UnitGroup { static { UNITS_NONE = new UnitGroup(); UNITS_NONE.addUnit(Unit.NOUNIT); + UNITS_NONE.setDefaultUnit(0); UNITS_ENERGY = new UnitGroup(); UNITS_ENERGY.addUnit(new GeneralUnit(1, "J")); @@ -127,9 +134,9 @@ public class UnitGroup { UNITS_LENGTH.setDefaultUnit(1); UNITS_MOTOR_DIMENSIONS = new UnitGroup(); - UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(1, "m")); // just added UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.001, "mm")); UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.01, "cm")); + UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(1, "m")); UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.0254, "in")); UNITS_MOTOR_DIMENSIONS.setDefaultUnit(0); @@ -140,6 +147,7 @@ public class UnitGroup { UNITS_DISTANCE.addUnit(new GeneralUnit(0.9144, "yd")); UNITS_DISTANCE.addUnit(new GeneralUnit(1609.344, "mi")); UNITS_DISTANCE.addUnit(new GeneralUnit(1852, "nmi")); + UNITS_DISTANCE.setDefaultUnit(0); UNITS_ALL_LENGTHS = new UnitGroup(); UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.001, "mm")); @@ -164,15 +172,16 @@ public class UnitGroup { UNITS_STABILITY = new UnitGroup(); - UNITS_STABILITY.addUnit(new GeneralUnit(1, "m")); UNITS_STABILITY.addUnit(new GeneralUnit(0.001, "mm")); UNITS_STABILITY.addUnit(new GeneralUnit(0.01, "cm")); + UNITS_STABILITY.addUnit(new GeneralUnit(1, "m")); UNITS_STABILITY.addUnit(new GeneralUnit(0.0254, "in")); UNITS_STABILITY.addUnit(new CaliberUnit((Rocket) null)); - UNITS_STABILITY.setDefaultUnit(3); + UNITS_STABILITY.setDefaultUnit(4); UNITS_STABILITY_CALIBERS = new UnitGroup(); UNITS_STABILITY_CALIBERS.addUnit(new GeneralUnit(1, "cal")); + UNITS_STABILITY_CALIBERS.setDefaultUnit(0); UNITS_VELOCITY = new UnitGroup(); @@ -180,23 +189,27 @@ public class UnitGroup { UNITS_VELOCITY.addUnit(new GeneralUnit(1 / 3.6, "km/h")); UNITS_VELOCITY.addUnit(new GeneralUnit(0.3048, "ft/s")); UNITS_VELOCITY.addUnit(new GeneralUnit(0.44704, "mph")); + UNITS_VELOCITY.setDefaultUnit(0); UNITS_WINDSPEED = new UnitGroup(); UNITS_WINDSPEED.addUnit(new GeneralUnit(1, "m/s")); UNITS_WINDSPEED.addUnit(new GeneralUnit(1 / 3.6, "km/h")); UNITS_WINDSPEED.addUnit(new GeneralUnit(0.3048, "ft/s")); UNITS_WINDSPEED.addUnit(new GeneralUnit(0.44704, "mph")); + UNITS_WINDSPEED.setDefaultUnit(0); UNITS_ACCELERATION = new UnitGroup(); UNITS_ACCELERATION.addUnit(new GeneralUnit(1, "m/s" + SQUARED)); UNITS_ACCELERATION.addUnit(new GeneralUnit(0.3048, "ft/s" + SQUARED)); UNITS_ACCELERATION.addUnit(new GeneralUnit(9.80665, "G")); + UNITS_ACCELERATION.setDefaultUnit(0); UNITS_MASS = new UnitGroup(); UNITS_MASS.addUnit(new GeneralUnit(0.001, "g")); UNITS_MASS.addUnit(new GeneralUnit(1, "kg")); UNITS_MASS.addUnit(new GeneralUnit(0.0283495231, "oz")); UNITS_MASS.addUnit(new GeneralUnit(0.45359237, "lb")); + UNITS_MASS.setDefaultUnit(0); UNITS_INERTIA = new UnitGroup(); UNITS_INERTIA.addUnit(new GeneralUnit(0.0001, "kg" + DOT + "cm" + SQUARED)); @@ -211,6 +224,7 @@ public class UnitGroup { UNITS_ANGLE.addUnit(new DegreeUnit()); UNITS_ANGLE.addUnit(new FixedPrecisionUnit("rad", 0.01)); UNITS_ANGLE.addUnit(new GeneralUnit(1.0 / 3437.74677078, "arcmin")); + UNITS_ANGLE.setDefaultUnit(0); UNITS_DENSITY_BULK = new UnitGroup(); UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1000, "g/cm" + CUBED)); @@ -218,6 +232,7 @@ public class UnitGroup { UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1, "kg/m" + CUBED)); UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1729.99404, "oz/in" + CUBED)); UNITS_DENSITY_BULK.addUnit(new GeneralUnit(16.0184634, "lb/ft" + CUBED)); + UNITS_DENSITY_BULK.setDefaultUnit(0); UNITS_DENSITY_SURFACE = new UnitGroup(); UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(10, "g/cm" + SQUARED)); @@ -232,15 +247,18 @@ public class UnitGroup { UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.001, "g/m")); UNITS_DENSITY_LINE.addUnit(new GeneralUnit(1, "kg/m")); UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.0930102465, "oz/ft")); + UNITS_DENSITY_LINE.setDefaultUnit(0); UNITS_FORCE = new UnitGroup(); UNITS_FORCE.addUnit(new GeneralUnit(1, "N")); UNITS_FORCE.addUnit(new GeneralUnit(4.44822162, "lbf")); UNITS_FORCE.addUnit(new GeneralUnit(9.80665, "kgf")); + UNITS_FORCE.setDefaultUnit(0); UNITS_IMPULSE = new UnitGroup(); UNITS_IMPULSE.addUnit(new GeneralUnit(1, "Ns")); UNITS_IMPULSE.addUnit(new GeneralUnit(4.44822162, "lbf" + DOT + "s")); + UNITS_IMPULSE.setDefaultUnit(0); UNITS_TIME_STEP = new UnitGroup(); UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("ms", 1, 0.001)); @@ -249,10 +267,12 @@ public class UnitGroup { UNITS_SHORT_TIME = new UnitGroup(); UNITS_SHORT_TIME.addUnit(new GeneralUnit(1, "s")); + UNITS_SHORT_TIME.setDefaultUnit(0); UNITS_FLIGHT_TIME = new UnitGroup(); UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(1, "s")); UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(60, "min")); + UNITS_FLIGHT_TIME.setDefaultUnit(0); UNITS_ROLL = new UnitGroup(); UNITS_ROLL.addUnit(new GeneralUnit(1, "rad/s")); @@ -274,32 +294,29 @@ public class UnitGroup { UNITS_PRESSURE.addUnit(new GeneralUnit(3386.389, "inHg")); UNITS_PRESSURE.addUnit(new GeneralUnit(6894.75729, "psi")); UNITS_PRESSURE.addUnit(new GeneralUnit(1, "Pa")); + UNITS_PRESSURE.setDefaultUnit(0); UNITS_RELATIVE = new UnitGroup(); UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + ZWSP, 0.01, 1.0)); UNITS_RELATIVE.addUnit(new GeneralUnit(0.01, "%")); UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + PERMILLE, 1, 0.001)); - // UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + ZWSP, 0.01, 1.0)); - // UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("%", 1, 0.01)); - // UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + PERMILLE, 1, 0.001)); UNITS_RELATIVE.setDefaultUnit(1); UNITS_ROUGHNESS = new UnitGroup(); - UNITS_ROUGHNESS.addUnit(new GeneralUnit(1, "m")); // just added UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.000001, MICRO + "m")); UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.0000254, "mil")); + UNITS_ROUGHNESS.addUnit(new GeneralUnit(1, "m")); + UNITS_ROUGHNESS.setDefaultUnit(0); UNITS_COEFFICIENT = new UnitGroup(); UNITS_COEFFICIENT.addUnit(new FixedPrecisionUnit("" + ZWSP, 0.01)); // zero-width space + UNITS_COEFFICIENT.setDefaultUnit(0); // This is not used by OpenRocket, and not extensively tested: UNITS_FREQUENCY = new UnitGroup(); - // UNITS_FREQUENCY.addUnit(new GeneralUnit(1, "s")); - // UNITS_FREQUENCY.addUnit(new GeneralUnit(0.001, "ms")); - // UNITS_FREQUENCY.addUnit(new GeneralUnit(0.000001, MICRO + "s")); UNITS_FREQUENCY.addUnit(new FrequencyUnit(.001, "mHz")); UNITS_FREQUENCY.addUnit(new FrequencyUnit(1, "Hz")); UNITS_FREQUENCY.addUnit(new FrequencyUnit(1000, "kHz")); diff --git a/core/src/net/sf/openrocket/util/AbstractChangeSource.java b/core/src/net/sf/openrocket/util/AbstractChangeSource.java index 57664aed3..1995c33ee 100644 --- a/core/src/net/sf/openrocket/util/AbstractChangeSource.java +++ b/core/src/net/sf/openrocket/util/AbstractChangeSource.java @@ -8,41 +8,47 @@ import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.startup.Application; /** - * Abstract implementation of a ChangeSource. + * Abstract implementation of a ChangeSource. Can either be extended + * or used as a helper object. * * @author Sampo Niskanen */ -public abstract class AbstractChangeSource implements ChangeSource { +public class AbstractChangeSource implements ChangeSource { private static final LogHelper log = Application.getLogger(); private final List listeners = new ArrayList(); - private final EventObject event = new EventObject(this); - @Override - public final void addChangeListener(EventListener listener) { + public final void addChangeListener(StateChangeListener listener) { listeners.add(listener); log.verbose(1, "Adding change listeners, listener count is now " + listeners.size()); } @Override - public final void removeChangeListener(EventListener listener) { + public final void removeChangeListener(StateChangeListener listener) { listeners.remove(listener); log.verbose(1, "Removing change listeners, listener count is now " + listeners.size()); } + public void fireChangeEvent(Object source) { + EventObject event = new EventObject(source); + // Copy the list before iterating to prevent concurrent modification exceptions. + EventListener[] list = listeners.toArray(new EventListener[0]); + for (EventListener l : list) { + if (l instanceof StateChangeListener) { + ((StateChangeListener) l).stateChanged(event); + } + } + + } + + /** * Fire a change event to all listeners. */ protected void fireChangeEvent() { - // Copy the list before iterating to prevent concurrent modification exceptions. - EventListener[] list = listeners.toArray(new EventListener[0]); - for (EventListener l : list) { - if ( l instanceof StateChangeListener ) { - ((StateChangeListener)l).stateChanged(event); - } - } + fireChangeEvent(this); } } diff --git a/core/src/net/sf/openrocket/util/ChangeSource.java b/core/src/net/sf/openrocket/util/ChangeSource.java index f6669ef3a..a115a2113 100644 --- a/core/src/net/sf/openrocket/util/ChangeSource.java +++ b/core/src/net/sf/openrocket/util/ChangeSource.java @@ -1,15 +1,15 @@ package net.sf.openrocket.util; -import java.util.EventListener; /** - * An interface defining an object firing ChangeEvents. Why isn't this included in the Java API?? + * An interface defining an object firing ChangeEvents. * * @author Sampo Niskanen */ public interface ChangeSource { - - public void addChangeListener(EventListener listener); - public void removeChangeListener(EventListener listener); + + public void addChangeListener(StateChangeListener listener); + + public void removeChangeListener(StateChangeListener listener); } diff --git a/core/src/net/sf/openrocket/util/Color.java b/core/src/net/sf/openrocket/util/Color.java index 5f56a1739..bfc833630 100644 --- a/core/src/net/sf/openrocket/util/Color.java +++ b/core/src/net/sf/openrocket/util/Color.java @@ -54,5 +54,10 @@ public class Color { public void setAlpha(int alpha) { this.alpha = alpha; } + + @Override + public String toString() { + return "Color [r=" + red + ", g=" + green + ", b=" + blue + ", a=" + alpha + "]"; + } } diff --git a/core/src/net/sf/openrocket/util/ComponentChangeAdapter.java b/core/src/net/sf/openrocket/util/ComponentChangeAdapter.java new file mode 100644 index 000000000..ccc602121 --- /dev/null +++ b/core/src/net/sf/openrocket/util/ComponentChangeAdapter.java @@ -0,0 +1,42 @@ +package net.sf.openrocket.util; + +import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; +import net.sf.openrocket.rocketcomponent.ComponentChangeListener; + +/** + * An adapter class which allows a StateChangeListener to act as + * a ComponentChangeListener. + */ +public class ComponentChangeAdapter implements ComponentChangeListener { + + private final StateChangeListener listener; + + public ComponentChangeAdapter(StateChangeListener listener) { + this.listener = listener; + } + + @Override + public void componentChanged(ComponentChangeEvent e) { + listener.stateChanged(e); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ComponentChangeAdapter other = (ComponentChangeAdapter) obj; + return this.listener.equals(other.listener); + } + + + @Override + public int hashCode() { + return listener.hashCode(); + } + + +} diff --git a/core/src/net/sf/openrocket/util/Coordinate.java b/core/src/net/sf/openrocket/util/Coordinate.java index f598c52a5..b42b44614 100644 --- a/core/src/net/sf/openrocket/util/Coordinate.java +++ b/core/src/net/sf/openrocket/util/Coordinate.java @@ -14,7 +14,7 @@ import net.sf.openrocket.startup.Application; */ public final class Coordinate implements Cloneable, Serializable { private static final LogHelper log = Application.getLogger(); - + // Defined for backwards compatibility after adding clone(). static final long serialVersionUID = 585574649794259293L; @@ -58,8 +58,8 @@ public final class Coordinate implements Cloneable, Serializable { //////// End debug section - - + + public static final Coordinate NUL = new Coordinate(0, 0, 0, 0); public static final Coordinate NaN = new Coordinate(Double.NaN, Double.NaN, Double.NaN, Double.NaN); @@ -67,7 +67,7 @@ public final class Coordinate implements Cloneable, Serializable { public final double x, y, z; public final double weight; - + private double length = -1; /* Cached when calculated */ @@ -202,6 +202,20 @@ public final class Coordinate implements Cloneable, Serializable { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } + /** + * Cross product of two Coordinates taken as vectors + */ + public Coordinate cross(Coordinate other) { + return cross(this, other); + } + + /** + * Cross product of two Coordinates taken as vectors + */ + public static Coordinate cross(Coordinate a, Coordinate b) { + return new Coordinate(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); + } + /** * Distance from the origin to the Coordinate. */ @@ -250,8 +264,8 @@ public final class Coordinate implements Cloneable, Serializable { } - - + + /** * Weighted average of two coordinates. If either of the weights are positive, * the result is the weighted average of the coordinates and the weight is the sum @@ -316,10 +330,10 @@ public final class Coordinate implements Cloneable, Serializable { else return String.format("(%.3f,%.3f,%.3f)", x, y, z); } - + @Override public Coordinate clone() { - return new Coordinate( this.x, this.y, this.z, this.weight ); + return new Coordinate(this.x, this.y, this.z, this.weight); } - + } diff --git a/core/src/net/sf/openrocket/util/FileUtils.java b/core/src/net/sf/openrocket/util/FileUtils.java new file mode 100644 index 000000000..3956f6b2d --- /dev/null +++ b/core/src/net/sf/openrocket/util/FileUtils.java @@ -0,0 +1,41 @@ +package net.sf.openrocket.util; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public abstract class FileUtils { + + public static void copy( InputStream is, OutputStream os ) throws IOException { + + if ( ! (os instanceof BufferedOutputStream ) ) { + os = new BufferedOutputStream(os); + } + + if ( ! (is instanceof BufferedInputStream ) ) { + is = new BufferedInputStream(is); + } + + byte[] buffer = new byte[1024]; + int bytesRead = 0; + + while( (bytesRead = is.read(buffer)) > 0 ) { + os.write(buffer,0,bytesRead); + } + os.flush(); + } + + public static byte[] readBytes( InputStream is ) throws IOException { + + ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); + + copy( is, bos ); + + return bos.toByteArray(); + + } + +} diff --git a/core/src/net/sf/openrocket/util/Reflection.java b/core/src/net/sf/openrocket/util/Reflection.java index b3c66f62b..a5e39d282 100644 --- a/core/src/net/sf/openrocket/util/Reflection.java +++ b/core/src/net/sf/openrocket/util/Reflection.java @@ -2,14 +2,13 @@ package net.sf.openrocket.util; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; import net.sf.openrocket.rocketcomponent.RocketComponent; public class Reflection { - private static final String ROCKETCOMPONENT_PACKAGE = "net.sf.openrocket.rocketcomponent"; - /** * Simple wrapper class that converts the Method.invoke() exceptions into suitable * RuntimeExceptions. @@ -88,25 +87,24 @@ public class Reflection { } - + /** - * Find a method from the rocket component classes. + * Find a method from a class. * Throws an exception if method not found. */ - public static Reflection.Method findMethod( - Class componentClass, - String method, Class... params) { - Reflection.Method m = findMethod(ROCKETCOMPONENT_PACKAGE, componentClass, - "", method, params); - if (m == null) { - throw new BugException("Could not find method for componentClass=" - + componentClass + " method=" + method); + public static Reflection.Method findMethod(Class c, String method, Class... params) { + + java.lang.reflect.Method m; + try { + m = c.getMethod(method, params); + return new Reflection.Method(m); + } catch (NoSuchMethodException e) { + throw new BugException("Could not find method " + method + "(" + Arrays.toString(params) + ") from class " + c); } - return m; } - + public static Reflection.Method findMethod(String pack, RocketComponent component, String method, Class... params) { return findMethod(pack, component.getClass(), "", method, params); diff --git a/core/src/net/sf/openrocket/util/SimpleStack.java b/core/src/net/sf/openrocket/util/SimpleStack.java index 6bdd96718..91915c5b9 100644 --- a/core/src/net/sf/openrocket/util/SimpleStack.java +++ b/core/src/net/sf/openrocket/util/SimpleStack.java @@ -26,4 +26,11 @@ public class SimpleStack extends ArrayList { return value; } + public String toString() { + StringBuilder sb = new StringBuilder("SimpleStack count=" + size() + "\n"); + for( T element: this ) { + sb.append(" ").append(element.toString()); + } + return sb.toString(); + } } diff --git a/core/src/net/sf/openrocket/util/TestRockets.java b/core/src/net/sf/openrocket/util/TestRockets.java index 0adb94fa0..acf1fc1f8 100644 --- a/core/src/net/sf/openrocket/util/TestRockets.java +++ b/core/src/net/sf/openrocket/util/TestRockets.java @@ -18,7 +18,6 @@ import net.sf.openrocket.rocketcomponent.InnerTube; import net.sf.openrocket.rocketcomponent.InternalComponent; import net.sf.openrocket.rocketcomponent.LaunchLug; import net.sf.openrocket.rocketcomponent.MassComponent; -import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent; import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.ReferenceType; import net.sf.openrocket.rocketcomponent.Rocket; @@ -134,8 +133,6 @@ public class TestRockets { setBasics(body); body.setThickness(rnd(0.002)); body.setFilled(rnd.nextBoolean()); - body.setIgnitionDelay(rnd.nextDouble() * 3); - body.setIgnitionEvent((IgnitionEvent) randomEnum(IgnitionEvent.class)); body.setLength(rnd(0.3)); body.setMotorMount(rnd.nextBoolean()); body.setMotorOverhang(rnd.nextGaussian() * 0.03); @@ -276,13 +273,13 @@ public class TestRockets { bodytube.setMaterial(material); finset.setMaterial(material); - String id = rocket.newMotorConfigurationID(); + String id = rocket.newFlightConfigurationID(); bodytube.setMotorMount(true); Motor m = Application.getMotorSetDatabase().findMotors(null, null, "B4", Double.NaN, Double.NaN).get(0); - bodytube.setMotor(id, m); + bodytube.getMotorConfiguration().get(id).setMotor(m); bodytube.setMotorOverhang(0.005); - rocket.getDefaultConfiguration().setMotorConfigurationID(id); + rocket.getDefaultConfiguration().setFlightConfigurationID(id); rocket.getDefaultConfiguration().setAllStages(); @@ -347,13 +344,13 @@ public class TestRockets { // bodytube.setMaterial(material); // finset.setMaterial(material); - String id = rocket.newMotorConfigurationID(); + String id = rocket.newFlightConfigurationID(); bodytube.setMotorMount(true); // Motor m = Application.getMotorSetDatabase().findMotors(null, null, "F12J", Double.NaN, Double.NaN).get(0); // bodytube.setMotor(id, m); // bodytube.setMotorOverhang(0.005); - rocket.getDefaultConfiguration().setMotorConfigurationID(id); + rocket.getDefaultConfiguration().setFlightConfigurationID(id); rocket.getDefaultConfiguration().setAllStages(); @@ -551,13 +548,13 @@ public class TestRockets { - String id = rocket.newMotorConfigurationID(); + String id = rocket.newFlightConfigurationID(); tube3.setMotorMount(true); // Motor m = Application.getMotorSetDatabase().findMotors(null, null, "L540", Double.NaN, Double.NaN).get(0); // tube3.setMotor(id, m); // tube3.setMotorOverhang(0.02); - rocket.getDefaultConfiguration().setMotorConfigurationID(id); + rocket.getDefaultConfiguration().setFlightConfigurationID(id); // tube3.setIgnitionEvent(MotorMount.IgnitionEvent.NEVER); diff --git a/core/src/net/sf/openrocket/util/TextUtil.java b/core/src/net/sf/openrocket/util/TextUtil.java index 0b86876de..41d7a2692 100644 --- a/core/src/net/sf/openrocket/util/TextUtil.java +++ b/core/src/net/sf/openrocket/util/TextUtil.java @@ -5,28 +5,24 @@ import java.nio.charset.Charset; public class TextUtil { - + private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - + /** - * Return the byte array for the string in the given charset. + * Return the byte array for the string (in US-ASCII charset). * * This function is implemented because Froyo (Android API 8) does not support * String.getBytes(Charset) - * - * @param string - * @param charSet - * @return */ - public static byte[] convertStringToBytes( String string, Charset charSet ) { - ByteBuffer encoded = charSet.encode(string); + public static byte[] asciiBytes(String string) { + ByteBuffer encoded = Charset.forName("US-ASCII").encode(string); return encoded.array(); } - + /** * Return the bytes formatted as a hexadecimal string. The length of the @@ -46,9 +42,9 @@ public class TextUtil { } /** - * Return a string of the double value with suitable precision (5 digits). - * The string is the shortest representation of the value including the - * required precision. + * Return a string of the double value with suitable precision for storage. + * The string is the shortest representation of the value including at least + * 5 digits of precision. * * @param d the value to present. * @return a representation with suitable precision. @@ -69,7 +65,7 @@ public class TextUtil { return "Inf"; } - + final String sign = (d < 0) ? "-" : ""; double abs = Math.abs(d); @@ -128,8 +124,8 @@ public class TextUtil { } - - + + /* * value must be positive! */ @@ -146,11 +142,11 @@ public class TextUtil { // Round value value = (Math.rint(value * rounding) + 0.1) / rounding; - + int whole = (int) value; value -= whole; - + if (value < limit) return "" + whole; limit *= 10; @@ -159,7 +155,7 @@ public class TextUtil { sb.append("" + whole); sb.append('.'); - + for (int i = 0; i < decimals; i++) { value *= 10; @@ -176,25 +172,42 @@ public class TextUtil { return sb.toString(); } - - public static String htmlEncode(String s) { - s = s.replace("&", "&"); - s = s.replace("\"", """); - s = s.replace("<", "<"); - s = s.replace(">", ">"); - return s; - } - - /* - * Returns a word-wrapped version of given input string using HTML syntax, wrapped to len characters. + /** + * Escape a string as XML or HTML. Encodes the following characters: + *

    + *
  • less than, greater than + *
  • quotation mark, apostrophe + *
  • ampersand + *
  • all control characters except newline, carriage return and tab + *
+ * + * The result is both valid XML and HTML 2.0. The majority of characters are left unchanged. */ - public static String wrap(String in,int len) { - in=in.trim(); - if(in.length()"+in.substring(0,place).trim()+"
"+wrap(in.substring(place),len); + public static String escapeXML(String s) { + StringBuilder sb = new StringBuilder(s.length()); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + if (c == '&') { + sb.append("&"); + } else if (c == '<') { + sb.append("<"); + } else if (c == '>') { + sb.append(">"); + } else if (c == '"') { + sb.append("""); + } else if (((c < 32) && (c != '\t') && (c != '\n') && (c != '\r')) || (c == '\'') || (c == 127)) { + // ' is not used since it's not standard HTML, use numerical escape instead + sb.append("&#").append((int) c).append(';'); + } else { + sb.append(c); + } + } + + return sb.toString(); } - + + + } diff --git a/core/src/net/sf/openrocket/util/enums/EnumName.java b/core/src/net/sf/openrocket/util/enums/EnumName.java index 78210612f..e6dfa8476 100644 --- a/core/src/net/sf/openrocket/util/enums/EnumName.java +++ b/core/src/net/sf/openrocket/util/enums/EnumName.java @@ -8,7 +8,7 @@ import net.sf.openrocket.util.BugException; public class EnumName> { - public static final EnumConversion NAME = new EnumConversion() { + private static final EnumConversion NAME = new EnumConversion() { @Override public String convert(Enum e) { return e.name(); diff --git a/core/src/net/sf/openrocket/utils/RocksimConverter.java b/core/src/net/sf/openrocket/utils/RocksimConverter.java index 49b91de69..ed46cef5b 100644 --- a/core/src/net/sf/openrocket/utils/RocksimConverter.java +++ b/core/src/net/sf/openrocket/utils/RocksimConverter.java @@ -6,11 +6,9 @@ import java.util.Locale; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.StorageOptions; -import net.sf.openrocket.file.DatabaseMotorFinder; +import net.sf.openrocket.file.GeneralRocketLoader; +import net.sf.openrocket.file.GeneralRocketSaver; import net.sf.openrocket.file.RocketLoadException; -import net.sf.openrocket.file.RocketLoader; -import net.sf.openrocket.file.RocketSaver; -import net.sf.openrocket.file.openrocket.OpenRocketSaver; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.ResourceBundleTranslator; import net.sf.openrocket.logging.LogLevel; @@ -32,8 +30,7 @@ public class RocksimConverter { setup(); - RocketLoader loader = new net.sf.openrocket.file.rocksim.importt.RocksimLoader(); - RocketSaver saver = new OpenRocketSaver(); + GeneralRocketSaver saver = new GeneralRocketSaver(); for (String inputFile : args) { System.out.println("Converting " + inputFile + "..."); @@ -59,11 +56,12 @@ public class RocksimConverter { try { StorageOptions opts = new StorageOptions(); - opts.setCompressionEnabled(true); + opts.setFileType(StorageOptions.FileType.OPENROCKET); opts.setSimulationTimeSkip(StorageOptions.SIMULATION_DATA_NONE); opts.setExplicitlySet(true); - OpenRocketDocument document = loader.load(input, new DatabaseMotorFinder()); + GeneralRocketLoader loader = new GeneralRocketLoader(input); + OpenRocketDocument document = loader.load(); saver.save(output, document, opts); } catch (RocketLoadException e) { diff --git a/core/test-writing/apocd.ork b/core/test-writing/apocd.ork new file mode 100644 index 000000000..7f67b35ab --- /dev/null +++ b/core/test-writing/apocd.ork @@ -0,0 +1,695 @@ + + + + apocalypse + maximum + + + + Stage + 4.0 + 1.05829 + true + + + + Nose cone + + + + + +
+ + + + + 0.283495 + 0.2286 + false + smooth + RS: Polystyrene PS + 0.42545 + 0.00318 + ogive + 1.0 + 0.05 + 0.049215 + 0.0762 + 0.00318 + false + + + + Body tube sup + + + + + +
+ + + + + smooth + RS: Paper + 0.6317999999999999 + 9.699999999999986E-4 + 0.05 + + + + Tube coupler + 0.3556 + 0.07370879999999999 + 0.0 + false + RS: Kraft phenolic + 0.1524 + 0.0 + 0.0 + 0.049030000000000004 + 0.0010748000000000077 + + + + Main Parachute + 0.0968 + 0.08788299999999999 + 0.0 + false + 0.025 + 0.045000000000000005 + 0.0 + 0.0 + 0.75 + ejection + 200.0 + 0.0 + RS: 1.9 oz. Ripstop Nylon (SkyAngle) + 0.9144 + 8 + 0.6858 + RS: 1/16 In. braided nylon + + + + + + Elec Bay + + + + + +
+ + + + + smooth + RS: Paper + 0.1118 + 9.699999999999986E-4 + 0.05 + + + + Tube coupler + 0.3556 + 0.07370879999999999 + 0.0 + false + RS: Kraft phenolic + 0.1524 + 0.0 + 0.0 + 0.049030000000000004 + 0.0010748000000000077 + + + + + + Body tube inf1 + + + + + +
+ + + + + smooth + RS: Paper + 0.2018 + 9.699999999999986E-4 + 0.05 + + + + Tube coupler + 0.3556 + 0.07370879999999999 + 0.0 + false + RS: Kraft phenolic + 0.1524 + 0.0 + 0.0 + 0.049030000000000004 + 0.0010748000000000077 + + + + drogue Parachute + 0.12029999999999999 + 0.08788299999999999 + 0.0 + false + 0.025 + 0.045000000000000005 + 0.0 + 0.0 + 0.75 + ejection + 200.0 + 0.0 + RS: 1.9 oz. Ripstop Nylon (SkyAngle) + 0.1844 + 8 + 0.6858 + RS: 1/16 In. braided nylon + + + + + + Body tube inf2 + + + + + + smooth + RS: Paper + 0.5636 + 9.699999999999986E-4 + 0.05 + + + + Body tube + 0.0 + RS: Paper + 0.4606 + 0.0 + 0.0 + 0.02743 + 2.500000000000002E-4 + single + 1.0 + 0.0 + + automatic + 0.0 + 0.0 + + + + + Custom Fin-1 + + + + + +
+ + + + + 0.127 + smooth + RS: Birch + 1 + 0.0 + 0.00478 + rounded + 0.0 + 0.023370000000000002 + 0.4147 + 0.0 + + + + + + + + + + + + + + + + + + + Contre fins 1a + + + + + + 0.1349 + smooth + RS: Birch + 1 + -2.8650009700383143 + 0.00478 + rounded + 0.0 + + + + + + + + + + + + + + Contre fins 1b + + + + + + 0.1349 + smooth + RS: Birch + 1 + 3.009999399252072 + 0.00478 + rounded + 0.0 + + + + + + + + + + + + + + Custom Fin-2 + + + + + +
+ + + + + 0.127 + smooth + RS: Birch + 1 + 90.0002104591497 + 0.00478 + rounded + 0.0 + 0.023370000000000002 + 0.3175 + 0.0 + + + + + + + + + + + + + + + + + + + Contre fins 2a + + + + + + 0.1349 + smooth + RS: Birch + 1 + 86.13503717319718 + 0.00478 + rounded + 0.0 + + + + + + + + + + + + + + Contre fins 2b + + + + + + 0.1349 + smooth + RS: Birch + 1 + 93.38524511278261 + 0.00478 + rounded + 0.0 + + + + + + + + + + + + + + Custom Fin-3 + + + + + +
+ + + + + 0.127 + smooth + RS: Birch + 1 + 179.99984796050427 + 0.00478 + rounded + 0.0 + 0.023370000000000002 + 0.3175 + 0.0 + + + + + + + + + + + + + + + + + + + Contre fins 3a + + + + + + 0.1349 + smooth + RS: Birch + 1 + 176.00976987521324 + 0.00478 + rounded + 0.0 + + + + + + + + + + + + + + Contre fins 3b + + + + + + 0.1349 + smooth + RS: Birch + 1 + -177.2399102613591 + 0.00478 + rounded + 0.0 + + + + + + + + + + + + + + Custom Fin-4 + + + + + +
+ + + + + 0.127 + smooth + RS: Birch + 1 + -90.0002104591497 + 0.00478 + rounded + 0.0 + 0.023370000000000002 + 0.3175 + 0.0 + + + + + + + + + + + + + + + + + + + Contre fins 4a + + + + + + 0.1349 + smooth + RS: Birch + 1 + -86.49027100617829 + 0.00478 + rounded + 0.0 + + + + + + + + + + + + + + Contre fins 4b + + + + + + 0.1349 + smooth + RS: Birch + 1 + -94.48990774179484 + 0.00478 + rounded + 0.0 + + + + + + + + + + + + + + Ring + 0.10790000000000001 + RS: Aircraft plywood (Birch) + 0.00318 + 0.0 + 0.0 + 0.049030000000000004 + auto + + + + Ring + 0.5596 + RS: Aircraft plywood (Birch) + 0.00318 + 0.0 + 0.0 + 0.049030000000000004 + auto + + + + Bulkhead + 0.0 + RS: Birch + 0.0030499999999999998 + 0.0 + 0.0 + 0.049030000000000004 + + + + Launch lug + + + + + + 0.54 + smooth + RS: Polycarbonate + 0.006715 + 0.01 + 0.0010 + -134.99974273092943 + + + + Launch lug + + + + + + 0.0125 + smooth + RS: Polycarbonate + 0.006715 + 0.01 + 0.0010 + -134.99974273092943 + + + + + + Transition + + + + + + polished + RS: Urethane + 0.04445 + 0.00521 + conical + 0.05 + 0.03975 + 0.049530000000000005 + 0.00635 + 0.00521 + false + 0.0 + 0.0 + 0.00521 + false + + + + + + + + + diff --git a/core/test-writing/apocdecals.rkt b/core/test-writing/apocdecals.rkt new file mode 100644 index 000000000..3d7dc8f9b --- /dev/null +++ b/core/test-writing/apocdecals.rkt @@ -0,0 +1,830 @@ + + 4 + + + apocalypse + 1 + 7 + 0 + 3 + 0 + 3 + 0.0 + 0.0 + 0 + 0 + 0 + 0 + 0 + 0 + 29 + 4000.0 + 0.0 + 0.0 + 1058.29 + 0.0 + 0.0 + 0.0 + 0.0 + 1 + 1 + 1 + + + 283.495 + 1049.21 + Polystyrene PS + Nose cone + 228.6 + 1 + 0.0 + 361.54749538948397 + 228.6 + 0 + 0.0 + 0.0 + 0 + 425.45 + 1 + 1 + 1 + 1 + 3.18 + 0.0 + + 100.0 + 76.2 + 98.43 + + + 213.78929571200192 + 1121.29 + Paper + Body tube sup + 0.0 + 0 + 0.0 + 213.78929571200192 + 315.9 + 0 + 0.0 + 0.0 + 0 + 631.8 + 1 + 2 + 100.0 + 98.06 + 0 + 98.06 + 0.0 + 0 + + + 73.7088 + 958.705 + Kraft phenolic + Tube coupler + 0.0 + 1 + 355.6 + 47.84680640645568 + 0.0 + 0 + 0.0 + 0.0 + 0 + 152.4 + 0 + 3 + 98.06 + 95.9104 + 4 + 0 + + + 87.883 + 2.7098958420476207E-6 + 1.9 oz. Ripstop Nylon (SkyAngle) + Main Parachute + 0.0 + 1 + 96.8 + 5.613923693399999 + 0.0 + 1 + 0.0 + 0.0 + 0 + 25.0 + 0 + 4 + 914.4 + 8 + 0.0 + 685.8 + 1 + 0.00102 + 1/16 In. braided nylon + 0.75 + + + + + 37.831027636280204 + 1121.29 + Paper + Elec Bay + 0.0 + 0 + 0.0 + 37.831027636280204 + 55.9 + 0 + 0.0 + 0.0 + 0 + 111.8 + 1 + 5 + 100.0 + 98.06 + 0 + 98.06 + 0.0 + 0 + + + 73.7088 + 958.705 + Kraft phenolic + Tube coupler + 0.0 + 1 + 355.6 + 47.84680640645568 + 0.0 + 0 + 0.0 + 0.0 + 0 + 152.4 + 0 + 6 + 98.06 + 95.9104 + 4 + 0 + + + + + 68.28534326477062 + 1121.29 + Paper + Body tube inf1 + 0.0 + 0 + 0.0 + 68.28534326477062 + 100.9 + 0 + 0.0 + 0.0 + 0 + 201.8 + 1 + 7 + 100.0 + 98.06 + 0 + 98.06 + 0.0 + 0 + + + 73.7088 + 958.705 + Kraft phenolic + Tube coupler + 0.0 + 1 + 355.6 + 47.84680640645568 + 0.0 + 0 + 0.0 + 0.0 + 0 + 152.4 + 0 + 8 + 98.06 + 95.9104 + 4 + 0 + + + 87.883 + 6.663512241985067E-5 + 1.9 oz. Ripstop Nylon (SkyAngle) + drogue Parachute + 0.0 + 1 + 120.3 + 5.613923693399999 + 0.0 + 1 + 0.0 + 0.0 + 0 + 25.0 + 0 + 9 + 184.4 + 8 + 0.0 + 685.8 + 1 + 0.00102 + 1/16 In. braided nylon + 0.75 + + + + + 190.71169209130176 + 1121.29 + Paper + Body tube inf2 + 0.0 + 0 + 0.0 + 190.71169209130176 + 281.8 + 0 + 0.0 + 0.0 + 0 + 563.6 + 1 + 10 + 100.0 + 98.06 + 0 + 98.06 + 0.0 + 0 + + + 22.15154083044647 + 1121.29 + Paper + Body tube + 0.0 + 0 + -0.0 + 22.15154083044647 + 230.3 + 0 + 0.0 + 0.0 + 2 + 460.6 + 0 + 11 + 54.86 + 54.36 + 1 + 54.36 + 0.0 + 1 + + + + 98.30302513638873 + 680.785 + Birch + Custom Fin-1 + 0.0 + 0 + 127.0 + 98.30302513638873 + 220.45869619622917 + 0 + 0.0 + 0.0 + 0 + 0.0 + 1 + 12 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 414.7 + 23.37 + 0.0 + 1 + 0.0 + 406.0,0.0|406.0,2.0|398.0,11.3|395.0,13.0|382.0,58.0|354.0,67.0|320.0,22.0|282.0,25.0|258.0,119.0|228.4,128.0|117.158,25.3139|13.6306,15.2533|0.0,0.0| + + + 20.54997772807576 + 680.785 + Birch + Contre fins 1a + 0.0 + 0 + 134.9 + 20.54997772807576 + 198.96585221328334 + 0 + 0.0 + -0.0500037 + 0 + 0.0 + 1 + 13 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 50.0 + 0.0 + 0.0 + 1 + 0.0 + 392.101,0.0|391.0,4.0|387.269,9.0|381.787,10.0|272.906,20.0|114.111,19.9156|3.8,7.0|0.0,0.0| + + + 20.58888761237905 + 680.785 + Birch + Contre fins 1b + 0.0 + 0 + 134.9 + 20.58888761237905 + 198.83005192727484 + 0 + 0.0 + 0.0525344 + 0 + 0.0 + 1 + 14 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 50.0 + 0.0 + 0.0 + 1 + 0.0 + 392.101,0.0|391.0,4.0|387.269,9.0|381.787,10.0|272.906,20.0|114.0,20.0|3.8,7.0|0.0,0.0| + + + 90.9849300733435 + 680.785 + Birch + Custom Fin-2 + 0.0 + 0 + 127.0 + 90.9849300733435 + 208.74448258434109 + 0 + 0.0 + 1.5707999999999998 + 0 + 0.0 + 1 + 15 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 317.5 + 23.37 + 0.0 + 1 + 0.0 + 406.0,0.0|406.0,2.0|398.0,11.3|395.0,13.0|382.0,58.0|354.0,67.0|320.0,22.0|282.0,25.0|258.0,119.0|228.4,128.0|117.158,25.3139|13.6306,15.2533|0.0,0.0| + + + 20.58888761237905 + 680.785 + Birch + Contre fins 2a + 0.0 + 0 + 134.9 + 20.58888761237905 + 198.83005192727484 + 0 + 0.0 + 1.50334 + 0 + 0.0 + 1 + 16 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 50.0 + 0.0 + 0.0 + 1 + 0.0 + 392.101,0.0|391.0,4.0|387.269,9.0|381.787,10.0|272.906,20.0|114.0,20.0|3.8,7.0|0.0,0.0| + + + 20.58888761237905 + 680.785 + Birch + Contre fins 2b + 0.0 + 0 + 134.9 + 20.58888761237905 + 198.83005192727484 + 0 + 0.0 + 1.62988 + 0 + 0.0 + 1 + 17 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 50.0 + 0.0 + 0.0 + 1 + 0.0 + 392.101,0.0|391.0,4.0|387.269,9.0|381.787,10.0|272.906,20.0|114.0,20.0|3.8,7.0|0.0,0.0| + + + 90.9849300733435 + 680.785 + Birch + Custom Fin-3 + 0.0 + 0 + 127.0 + 90.9849300733435 + 208.74448258434109 + 0 + 0.0 + 3.1415899999999994 + 0 + 0.0 + 1 + 18 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 317.5 + 23.37 + 0.0 + 1 + 0.0 + 406.0,0.0|406.0,2.0|398.0,11.3|395.0,13.0|382.0,58.0|354.0,67.0|320.0,22.0|282.0,25.0|258.0,119.0|228.4,128.0|117.158,25.3139|13.6306,15.2533|0.0,0.0| + + + 20.58888761237905 + 680.785 + Birch + Contre fins 3a + 0.0 + 0 + 134.9 + 20.58888761237905 + 198.83005192727484 + 0 + 0.0 + 3.07195 + 0 + 0.0 + 1 + 19 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 50.0 + 0.0 + 0.0 + 1 + 0.0 + 392.101,0.0|391.0,4.0|387.269,9.0|381.787,10.0|272.906,20.0|114.0,20.0|3.8,7.0|0.0,0.0| + + + 20.58888761237905 + 680.785 + Birch + Contre fins 3b + 0.0 + 0 + 134.9 + 20.58888761237905 + 198.83005192727484 + 0 + 0.0 + -3.09342 + 0 + 0.0 + 1 + 20 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 50.0 + 0.0 + 0.0 + 1 + 0.0 + 392.101,0.0|391.0,4.0|387.269,9.0|381.787,10.0|272.906,20.0|114.0,20.0|3.8,7.0|0.0,0.0| + + + 90.9849300733435 + 680.785 + Birch + Custom Fin-4 + 0.0 + 0 + 127.0 + 90.9849300733435 + 208.74448258434109 + 0 + 0.0 + -1.5707999999999998 + 0 + 0.0 + 1 + 21 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 317.5 + 23.37 + 0.0 + 1 + 0.0 + 406.0,0.0|406.0,2.0|398.0,11.3|395.0,13.0|382.0,58.0|354.0,67.0|320.0,22.0|282.0,25.0|258.0,119.0|228.4,128.0|117.158,25.3139|13.6306,15.2533|0.0,0.0| + + + 20.58888761237905 + 680.785 + Birch + Contre fins 4a + 0.0 + 0 + 134.9 + 20.58888761237905 + 198.83005192727484 + 0 + 0.0 + -1.50954 + 0 + 0.0 + 1 + 22 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 50.0 + 0.0 + 0.0 + 1 + 0.0 + 392.101,0.0|391.0,4.0|387.269,9.0|381.787,10.0|272.906,20.0|114.0,20.0|3.8,7.0|0.0,0.0| + + + 20.58888761237905 + 680.785 + Birch + Contre fins 4b + 0.0 + 0 + 134.9 + 20.58888761237905 + 198.83005192727484 + 0 + 0.0 + -1.64916 + 0 + 0.0 + 1 + 23 + 1 + 0.0 + 0.0 + 0.0 + 0.0 + 4.78 + 2 + 1 + 50.0 + 0.0 + 0.0 + 1 + 0.0 + 392.101,0.0|391.0,4.0|387.269,9.0|381.787,10.0|272.906,20.0|114.0,20.0|3.8,7.0|0.0,0.0| + + + 11.961913235279138 + 724.996 + Aircraft plywood (Birch) + Ring + 0.0 + 0 + 107.9 + 11.961913235279138 + 1.59 + 0 + 0.0 + 0.0 + 0 + 3.18 + 0 + 24 + 98.06 + 54.86 + 0 + 0 + + + 11.961913235279138 + 724.996 + Aircraft plywood (Birch) + Ring + 0.0 + 0 + 559.6 + 11.961913235279138 + 1.59 + 0 + 0.0 + 0.0 + 0 + 3.18 + 0 + 25 + 98.06 + 54.86 + 0 + 0 + + + 15.681351031672762 + 680.785 + Birch + Bulkhead + 0.0 + 0 + 0.0 + 15.681351031672762 + 1.525 + 0 + 0.0 + 0.0 + 0 + 3.05 + 0 + 26 + 98.06 + 0.0 + 1 + 0 + + + 0.4685140502167485 + 1199.78 + Polycarbonate + Launch lug + 0.0 + 0 + 540.0 + 0.4685140502167485 + 5.0 + 0 + 0.0 + -2.35619 + 0 + 10.0 + 1 + 27 + 13.43 + 11.43 + + + 0.4685140502167485 + 1199.78 + Polycarbonate + Launch lug + 0.0 + 0 + 12.5 + 0.4685140502167485 + 5.0 + 0 + 0.0 + -2.35619 + 0 + 10.0 + 1 + 28 + 13.43 + 11.43 + + + + + 61.64272321712273 + 847.056 + Urethane + Transition + 0.0 + 0 + 0.0 + 61.64272321712273 + 18.041444744426794 + 0 + 0.0 + 0.0 + 0 + 44.45 + 0 + 29 + 0 + 1 + 5.21 + 0.0 + + 6.35 + 0.0 + 99.06 + 0.0 + 100.0 + 79.5 + + + + + + + \ No newline at end of file diff --git a/core/test-writing/asimple.ork b/core/test-writing/asimple.ork new file mode 100644 index 000000000..a1305234f --- /dev/null +++ b/core/test-writing/asimple.ork @@ -0,0 +1,1050 @@ + + + + A simple model rocket + Sampo Niskanen + + + + + + maximum + + + + Sustainer + + + + Nose cone + normal + Polystyrene + 0.1 + 0.0020 + ogive + 1.0 + 0.0125 + 0.0115 + 0.02 + 0.0020 + true + + + + Body tube + normal + Cardboard + 0.3 + 0.0010 + auto + + + + Trapezoidal fin set + 0.0 + normal + Cardboard + 3 + 0.0 + 0.0020 + square + 0.0 + 0.05 + 0.05 + 0.025 + 0.03 + + + + Inner Tube + 0.0050 + Cardboard + 0.075 + 0.0 + 0.0 + 0.0095 + 5.000000000000004E-4 + single + 1.0 + 0.0 + + + single + Estes + 22aec01287ea1e3b8c6f66b26fe5fea6 + A8 + 0.018 + 0.07 + 3.0 + + + single + Estes + c15b9b96bf06e0ab896394787da3c47e + B4 + 0.018 + 0.07 + 4.0 + + + single + Estes + c8743ef3fa99e14a89885cbe0ead47b2 + C6 + 0.018 + 0.07 + 3.0 + + + single + Estes + c8743ef3fa99e14a89885cbe0ead47b2 + C6 + 0.018 + 0.07 + 5.0 + + + single + Estes + c8743ef3fa99e14a89885cbe0ead47b2 + C6 + 0.018 + 0.07 + 7.0 + + automatic + 0.0 + 0.0030 + + + + + Engine block + 0.0020 + Cardboard + 0.0050 + 0.0 + 0.0 + auto + 0.0030 + + + + + + Centering ring + 0.0 + The centering ring automatically takes the outer diameter of the body tube and the inner diameter of the inner tube. + Cardboard + 0.0020 + 0.0 + 0.0 + auto + auto + + + + Centering ring + -0.045 + The centering ring automatically takes the outer diameter of the body tube and the inner diameter of the inner tube. + Cardboard + 0.0020 + 0.0 + 0.0 + auto + auto + + + + Shock cord + 0.02 + The shock cord does not need to be attached to anything in particular, as it functions only as a mass component. + 0.052000000000000005 + 0.0060 + 0.0 + 0.0 + 0.4 + Elastic cord (round 2mm, 1/16 in) + + + + Parachute + 0.032 + 0.042 + 0.009000000000000001 + 0.0 + 0.0 + auto + ejection + 200.0 + 0.0 + Ripstop nylon + 0.3 + 6 + 0.3 + Elastic cord (round 2mm, 1/16 in) + + + + Wadding + 0.08 + 0.03 + 0.0115 + 0.0 + 0.0 + 0.0020 + + + + Launch lug + 0.0 + normal + Cardboard + 0.0035 + 0.035 + 0.0010 + 19.0 + + + + + + + + + + + Simulation 1 + RK4Simulator + BarrowmanCalculator + + da326836-0959-4c94-bcd5-49dee07235a4 + 1.0 + 0.0 + 0.0 + 2.0 + 0.1 + 0.0 + 45.0 + 0.0 + flat + + 0.05 + + + + + + + + + + + + + + 0,0,0,-8.838,0,8.838,0,0,0,0,0,0,0.7854,0,1.5708,0,0,0,0.064492,0.0012969,1.9993e-5,NaN,0.24786,NaN,0.0066002,64548,0.062439,0,1.0783,0,0.66244,0.29583,0.12001,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,0.025,4.9087e-4,1.5708,0,NaN,288.15,101325,340.39,0.01,0.0089838 + 0.24959,1.0565,15.602,88.854,15.602,88.894,0,0,0,0,0,2.6477,0.7854,0,0.14943,0,0,0,0.062913,0.0012709,1.9929e-5,0.29863,0.24472,2.1565,0.046352,453271,6.0412,0.073498,0.8528,0.98212,0.43647,0.29606,0.12028,2.2259,4.8005,2.6144e-4,0,0,0,0,0,0.025,4.9087e-4,1.5708,0,NaN,288.14,101313,340.38,0.0098793,0.013483 + 0.4949,6.1749,24.525,24.591,24.528,25.801,-0.015081,-6.2971e-7,0.015081,-3.1416,0.34218,7.8109,0.7854,0,0.053357,2.7316e-8,-0.71171,-1.0985e-5,0.061846,0.0012526,1.9886e-5,0.31097,0.24251,2.7385,0.07251,708754,2.3387,0.1602,0.85341,0.87528,0.43633,0.2964,0.12068,0.69123,1.9573,1.7839e-4,0,0,0,0,-0.06471,0.025,4.9087e-4,1.4055,1.9157e-6,NaN,288.11,101253,340.36,0.0097644,0.016576 + 0.74534,12.996,28.328,-13.039,28.387,13.333,0.32432,-2.1448e-6,0.32432,-6.613e-6,1.8294,2.7846,0.7854,7.2086e-8,0.046826,3.89e-8,-0.40626,-7.2924e-7,0.061193,0.0012411,1.986e-5,0.31197,0.24112,2.834,0.084235,822877,0,0.21494,0.85378,0.8709,0.43625,0.2966,0.12092,0.60046,1.717,4.8388e-4,0,0,0,0,-0.015626,0.025,4.9087e-4,1.4633,-1.04e-6,NaN,288.07,101173,340.34,0.0081786,0.032429 + 0.99614,19.697,25.116,-12.791,25.161,12.829,0.71757,-3.3832e-6,0.71757,-4.7148e-6,1.5076,0.98224,0.7854,1.5949e-7,0.038335,6.2726e-8,-0.33657,-3.6092e-6,0.061193,0.0012411,1.986e-5,0.31327,0.24112,2.8863,0.074693,729236,0,0.16777,0.85348,0.8652,0.43632,0.29644,0.12073,0.48486,1.4132,4.5345e-4,0,0,0,0,-0.013642,0.025,4.9087e-4,1.3779,-4.0581e-6,NaN,288.02,101094,340.31,0.012647,0.035929 + 1.2526,25.727,21.96,-11.768,22.016,11.811,1.1258,-5.5063e-6,1.1258,-4.8908e-6,1.5637,1.0054,0.7854,2.5023e-7,0.023747,8.9451e-8,-0.26377,2.1146e-5,0.061193,0.0012411,1.986e-5,0.31562,0.24112,2.9801,0.065455,638709,0,0.12766,0.85322,0.85788,0.43637,0.29629,0.12056,0.29322,0.88443,-3.2667e-4,0,0,0,0,-0.010913,0.025,4.9087e-4,1.4266,1.4172e-5,NaN,287.98,101023,340.29,0.026335,0.050857 + 1.5097,30.989,19.002,-11.309,19.048,11.319,1.4915,-6.6604e-6,1.4915,-4.4655e-6,1.3125,0.48019,0.7854,3.3152e-7,0.0094529,8.5102e-8,0.40485,1.5504e-4,0.061193,0.0012411,1.986e-5,0.31803,0.24112,3.0767,0.056826,554253,0,0.095698,0.85302,0.85378,0.43642,0.29618,0.12042,0.11394,0.31682,3.2445e-4,0,0,0,0,0.034112,0.025,4.9087e-4,1.3942,-1.9601e-5,NaN,287.95,100962,340.27,0.05,0.052582 + 1.7692,35.541,16.089,-11.124,16.145,11.135,1.8304,-1.015e-5,1.8304,-5.5454e-6,1.3447,0.51338,0.7854,4.0683e-7,0.048483,9.3609e-8,0.0067396,3.7735e-5,0.061193,0.0012411,1.986e-5,0.31169,0.24112,2.8229,0.048257,470491,0,0.070376,0.85284,0.8711,0.43646,0.29608,0.1203,0.62311,1.7593,-4.2227e-4,0,0,0,0,1.311e-5,0.025,4.9087e-4,1.3214,9.4594e-6,NaN,287.92,100908,340.25,0.024385,0.07902 + 2.0024,38.998,13.571,-10.532,13.636,10.546,2.1476,-1.1745e-5,2.1476,-5.4689e-6,1.3287,0.54171,0.7854,4.7734e-7,0.031578,9.9846e-8,-0.13497,1.51e-5,0.061193,0.0012411,1.986e-5,0.31433,0.24112,2.9285,0.041089,400477,0,0.050395,0.85271,0.86079,0.43649,0.29601,0.12022,0.3949,1.1636,4.9189e-4,0,0,0,0,-0.0072528,0.025,4.9087e-4,1.3601,2.4367e-5,NaN,287.9,100868,340.24,0.05,0.080075 + 2.2587,42.133,10.912,-10.244,10.975,10.261,2.4681,-1.0341e-5,2.4681,-4.1898e-6,1.172,0.59578,0.7854,5.4859e-7,0.058071,8.0205e-8,0.28979,1.709e-5,0.061193,0.0012411,1.986e-5,0.31025,0.24112,2.7654,0.033452,325960,0,0.034389,0.8607,0.8865,0.44461,0.29595,0.12015,0.75764,2.0451,4.3456e-4,0,0,0,0,0.050448,0.025,4.9087e-4,1.3408,5.8626e-6,NaN,287.88,100831,340.22,0.043685,0.081141 + 2.5007,44.476,8.4503,-10.111,8.5189,10.113,2.7383,-8.5454e-6,2.7383,-3.1207e-6,1.0789,0.1928,0.7854,6.0864e-7,0.017035,8.3625e-8,0.4989,1.54e-4,0.061193,0.0012411,1.986e-5,0.31672,0.24112,3.0242,0.026444,257621,0,0.021507,0.88492,0.88744,0.46893,0.2959,0.12009,0.20793,0.38983,1.6296e-4,0,0,0,0,0.23929,0.025,4.9087e-4,1.2375,-1.1966e-5,NaN,287.86,100803,340.21,0.05,0.08202 + 2.7507,46.274,5.9369,-9.9754,6.0295,9.976,3.0044,-7.9076e-6,3.0044,-2.632e-6,1.0524,0.10482,0.7854,6.6777e-7,0.0075583,8.4846e-8,0.47472,7.795e-4,0.061193,0.0012411,1.986e-5,0.31835,0.24112,3.0893,0.019508,190015,0,0.012129,0.91937,0.91989,0.50345,0.29586,0.12005,0.090788,-0.1176,-4.1697e-4,0,0,0,0,0.39813,0.025,4.9087e-4,1.115,-2.8134e-5,NaN,287.85,100782,340.21,0.05,0.082871 + 3.0007,47.448,3.4652,-9.803,3.6105,9.805,3.2633,-8.9777e-6,3.2633,-2.7511e-6,1.014,0.2006,0.7854,7.2532e-7,0.11895,8.4827e-8,0.52485,6.1001e-5,0.061193,0.0012411,1.986e-5,0.30213,0.24112,2.4406,0.013284,129375,0,0.0065489,0.96864,1.0713,0.55277,0.29584,0.12002,1.6987,3.0964,1.6322e-4,0,0,0,0,1.0496,0.025,4.9087e-4,0.99276,-2.758e-5,NaN,287.84,100769,340.2,0.05,0.083708 + 3.2507,48.01,1.0388,-9.6235,1.4069,9.6275,3.5093,-1.1159e-5,3.5093,-3.1799e-6,0.94886,0.27873,0.7854,7.8e-7,0.46926,8.448e-8,0.63034,7.4145e-6,0.061193,0.0012411,1.986e-5,0.27588,0.24112,1.3907,0.0082584,80429,0,0.0029038,1.0403,1.2291,0.62446,0.29583,0.12001,8.5425,3.2664,3.7598e-4,0,0,0,0,3.9169,0.025,4.9087e-4,0.84798,-2.1795e-5,NaN,287.84,100762,340.2,0.05,0.084575 + 3.5007,47.97,-1.3642,-9.603,1.6304,9.6046,3.7383,-1.3774e-5,3.7383,-3.6845e-6,0.89277,0.1765,0.7854,8.309e-7,1.151,8.4537e-8,0.62945,-6.2453e-6,0.061193,0.0012411,1.986e-5,0.32491,0.24112,3.3519,0.0090309,87952,0,4.058e-4,1.0258,0.14363,0.60995,0.29583,0.12001,5.8252,0.42338,3.221e-4,0,0,0,0,3.2662,0.025,4.9087e-4,0.69092,-1.6523e-5,NaN,287.84,100762,340.2,0.05,0.10371 + 3.7362,47.385,-3.5883,-9.2274,3.6837,9.234,3.9424,-1.6279e-5,3.9424,-4.1291e-6,0.83309,0.34903,0.7854,8.7626e-7,1.4677,8.4534e-8,0.63165,-7.695e-6,0.061193,0.0012411,1.986e-5,0.3259,0.24112,3.3912,0.013204,128603,0,1.5159e-5,0.96946,0.0025098,0.5536,0.29584,0.12002,6.8462,0.023308,2.6061e-4,0,0,0,0,1.5385,0.025,4.9087e-4,0.54239,-1.2608e-5,NaN,287.84,100769,340.2,0.0025,0.10471 + 4.0271,46.298,-3.883,-1.0028,4.1503,4.2111,3.9484,-1.8424e-5,3.9484,-4.666e-6,0.63978,4.09,0.7854,8.776e-7,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012199,118823,0,0.594,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9029,287.85,100777,340.2,0.11873,0.10499 + 4.1945,45.636,-4.0311,-0.88505,4.0767,2.9869,3.8014,-1.9072e-5,3.8014,-5.0172e-6,1.1173,2.8528,0.7854,8.4492e-7,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011983,116719,0,0.57314,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8815,287.85,100782,340.21,0.1674,0.10503 + 4.4158,44.734,-4.1186,-0.3952,4.1422,2.2594,3.4996,-1.9557e-5,3.4996,-5.5882e-6,1.6096,2.2246,0.7854,7.7785e-7,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012175,118601,0,0.59175,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0702,287.85,100790,340.21,0.2213,0.10508 + 4.9158,42.645,-4.2353,-0.23347,4.1236,0.52644,2.6358,-1.9856e-5,2.6358,-7.5329e-6,1.8455,0.47184,0.7854,5.8586e-7,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012121,118076,0,0.58649,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8126,287.86,100800,340.21,0.5,0.10512 + 5.4158,40.566,-4.08,0.31053,4.237,0.42041,1.7485,-1.9809e-5,1.7485,-1.1329e-5,1.7038,0.2834,0.7854,3.8863e-7,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012454,121342,0,0.61931,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7269,287.87,100825,340.22,0.5,0.10517 + 5.9158,38.474,-4.2898,-0.41944,4.0801,0.4203,0.89321,-1.9819e-5,0.89321,-2.2188e-5,1.7173,0.026906,0.7854,1.9853e-7,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011992,116867,0,0.5744,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7155,287.89,100849,340.23,0.5,0.10521 + 6.4158,36.402,-3.9984,0.5828,4.2941,0.74684,-0.023813,-1.9817e-5,0.023813,-3.1408,1.9508,0.46702,0.7854,-5.2927e-9,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012621,123017,0,0.63637,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9101,287.9,100874,340.24,0.5,0.10526 + 6.9158,34.305,-4.388,-0.77918,4.0023,0.87593,-0.9492,-1.9818e-5,0.9492,-3.1416,1.7507,0.40017,0.7854,-2.1097e-7,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011763,114676,0,0.55293,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7736,287.91,100898,340.25,0.5,0.1053 + 7.2815,32.774,-3.99,1.0884,4.4006,1.3673,-1.6447,-1.9818e-5,1.6447,-3.1416,2.0534,0.82766,0.7854,-3.6557e-7,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012933,126108,0,0.66859,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0841,287.93,100923,340.25,0.36567,0.10535 + 7.7815,30.676,-4.4009,-0.82195,3.9904,0.83208,-2.6876,-1.9818e-5,2.6876,-3.1416,2.1181,0.12945,0.7854,-5.9736e-7,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011727,114365,0,0.54983,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1109,287.94,100941,340.26,0.5,0.10539 + 8.1883,28.98,-3.9391,1.1353,4.405,1.2291,-3.5102,-1.9818e-5,3.5102,-3.1416,1.9265,0.47105,0.7854,-7.8021e-7,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012946,126269,0,0.67016,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9286,287.95,100965,340.27,0.4068,0.10544 + 8.6639,26.988,-4.4369,-1.0467,3.9394,1.0513,-4.4375,-1.9818e-5,4.4375,-3.1416,1.973,0.097856,0.7854,-9.8632e-7,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011577,112936,0,0.53605,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9705,287.96,100985,340.27,0.4756,0.10548 + 8.9423,25.806,-4.0559,1.3686,4.4609,1.796,-5.0319,-1.9818e-5,5.0319,-3.1416,2.2968,1.163,0.7854,-1.1184e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.013109,127907,0,0.68751,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.4348,287.97,101008,340.28,0.27839,0.10553 + 9.4423,23.713,-4.3129,-0.51386,4.0574,0.57317,-6.212,-1.9818e-5,6.212,-3.1416,2.4237,0.2539,0.7854,-1.3807e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011924,116349,0,0.56884,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.4076,287.98,101022,340.29,0.5,0.10557 + 9.9423,21.645,-3.9625,0.70079,4.3137,0.7296,-7.4493,-1.9818e-5,7.4493,-3.1416,2.5252,0.20301,0.7854,-1.6557e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012676,123716,0,0.64307,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.5071,288,101047,340.3,0.5,0.10562 + 10.41,19.691,-4.3928,-0.92043,3.9699,1.0693,-8.5705,-1.9818e-5,8.5705,-3.1416,2.2707,0.54431,0.7854,-1.9049e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011666,113874,0,0.54476,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2825,288.01,101071,340.3,0.46758,0.10566 + 10.78,18.144,-3.978,1.1221,4.4033,1.3525,-9.3584,-1.9818e-5,9.3584,-3.1416,1.9916,0.755,0.7854,-2.0801e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012939,126325,0,0.67033,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9673,288.02,101094,340.31,0.36969,0.10614 + 11.28,16.047,-4.4085,-0.86107,3.9795,0.8957,-10.385,-1.9818e-5,10.385,-3.1416,2.115,0.24667,0.7854,-2.3082e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011694,114180,0,0.54759,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1013,288.03,101112,340.32,0.5,0.10619 + 11.657,14.469,-3.9584,1.193,4.4146,1.325,-11.142,-1.9818e-5,11.142,-3.1416,1.8974,0.57654,0.7854,-2.4765e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012972,126684,0,0.674,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8839,288.05,101137,340.33,0.37736,0.10624 + 12.157,12.371,-4.4331,-0.94951,3.9584,0.94996,-12.087,-1.9818e-5,12.087,-3.1416,1.8828,0.02923,0.7854,-2.6866e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011631,113605,0,0.54197,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8843,288.06,101155,340.33,0.5,0.1133 + 12.539,10.774,-3.935,1.3051,4.4333,1.3099,-12.814,-1.9818e-5,12.814,-3.1416,1.9259,0.11286,0.7854,-2.8481e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.013026,127257,0,0.67997,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9278,288.07,101180,340.34,0.3817,0.1134 + 13.01,8.8026,-4.4296,-1.0495,3.9356,1.061,-13.739,-1.9818e-5,13.739,-3.1416,1.9991,0.15549,0.7854,-3.0537e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011563,112983,0,0.53593,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9957,288.08,101199,340.35,0.47127,0.11345 + 13.36,7.3337,-3.973,1.3057,4.4357,1.4301,-14.402,-1.9818e-5,14.402,-3.1416,1.7952,0.58324,0.7854,-3.2011e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.013032,127358,0,0.68091,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7666,288.09,101222,340.35,0.34964,0.11349 + 13.86,5.2375,-4.4119,-0.87768,3.9731,0.87865,-15.305,-1.9818e-5,15.305,-3.1416,1.8159,0.041374,0.7854,-3.4018e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011673,114089,0,0.54637,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8136,288.1,101239,340.36,0.5,0.11354 + 14.273,3.5166,-3.9133,1.206,4.412,1.2094,-16.048,-1.9818e-5,16.048,-3.1416,1.7785,0.090503,0.7854,-3.5669e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012963,126713,0,0.6739,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7796,288.12,101264,340.37,0.41342,0.11358 + 14.612,2.1281,-4.2808,-1.0845,3.9389,1.4754,-16.708,-1.9818e-5,16.708,-3.1416,2.1175,1.0004,0.7854,-3.7137e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011572,113141,0,0.53721,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2273,288.13,101284,340.38,0.33889,0.11363 + 15.112,0.061532,-3.9856,0.59045,4.2917,0.94898,-17.674,-1.9818e-5,17.674,-3.1416,1.746,0.74292,0.7854,-3.9283e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.012609,123287,0,0.63783,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8116,288.14,101300,340.38,0.5,0.11367 + 15.509,-1.5823,-4.2877,-0.76025,4.01,1.2583,-18.447,-1.9818e-5,18.447,-3.1416,2.1445,1.0026,0.7854,-4.1002e-6,NaN,NaN,NaN,NaN,0.061193,NaN,NaN,NaN,NaN,NaN,0.011781,115211,0,0.55694,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1878,288.15,101324,340.39,0.39737,0.11372 + + + + + + Simulation 2 + RK4Simulator + BarrowmanCalculator + + 05896b5a-71a7-48ac-b7ae-eadeb267975b + 1.0 + 0.0 + 0.0 + 2.0 + 0.1 + 0.0 + 45.0 + 0.0 + flat + + 0.05 + + + + + + + + + + + + + + 0,0,0,-8.2475,0,8.2475,0,0,0,0,0,0,0.7854,0,1.5708,0,0,0,0.067042,0.0013365,2.0096e-5,NaN,0.25262,NaN,0.0069905,68366,0.1045,0,1.068,0,0.65219,0.29583,0.12001,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,0.025,4.9087e-4,1.5708,0,NaN,288.15,101325,340.39,0.01,0.0012337 + 0.24863,2.2951,22.47,53.994,22.47,54.016,-0.0040116,-7.5957e-9,0.0040116,-3.1416,0.12846,1.5368,0.7854,0,0.072709,0,0.40711,1.1357e-5,0.064779,0.0013016,2.0005e-5,0.30817,0.24842,2.39,0.06625,647781,4.2647,0.13632,0.85324,0.89182,0.43637,0.29631,0.12057,0.97058,2.2945,-3.8965e-6,0,0,0,0,0.025362,0.025,4.9087e-4,1.559,-1.9695e-5,NaN,288.14,101298,340.38,0.010363,0.0059605 + 0.49515,9.2601,33.457,38.93,33.477,39.761,0.060434,3.326e-7,0.060434,5.5036e-6,1.1366,8.0852,0.7854,1.3432e-8,0.03091,-5.0971e-8,0.24656,2.6489e-5,0.063489,0.0012806,1.9953e-5,0.31448,0.24588,2.7437,0.098717,964657,3.4254,0.29233,0.8543,0.86206,0.43613,0.2969,0.12127,0.38639,1.0561,-4.5751e-5,0,0,0,0,0.0041906,0.025,4.9087e-4,1.4482,4.0186e-5,NaN,288.09,101216,340.35,0.0099852,0.020806 + 0.73215,18.25,42.323,36.191,42.366,36.418,0.44933,1.3008e-5,0.44933,2.8949e-5,1.9111,4.0559,0.7854,9.987e-8,9.2738e-4,-2.2944e-8,0.45064,6.4451e-4,0.062397,0.0012622,1.9908e-5,0.3196,0.24366,3.0374,0.12488,1219372,3.345,0.46376,0.85545,0.85546,0.43588,0.29754,0.12203,0.011028,0.024306,-3.718e-5,0,0,0,0,0.0087491,0.025,4.9087e-4,1.4808,-6.9838e-6,NaN,288.03,101111,340.32,0.030069,0.024317 + 0.97855,29.753,51.045,35.452,51.139,35.536,1.0951,1.5672e-5,1.0951,1.4312e-5,3.112,2.4403,0.7854,2.434e-7,0.0091791,-8.1849e-8,0.14706,-1.3601e-5,0.061212,0.0012414,1.986e-5,0.31818,0.24116,3.0808,0.15068,1469853,3.4482,0.67597,0.85685,0.85757,0.43557,0.29833,0.12295,0.11078,0.34078,-3.0953e-4,0,0,0,0,6.4013e-4,0.025,4.9087e-4,1.4855,-1.9261e-5,NaN,287.96,100976,340.27,0.012906,0.027579 + 1.2283,42.243,47.617,-19.307,47.713,19.39,1.8799,1.5277e-5,1.8799,8.1265e-6,3.0346,1.7941,0.7854,4.1785e-7,0.0065203,2.5108e-8,-0.0017132,1.0929e-4,0.061043,0.0012384,1.9853e-5,0.31863,0.24079,3.1134,0.14069,1370858,0,0.58777,0.85628,0.85664,0.43569,0.29801,0.12257,0.078314,0.24401,2.3593e-4,0,0,0,0,-9.968e-8,0.025,4.9087e-4,1.4753,-2.2512e-5,NaN,287.88,100830,340.22,0.020657,0.029463 + 1.4972,54.367,42.67,-17.475,42.761,17.501,2.6589,1.2472e-5,2.6589,4.6908e-6,2.7899,0.9441,0.7854,5.9098e-7,9.6026e-4,9.5594e-8,0.0014214,2.1885e-4,0.061043,0.0012384,1.9853e-5,0.31959,0.24079,3.152,0.1262,1228380,0,0.47165,0.85551,0.85552,0.43586,0.29758,0.12207,0.01142,0.036126,-6.3287e-5,0,0,0,0,8.5304e-8,0.025,4.9087e-4,1.4617,1.8234e-5,NaN,287.8,100687,340.17,0.05,0.030672 + 1.7472,64.504,38.488,-16.068,38.573,16.08,3.3281,6.3213e-6,3.3281,1.8994e-6,2.561,0.62761,0.7854,7.3972e-7,0.0016351,1.3446e-7,-0.12893,-5.0872e-4,0.061043,0.0012384,1.9853e-5,0.31946,0.24079,3.1466,0.11399,1108519,0,0.38407,0.85494,0.85496,0.43599,0.29726,0.12169,0.019463,0.061688,1.6862e-4,0,0,0,0,-8.6053e-4,0.025,4.9087e-4,1.4481,4.8447e-5,NaN,287.73,100568,340.13,0.05,0.031563 + 1.9913,73.435,34.709,-14.868,34.794,14.887,3.9391,3.2035e-6,3.9391,8.1327e-7,2.4327,0.73675,0.7854,8.7553e-7,0.0015316,1.8856e-7,-0.05753,-1.9927e-4,0.061043,0.0012384,1.9853e-5,0.31946,0.24079,3.1469,0.10285,999474,0,0.31222,0.85446,0.85448,0.4361,0.29699,0.12138,0.018225,0.057811,-4.943e-4,0,0,0,0,-2.1046e-4,0.025,4.9087e-4,1.4476,-3.2801e-5,NaN,287.67,100464,340.1,0.05,0.032425 + 2.2615,82.285,30.843,-13.875,30.923,13.876,4.5663,-9.5088e-6,4.5663,-2.0824e-6,2.2252,0.12232,0.7854,1.0149e-6,0.008001,2.7145e-7,-0.14656,1.4111e-4,0.061043,0.0012384,1.9853e-5,0.31831,0.24079,3.1008,0.09152,888637,0,0.24696,0.85403,0.85458,0.43619,0.29675,0.12109,0.096246,0.3002,1.5911e-4,0,0,0,0,-0.0017255,0.025,4.9087e-4,1.4286,-4.7047e-5,NaN,287.62,100360,340.06,0.039863,0.033451 + 2.5174,89.733,27.413,-12.884,27.496,12.944,5.1313,-2.3789e-5,5.1313,-4.6361e-6,2.1286,1.2466,0.7854,1.1405e-6,0.018259,1.9093e-7,0.020865,2.8977e-5,0.061043,0.0012384,1.9853e-5,0.31654,0.24079,3.0301,0.081509,790911,0,0.19615,0.85369,0.85648,0.43627,0.29655,0.12086,0.22345,0.6775,-1.9827e-4,0,0,0,0,4.4101e-5,0.025,4.9087e-4,1.4412,2.3491e-5,NaN,287.57,100273,340.04,0.022288,0.034672 + 2.7635,96.094,24.306,-12.451,24.387,12.455,5.6282,-3.1391e-5,5.6282,-5.5775e-6,1.9918,0.31851,0.7854,1.251e-6,0.021997,2.1028e-7,-0.11973,5.7413e-5,0.061043,0.0012384,1.9853e-5,0.31591,0.24079,3.0048,0.07246,702710,0,0.15507,0.85341,0.85743,0.43633,0.2964,0.12068,0.27085,0.81608,-3.9023e-4,0,0,0,0,-0.0018378,0.025,4.9087e-4,1.3847,-4.2365e-5,NaN,287.53,100198,340.01,0.023431,0.036157 + 3.0228,101.99,21.183,-11.629,21.273,11.661,6.1485,-4.4797e-5,6.1485,-7.2859e-6,1.9554,0.85788,0.7854,1.3666e-6,0.020784,2.4457e-7,-0.11791,3.3035e-5,0.061043,0.0012384,1.9853e-5,0.31611,0.24079,3.0126,0.063225,612836,0,0.11789,0.85317,0.85676,0.43638,0.29626,0.12052,0.25537,0.77172,-2.8148e-4,0,0,0,0,-0.0023414,0.025,4.9087e-4,1.4208,1.1961e-5,NaN,287.49,100129,339.99,0.032568,0.037607 + 3.2959,107.35,18.076,-11.185,18.161,11.189,6.6508,-5.5365e-5,6.6508,-8.3245e-6,1.7532,0.32045,0.7854,1.4782e-6,0.0021405,2.5383e-7,0.3582,5.7678e-4,0.061043,0.0012384,1.9853e-5,0.31932,0.24079,3.1411,0.054257,525663,0,0.086384,0.85296,0.853,0.43643,0.29615,0.12038,0.025479,0.05065,3.4303e-4,0,0,0,0,0.029344,0.025,4.9087e-4,1.3723,-3.0948e-5,NaN,287.45,100066,339.97,0.05,0.03907 + 3.5418,111.45,15.355,-10.901,15.454,10.902,7.0799,-6.6886e-5,7.0799,-9.4473e-6,1.7505,0.062918,0.7854,1.5736e-6,0.022843,2.808e-7,-0.093554,-1.6036e-5,0.061043,0.0012384,1.9853e-5,0.31576,0.24079,2.9986,0.046469,450046,0,0.06364,0.85281,0.85712,0.43646,0.29606,0.12028,0.28158,0.84722,1.9299e-4,0,0,0,0,-0.0027291,0.025,4.9087e-4,1.3107,4.406e-6,NaN,287.43,100018,339.95,0.05,0.040055 + 3.7918,114.96,12.688,-10.452,12.802,10.46,7.5148,-7.6618e-5,7.5148,-1.0196e-5,1.7064,0.40603,0.7854,1.6703e-6,0.021289,2.8425e-7,0.042715,5.9162e-5,0.061043,0.0012384,1.9853e-5,0.31601,0.24079,3.0088,0.038902,376643,0,0.044547,0.85268,0.85644,0.43649,0.29599,0.1202,0.26174,0.78656,3.375e-4,0,0,0,0,8.1183e-4,0.025,4.9087e-4,1.3063,-3.3477e-5,NaN,287.4,99977,339.94,0.05,0.040938 + 4.0346,117.73,10.188,-10.189,10.309,10.199,7.9139,-8.8215e-5,7.9139,-1.1147e-5,1.5795,0.46573,0.7854,1.759e-6,0.044355,2.9745e-7,0.35261,7.8667e-7,0.061043,0.0012384,1.9853e-5,0.31231,0.24079,2.8609,0.031976,309507,0,0.030968,0.86586,0.88154,0.4498,0.29593,0.12013,0.56625,1.5376,1.4581e-4,0,0,0,0,0.081892,0.025,4.9087e-4,1.2591,-3.0873e-5,NaN,287.38,99944,339.93,0.05,0.041809 + 4.2846,119.96,7.6584,-10.059,7.8032,10.061,8.2968,-1.0177e-4,8.2968,-1.2267e-5,1.4967,0.19907,0.7854,1.8441e-6,0.01796,2.8748e-7,0.50724,-2.2228e-4,0.061043,0.0012384,1.9853e-5,0.31657,0.24079,3.0309,0.024936,241321,0,0.019113,0.89201,0.89483,0.47604,0.29589,0.12008,0.21956,0.38637,3.1544e-4,0,0,0,0,0.27866,0.025,4.9087e-4,1.1459,1.027e-6,NaN,287.37,99918,339.92,0.05,0.042721 + 4.5346,121.57,5.1634,-9.8935,5.363,9.8956,8.6652,-1.1384e-4,8.6652,-1.3137e-5,1.4497,0.20507,0.7854,1.926e-6,0.051391,2.8685e-7,0.55264,-1.8812e-4,0.061043,0.0012384,1.9853e-5,0.31124,0.24079,2.8179,0.018505,179062,0,0.011157,0.92653,0.94866,0.51063,0.29586,0.12004,0.66343,1.2692,-4.7889e-4,0,0,0,0,0.60064,0.025,4.9087e-4,1.0143,1.5799e-5,NaN,287.36,99899,339.91,0.05,0.043645 + 4.7846,122.55,2.7101,-9.7032,3.0478,9.7076,9.0212,-1.2375e-4,9.0212,-1.3718e-5,1.3944,0.29095,0.7854,2.0051e-6,0.19631,2.8642e-7,0.64636,-5.7697e-5,0.061043,0.0012384,1.9853e-5,0.29383,0.24079,2.1216,0.012842,124250,0,0.006732,0.97424,1.1888,0.55838,0.29584,0.12002,3.1059,4.8833,-1.0276e-4,0,0,0,0,1.7062,0.025,4.9087e-4,0.86617,1.6053e-5,NaN,287.35,99888,339.91,0.05,0.044528 + 5.0346,122.92,0.31154,-9.4602,1.352,9.4665,9.3602,-1.3207e-4,9.3602,-1.411e-5,1.3156,0.34494,0.7854,2.0804e-6,0.58599,2.8581e-7,0.74634,-1.1964e-5,0.061043,0.0012384,1.9853e-5,0.27157,0.24079,1.2313,0.0089351,86447,0,0.0028457,1.0286,1.0381,0.61271,0.29583,0.12001,10.824,-1.032,-2.0497e-4,0,0,0,0,4.6992,0.025,4.9087e-4,0.68875,1.054e-5,NaN,287.35,99883,339.9,0.0045557,0.045469 + 5.3019,122.68,-2.0169,-7.3852,2.757,8.1767,9.562,-1.3883e-4,9.562,-1.4519e-5,0.24881,3.5097,0.7854,2.1253e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.0081112,78477,0,0.26026,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8061,287.35,99885,339.91,0.061149,0.04614 + 5.5442,122.02,-3.3353,-4.4864,3.2621,5.2401,9.5332,-1.4282e-4,9.5332,-1.4981e-5,0.46379,2.7075,0.7854,2.1189e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.009597,92857,0,0.36436,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6852,287.35,99890,339.91,0.095419,0.046275 + 5.8456,120.89,-4.0348,-1.7851,3.8479,2.7994,9.2812,-1.4561e-4,9.2812,-1.5689e-5,1.1731,2.1564,0.7854,2.0629e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.01132,109537,0,0.507,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.787,287.36,99899,339.91,0.17861,0.046362 + 6.098,119.86,-4.1563,-0.48139,4.1195,1.981,8.9239,-1.4667e-4,8.9239,-1.6436e-5,1.6582,1.9216,0.7854,1.9835e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012119,117277,0,0.58116,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0046,287.36,99907,339.91,0.2524,0.046406 + 6.598,117.77,-4.1938,-0.074981,4.173,0.87754,7.9855,-1.4719e-4,7.9855,-1.8432e-5,2.0953,0.87433,0.7854,1.7749e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012277,118808,0,0.5964,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0316,287.37,99919,339.92,0.5,0.04645 + 7.098,115.68,-4.16,0.067435,4.1954,0.28499,6.9032,-1.471e-4,6.9032,-2.1309e-5,2.2338,0.2769,0.7854,1.5344e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012342,119465,0,0.60293,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2129,287.38,99944,339.93,0.5,0.046496 + 7.598,113.59,-4.1991,-0.078186,4.1662,0.53572,5.8526,-1.4712e-4,5.8526,-2.5137e-5,1.9688,0.52998,0.7854,1.3008e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012256,118653,0,0.59469,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0071,287.4,99968,339.93,0.5,0.046541 + 8.098,111.5,-4.1516,0.095112,4.2001,0.23522,4.8413,-1.4712e-4,4.8413,-3.0388e-5,2.0764,0.21513,0.7854,1.0761e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012355,119638,0,0.60453,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.06,287.41,99993,339.94,0.5,0.046587 + 8.598,109.41,-4.2075,-0.1119,4.1586,0.57491,3.7326,-1.4712e-4,3.7326,-3.9413e-5,2.3583,0.56392,0.7854,8.2964e-7,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012233,118474,0,0.59275,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3179,287.43,100017,339.95,0.5,0.046634 + 9.098,107.33,-4.1204,0.17425,4.2236,0.89004,2.6626,-1.4712e-4,2.6626,-5.5253e-5,1.9219,0.87281,0.7854,5.918e-7,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012424,120345,0,0.61154,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9903,287.44,100042,339.96,0.5,0.046725 + 9.598,105.24,-4.249,-0.25719,4.1256,0.54572,1.6415,-1.4712e-4,1.6415,-8.9625e-5,2.1626,0.48131,0.7854,3.6484e-7,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012135,117572,0,0.58362,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1296,287.45,100066,339.97,0.5,0.046774 + 10.098,103.16,-4.0789,0.34013,4.2502,0.41714,0.53001,-1.4712e-4,0.53001,-2.7757e-4,2.2833,0.24149,0.7854,1.178e-7,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012502,121142,0,0.61952,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2637,287.47,100091,339.97,0.5,0.04682 + 10.598,101.06,-4.3032,-0.4485,4.0825,0.59433,-0.5629,-1.4712e-4,0.5629,-3.1413,2.0883,0.38997,0.7854,-1.2511e-7,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012008,116380,0,0.5717,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1133,287.48,100115,339.98,0.5,0.046867 + 11.098,98.985,-4.0009,0.60453,4.3044,0.65151,-1.5767,-1.4712e-4,1.5767,-3.1415,1.9669,0.2429,0.7854,-3.5045e-7,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.01266,122724,0,0.63566,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9879,287.49,100140,339.99,0.5,0.046914 + 11.598,96.884,-4.4038,-0.8058,4.0016,0.82239,-2.5396,-1.4712e-4,2.5396,-3.1415,1.8847,0.16435,0.7854,-5.6446e-7,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011769,114110,0,0.54949,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8938,287.51,100164,340,0.5,0.04696 + 12.053,94.994,-3.904,1.0985,4.4038,1.0989,-3.3941,-1.4712e-4,3.3941,-3.1415,1.8712,0.02966,0.7854,-7.5438e-7,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012952,125601,0,0.66564,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8727,287.52,100189,340.01,0.455,0.047006 + 12.424,93.46,-4.3555,-1.2154,3.9128,1.346,-4.0492,-1.4712e-4,4.0492,-3.1416,1.6563,0.57852,0.7854,-9.0001e-7,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011508,111614,0,0.52558,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6083,287.53,100211,340.01,0.37147,0.047052 + 12.924,91.391,-3.9211,0.8688,4.3575,0.92891,-4.9185,-1.4712e-4,4.9185,-3.1416,1.8207,0.32874,0.7854,-1.0932e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012815,124313,0,0.65193,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7904,287.54,100229,340.02,0.5,0.047097 + 13.323,89.739,-4.3752,-1.1409,3.9283,1.256,-5.6849,-1.4712e-4,5.6849,-3.1416,2.0297,0.52517,0.7854,-1.2636e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011553,112084,0,0.52991,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0583,287.56,100253,340.03,0.3981,0.047145 + 13.823,87.672,-3.8929,0.96476,4.3753,0.96552,-6.7046,-1.4712e-4,6.7046,-3.1416,2.0489,0.038373,0.7854,-1.4902e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012867,124855,0,0.65748,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0453,287.57,100273,340.04,0.5,0.047192 + 14.212,86.06,-4.39,-1.2766,3.8934,1.284,-7.492,-1.4712e-4,7.492,-3.1416,1.9954,0.13733,0.7854,-1.6652e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.01145,111120,0,0.52073,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9862,287.58,100297,340.04,0.38942,0.047281 + 14.692,84.073,-3.8903,1.0413,4.39,1.0419,-8.4538,-1.4712e-4,8.4538,-3.1416,2.0127,0.035962,0.7854,-1.879e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.01291,125311,0,0.66215,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.01,287.59,100316,340.05,0.4799,0.047328 + 15.073,82.496,-4.3789,-1.2815,3.8924,1.3114,-9.2009,-1.4712e-4,9.2009,-3.1416,1.9065,0.27851,0.7854,-2.0451e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011446,111122,0,0.52064,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8856,287.6,100339,340.06,0.38126,0.047373 + 15.566,80.46,-3.8901,0.99211,4.3798,1.0149,-10.114,-1.4712e-4,10.114,-3.1416,1.8011,0.21401,0.7854,-2.248e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012879,125052,0,0.65928,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8197,287.61,100357,340.06,0.49264,0.047418 + 15.937,78.926,-4.363,-1.2727,3.8952,1.3458,-10.814,-1.4712e-4,10.814,-3.1416,1.9636,0.43749,0.7854,-2.4035e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011454,111235,0,0.52158,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0005,287.63,100381,340.07,0.37153,0.047464 + 16.437,76.86,-3.9039,0.91817,4.3642,0.95161,-11.764,-1.4712e-4,11.764,-3.1416,1.8386,0.25006,0.7854,-2.6148e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012833,124640,0,0.65481,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8619,287.64,100399,340.08,0.5,0.047507 + 16.84,75.189,-4.3939,-1.2171,3.9055,1.2418,-12.484,-1.4712e-4,12.484,-3.1416,1.7394,0.24645,0.7854,-2.7749e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011484,111559,0,0.52451,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7266,287.65,100424,340.09,0.40265,0.047552 + 17.279,73.365,-3.9209,1.0781,4.3965,1.1396,-13.283,-1.4712e-4,13.283,-3.1416,1.9014,0.36939,0.7854,-2.9524e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012927,125599,0,0.66478,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8885,287.66,100443,340.09,0.43875,0.047597 + 17.716,71.54,-4.4209,-1.1426,3.9209,1.1427,-14.117,-1.4712e-4,14.117,-3.1416,1.9085,0.016056,0.7854,-3.1377e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011529,112030,0,0.52884,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9087,287.67,100465,340.1,0.43755,0.047643 + 18.116,69.869,-3.9356,1.2135,4.4225,1.2504,-14.856,-1.4712e-4,14.856,-3.1416,1.7879,0.30154,0.7854,-3.3019e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.013003,126379,0,0.67292,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7875,287.68,100486,340.11,0.39986,0.047688 + 18.577,67.94,-4.4308,-1.0734,3.9362,1.0838,-15.696,-1.4712e-4,15.696,-3.1416,1.8572,0.15033,0.7854,-3.4888e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011573,112496,0,0.53315,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8556,287.7,100506,340.11,0.46133,0.047733 + 18.973,66.288,-3.9319,1.263,4.4309,1.2656,-16.437,-1.4712e-4,16.437,-3.1416,1.8895,0.081717,0.7854,-3.6533e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.013028,126653,0,0.6757,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.89,287.71,100528,340.12,0.39507,0.04778 + 19.389,64.559,-4.3778,-1.0717,3.9395,1.2017,-17.27,-1.4712e-4,17.27,-3.1416,2.1157,0.54366,0.7854,-3.8385e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011582,112620,0,0.5342,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1343,287.72,100548,340.13,0.41609,0.047825 + 19.833,62.715,-3.9276,1.0137,4.3822,1.1258,-18.161,-1.4712e-4,18.161,-3.1416,1.8982,0.48979,0.7854,-4.0366e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012884,125294,0,0.66114,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9176,287.73,100568,340.13,0.44413,0.047871 + 20.246,60.998,-4.3777,-1.0888,3.9347,1.2094,-18.901,-1.4712e-4,18.901,-3.1416,1.6806,0.52635,0.7854,-4.2011e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011568,112514,0,0.5331,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6611,287.74,100589,340.14,0.41344,0.047965 + 20.665,59.256,-3.9496,1.0236,4.3848,1.1953,-19.658,-1.4712e-4,19.658,-3.1416,1.9388,0.61731,0.7854,-4.3693e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012891,125402,0,0.66215,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9301,287.75,100610,340.15,0.41831,0.048014 + 21.144,57.248,-4.4283,-0.99892,3.9519,1.0433,-20.622,-1.4712e-4,20.622,-3.1416,2.083,0.30091,0.7854,-4.5835e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011618,113034,0,0.53793,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0738,287.76,100630,340.16,0.47927,0.048061 + 21.508,55.721,-3.9648,1.2732,4.4331,1.3734,-21.414,-1.4712e-4,21.414,-3.1416,2.2705,0.51498,0.7854,-4.7597e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.013032,126818,0,0.67704,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2889,287.78,100654,340.16,0.36406,0.048107 + 22.008,53.622,-4.4313,-0.93306,3.9648,0.93343,-22.546,-1.4712e-4,22.546,-3.1416,2.2575,0.026061,0.7854,-5.0113e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011655,113435,0,0.54164,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2589,287.79,100671,340.17,0.5,0.048153 + 22.37,52.102,-3.964,1.2909,4.4357,1.3812,-23.331,-1.4712e-4,23.331,-3.1416,2.0797,0.49109,0.7854,-5.1858e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.013039,126927,0,0.67806,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0614,287.8,100696,340.18,0.36201,0.048199 + 22.839,50.142,-4.394,-0.91706,3.9714,1.0663,-24.247,-1.4712e-4,24.247,-3.1416,1.8246,0.54399,0.7854,-5.3892e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011674,113655,0,0.54363,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8371,287.81,100714,340.18,0.46893,0.048245 + 23.248,48.438,-3.94,1.1098,4.3989,1.2222,-25.036,-1.4712e-4,25.036,-3.1416,2.0341,0.51197,0.7854,-5.5647e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012931,125907,0,0.66707,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0307,287.82,100737,340.19,0.40911,0.048292 + 23.726,46.435,-4.4356,-1.0366,3.9405,1.0458,-26.024,-1.4712e-4,26.024,-3.1416,2.1001,0.13812,0.7854,-5.7843e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011583,112802,0,0.53538,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0961,287.84,100757,340.2,0.47812,0.04834 + 24.07,44.989,-3.9802,1.3253,4.4421,1.4551,-26.781,-1.4712e-4,26.781,-3.1416,2.3065,0.60071,0.7854,-5.9526e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.013057,127180,0,0.68048,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3395,287.85,100780,340.21,0.34363,0.048386 + 24.377,43.73,-4.2083,-0.74161,4.0306,1.6256,-27.422,-1.4712e-4,27.422,-3.1416,1.8616,1.4466,0.7854,-6.0951e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011847,115411,0,0.56033,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6713,287.86,100797,340.21,0.30757,0.048432 + 24.874,41.668,-4.0848,0.24852,4.228,1.0055,-28.228,-1.4712e-4,28.228,-3.1416,1.3771,0.97434,0.7854,-6.2741e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012428,121077,0,0.61664,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.4538,287.87,100812,340.22,0.49725,0.04848 + 25.374,39.582,-4.2589,-0.34819,4.0966,0.80183,-29.006,-1.4712e-4,29.006,-3.1416,1.7382,0.72228,0.7854,-6.4472e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012041,117332,0,0.57902,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.689,287.88,100836,340.22,0.5,0.048527 + 25.874,37.509,-4.0339,0.45,4.26,0.50695,-29.905,-1.4712e-4,29.905,-3.1416,1.8549,0.23344,0.7854,-6.6468e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012521,122029,0,0.62623,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8352,287.89,100861,340.23,0.5,0.048573 + 26.374,35.417,-4.3345,-0.60126,4.0358,0.66529,-30.797,-1.4712e-4,30.797,-3.1416,1.7126,0.28479,0.7854,-6.8451e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011862,115625,0,0.56216,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7301,287.91,100885,340.24,0.5,0.04862 + 26.874,33.352,-3.9247,0.81956,4.3348,0.82859,-31.668,-1.4712e-4,31.668,-3.1416,1.7735,0.12196,0.7854,-7.0388e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.01274,124211,0,0.64867,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7623,287.92,100910,340.25,0.5,0.048666 + 27.3,31.583,-4.3853,-1.0815,3.9301,1.174,-32.465,-1.4712e-4,32.465,-3.1416,1.968,0.45661,0.7854,-7.2159e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.01155,112632,0,0.53331,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9789,287.93,100934,340.26,0.4259,0.048714 + 27.695,29.938,-3.9545,1.0925,4.393,1.268,-33.191,-1.4712e-4,33.191,-3.1416,1.7142,0.64361,0.7854,-7.3772e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.01291,125915,0,0.66644,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.709,287.94,100955,340.26,0.39434,0.04876 + 28.046,28.496,-4.2664,-0.88892,3.9853,1.4251,-33.861,-1.4712e-4,33.861,-3.1416,2.105,1.1139,0.7854,-7.5262e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011712,114243,0,0.54857,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2082,287.96,100974,340.27,0.35086,0.048807 + 28.546,26.426,-4.0157,0.50143,4.2693,0.62895,-34.961,-1.4712e-4,34.961,-3.1416,2.2949,0.37968,0.7854,-7.7706e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012547,122399,0,0.62963,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2622,287.96,100991,340.28,0.5,0.048856 + 29.046,24.333,-4.3538,-0.67615,4.0169,0.71151,-36.081,-1.4712e-4,36.081,-3.1416,2.1841,0.2215,0.7854,-8.0195e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011804,115180,0,0.55749,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1974,287.98,101015,340.28,0.5,0.048903 + 29.49,22.49,-3.9346,0.9422,4.3608,1.124,-37.113,-1.4712e-4,37.113,-3.1416,2.4567,0.61287,0.7854,-8.2489e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012815,125063,0,0.65718,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.4324,287.99,101040,340.29,0.44484,0.04895 + 29.971,20.479,-4.4346,-1.0404,3.9347,1.0404,-38.292,-1.4712e-4,38.292,-3.1416,2.4502,0.013642,0.7854,-8.511e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011562,112856,0,0.53509,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.4506,288,101061,340.3,0.48056,0.048998 + 30.193,19.529,-4.1105,1.459,4.4856,2.2507,-38.794,-1.4712e-4,38.794,-3.1416,2.0695,1.7137,0.7854,-8.6226e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.013181,128679,0,0.69557,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7756,288.02,101085,340.31,0.22216,0.049046 + 30.693,17.445,-4.2282,-0.23542,4.1111,0.28651,-39.808,-1.4712e-4,39.808,-3.1416,1.9878,0.16331,0.7854,-8.848e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.01208,117943,0,0.58431,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9993,288.02,101096,340.31,0.5,0.049092 + 31.193,15.371,-4.0665,0.32329,4.2291,0.38688,-40.776,-1.4712e-4,40.776,-3.1416,1.8816,0.2125,0.7854,-9.0631e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012427,121348,0,0.61847,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8991,288.04,101120,340.32,0.5,0.049139 + 31.693,13.284,-4.2829,-0.4327,4.0683,0.51154,-41.682,-1.4712e-4,41.682,-3.1416,1.7451,0.27286,0.7854,-9.2646e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011954,116751,0,0.57243,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7632,288.05,101145,340.33,0.5,0.049185 + 32.193,11.217,-3.9852,0.59533,4.2856,0.69991,-42.601,-1.4712e-4,42.601,-3.1416,1.9292,0.36803,0.7854,-9.4687e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012592,123007,0,0.63534,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8967,288.06,101169,340.34,0.5,0.049231 + 32.693,9.1251,-4.3814,-0.79239,3.9904,0.91681,-43.508,-1.4712e-4,43.508,-3.1416,1.6986,0.46115,0.7854,-9.6703e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011725,114553,0,0.55095,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7253,288.08,101193,340.34,0.5,0.049278 + 33.023,7.7402,-4.0105,1.1237,4.4003,1.5149,-44.124,-1.4712e-4,44.124,-3.1416,2.0339,1.0159,0.7854,-9.8072e-6,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012929,126339,0,0.67007,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1058,288.09,101218,340.35,0.33006,0.049325 + 33.523,5.6509,-4.3466,-0.67215,4.0169,0.846,-45.077,-1.4712e-4,45.077,-3.1416,1.777,0.51375,0.7854,-1.0019e-5,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011802,115342,0,0.55845,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8083,288.1,101234,340.36,0.5,0.049375 + 33.985,3.7422,-3.9204,0.92289,4.3526,1.0828,-45.957,-1.4712e-4,45.957,-3.1416,2.0385,0.56628,0.7854,-1.0215e-5,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012788,125004,0,0.65585,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0064,288.11,101259,340.37,0.46178,0.04942 + 34.43,1.8893,-4.4028,-1.0835,3.9227,1.123,-46.894,-1.4712e-4,46.894,-3.1416,2.1701,0.29548,0.7854,-1.0423e-5,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011525,112672,0,0.53277,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1713,288.13,101281,340.37,0.44522,0.049467 + 34.85,0.14747,-3.9033,1.1909,4.4029,1.1921,-47.8,-1.4712e-4,47.8,-3.1416,2.147,0.055056,0.7854,-1.0624e-5,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.012935,126482,0,0.67131,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.148,288.14,101303,340.38,0.41941,0.049514 + 35.188,-1.2377,-4.2782,-1.107,3.9279,1.4766,-48.471,-1.4712e-4,48.471,-3.1416,1.8161,0.97718,0.7854,-1.0773e-5,NaN,NaN,NaN,NaN,0.061043,NaN,NaN,NaN,NaN,NaN,0.011539,112852,0,0.53437,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7085,288.15,101323,340.39,0.33861,0.049562 + + + + + + Simulation 3 - too short delay + RK4Simulator + BarrowmanCalculator + + b13e8ca2-f54d-4ca8-9b23-06c6c9eb237a + 1.0 + 0.0 + 0.0 + 2.0 + 0.1 + 0.0 + 45.0 + 0.0 + flat + + 0.05 + + + Recovery device deployment at high speed (24.1 m/s). + + + + + + + + + + + + 0,0,0,-7.6644,0,7.6644,0,0,0,0,0,0,0.7854,0,1.5708,0,0,0,0.07124,0.0013955,2.0267e-5,NaN,0.25972,NaN,0.0061034,59690,0.15258,0,1.0926,0,0.67679,0.29583,0.12,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,0.025,4.9087e-4,1.5708,0,NaN,288.15,101325,340.39,0.01,7.5813e-4 + 0.25344,1.9531,22.409,74.704,22.409,74.725,-0.0023113,0,0.0023113,3.1416,0.096789,1.7815,0.7854,0,0.077133,0,0.2605,1.9751e-6,0.069152,0.001367,2.0182e-5,0.30755,0.25629,2.0503,0.066063,645973,5.9797,0.13621,0.85324,0.89614,0.43637,0.2963,0.12057,1.0366,2.1148,1.8073e-5,0,0,0,0,0.010443,0.025,4.9087e-4,1.5649,1.3793e-5,NaN,288.14,101302,340.38,0.011875,0.021574 + 0.50343,9.5376,37.228,49.441,37.245,50.365,0.056379,-1.7839e-6,0.056379,-3.1642e-5,1.1389,9.6015,0.7854,1.2531e-8,0.03196,5.2577e-9,0.078989,2.3233e-5,0.067603,0.0013448,2.0119e-5,0.31432,0.25362,2.4279,0.1098,1072920,4.416,0.36205,0.85475,0.86304,0.43603,0.29715,0.12157,0.40027,0.97129,-1.8922e-4,0,0,0,0,3.4765e-4,0.025,4.9087e-4,1.4515,1.1881e-5,NaN,288.09,101213,340.35,0.0092163,0.024527 + 0.74357,19.866,48.638,44.547,48.689,44.771,0.49749,5.0787e-6,0.49749,1.0209e-5,2.2144,4.4694,0.7854,1.1057e-7,7.5378e-4,1.659e-8,0.36685,-0.0011451,0.066348,0.001326,2.0068e-5,0.31965,0.25136,2.7316,0.14344,1400359,4.2412,0.6124,0.85643,0.85643,0.43566,0.29809,0.12267,0.0089649,0.02019,1.5998e-4,0,0,0,0,0.004395,0.025,4.9087e-4,1.487,4.6795e-5,NaN,288.02,101092,340.31,0.027886,0.041743 + 1.0008,33.869,60.18,42.944,60.289,43.128,1.2686,4.4302e-5,1.2686,3.4922e-5,3.6127,3.9804,0.7854,2.8196e-7,0.0044337,1.592e-7,0.18608,-3.0648e-4,0.064967,0.0013045,2.0012e-5,0.31905,0.24878,2.811,0.1777,1732740,4.3757,0.94092,0.8586,0.85877,0.43518,0.29931,0.1241,0.053117,0.14824,-4.0961e-4,0,0,0,0,7.3705e-4,0.025,4.9087e-4,1.4785,4.4631e-5,NaN,287.93,100928,340.26,0.02243,0.051559 + 1.2548,50.482,70.32,34.746,70.488,35.276,2.3588,1.2827e-4,2.3588,5.4382e-5,4.8581,6.0965,0.7854,5.2428e-7,0.0049966,2.4176e-7,-0.17993,-1.1216e-4,0.063638,0.001283,1.9959e-5,0.31901,0.24618,2.9131,0.20774,2022738,4.13,1.2869,0.86088,0.8611,0.43468,0.30059,0.12561,0.05998,0.17482,2.2201e-4,0,0,0,0,-5.0438e-4,0.025,4.9087e-4,1.4661,1.7645e-5,NaN,287.82,100733,340.19,0.013713,0.054025 + 1.5142,69.902,79.378,34.047,79.614,34.255,3.7889,2.3716e-4,3.7889,6.2593e-5,6.1303,3.7747,0.7854,8.4215e-7,0.0017654,3.8514e-7,-0.11076,1.2464e-4,0.062277,0.0012601,1.9903e-5,0.31964,0.24341,3.0492,0.23461,2280514,4.3836,1.6418,0.86322,0.86325,0.43416,0.30191,0.12716,0.021097,0.064487,-1.6805e-6,0,0,0,0,-1.4993e-4,0.025,4.9087e-4,1.4703,9.4934e-5,NaN,287.7,100505,340.11,0.028691,0.065314 + 1.7815,92.293,87.984,29.809,88.283,30.179,5.575,3.8475e-4,5.575,6.9014e-5,7.2698,4.71,0.7854,1.2391e-6,0.0010301,4.055e-7,0.012624,-2.9848e-4,0.060866,0.0012352,1.9846e-5,0.31984,0.24041,3.1771,0.2602,2524279,4.448,2.02,0.86572,0.86573,0.4336,0.30332,0.1288,0.012309,0.038711,-1.266e-4,0,0,0,0,1.5841e-6,0.025,4.9087e-4,1.4643,-1.6033e-6,NaN,287.55,100243,340.03,0.03819,0.067386 + 2.0432,114.83,81.364,-38.056,81.651,38.216,7.4616,5.0009e-4,7.4616,6.7022e-5,6.8464,3.4923,0.7854,1.6585e-6,0.0012995,9.8313e-7,-0.028929,7.3822e-4,0.060443,0.0012276,1.9829e-5,0.31974,0.23948,3.2104,0.24071,2330537,0,1.7202,0.8638,0.86381,0.43403,0.30223,0.12753,0.015522,0.04966,-1.5488e-4,0,0,0,0,-9.7258e-6,0.025,4.9087e-4,1.4658,1.2834e-4,NaN,287.4,99978,339.94,0.034663,0.068746 + 2.2932,134.05,72.605,-32.264,72.863,32.359,9.0806,5.9739e-4,9.0806,6.5788e-5,6.1312,2.4755,0.7854,2.0183e-6,2.5905e-4,8.606e-7,-0.064121,-2.9501e-4,0.060443,0.0012276,1.9829e-5,0.31987,0.23948,3.2155,0.215,2078130,0,1.3657,0.86149,0.86149,0.43454,0.30093,0.12601,0.0030853,0.010325,2.7431e-4,0,0,0,0,-5.9919e-5,0.025,4.9087e-4,1.4574,4.9544e-5,NaN,287.28,99753,339.86,0.05,0.069945 + 2.5411,151.1,65.179,-27.862,65.416,27.932,10.529,6.7793e-4,10.529,6.4384e-5,5.5703,1.9832,0.7854,2.3403e-6,6.8963e-4,1.1874e-6,-0.0031851,-4.8694e-4,0.060443,0.0012276,1.9829e-5,0.31975,0.23948,3.2106,0.19317,1864246,0,1.0979,0.85973,0.85973,0.43493,0.29995,0.12485,0.0082132,0.026001,-1.865e-4,0,0,0,0,-1.8323e-7,0.025,4.9087e-4,1.4518,-2.9439e-5,NaN,287.17,99553,339.79,0.05,0.076962 + 2.79,166.5,58.696,-24.385,58.916,24.457,11.853,7.5988e-4,11.853,6.411e-5,5.0886,1.8794,0.7854,2.6345e-6,4.662e-4,1.0316e-6,-0.016065,-5.9947e-4,0.060443,0.0012276,1.9829e-5,0.31975,0.23948,3.2109,0.17407,1677632,0,0.88847,0.85835,0.85835,0.43524,0.29917,0.12394,0.0055467,0.018279,-2.3241e-4,0,0,0,0,-5.7421e-6,0.025,4.9087e-4,1.4491,3.8441e-7,NaN,287.07,99372,339.73,0.05,0.077837 + 3.04,180.44,52.952,-21.621,53.159,21.688,13.071,8.3915e-4,13.071,6.4198e-5,4.6892,1.6979,0.7854,2.9053e-6,0.0015313,8.438e-7,-0.053254,-2.6717e-4,0.060443,0.0012276,1.9829e-5,0.31953,0.23948,3.2021,0.15707,1511959,0,0.72134,0.85724,0.85726,0.43548,0.29855,0.12321,0.018243,0.058783,3.7574e-4,0,0,0,0,-7.7519e-5,0.025,4.9087e-4,1.4494,5.4092e-6,NaN,286.98,99209,339.68,0.05,0.07869 + 3.2875,192.91,47.875,-19.446,48.07,19.504,14.185,9.2116e-4,14.185,6.4938e-5,4.3352,1.4924,0.7854,3.1529e-6,0.0017782,8.4463e-7,0.040222,3.5135e-4,0.060443,0.0012276,1.9829e-5,0.31947,0.23948,3.1995,0.14217,1366994,0,0.58949,0.85636,0.85639,0.43568,0.29805,0.12263,0.021186,0.067508,4.8137e-4,0,0,0,0,5.3993e-5,0.025,4.9087e-4,1.442,1.3819e-5,NaN,286.9,99063,339.63,0.05,0.079546 + 3.5375,204.29,43.235,-17.676,43.425,17.711,15.232,0.0010234,15.232,6.7189e-5,4.0564,1.1142,0.7854,3.3856e-6,3.9346e-4,8.8386e-7,-0.024041,-2.1248e-4,0.060443,0.0012276,1.9829e-5,0.3197,0.23948,3.2087,0.1285,1234246,0,0.48046,0.85563,0.85563,0.43584,0.29764,0.12215,0.0046749,0.014529,3.9364e-4,0,0,0,0,-2.3621e-5,0.025,4.9087e-4,1.4356,2.9968e-5,NaN,286.82,98929,339.58,0.05,0.080441 + 3.7875,214.56,39.005,-16.205,39.19,16.234,16.211,0.00112,16.211,6.909e-5,3.8008,0.98274,0.7854,3.6031e-6,6.6332e-4,9.0949e-7,0.0044258,8.339e-5,0.060443,0.0012276,1.9829e-5,0.31963,0.23948,3.2062,0.11607,1113891,0,0.39129,0.85503,0.85504,0.43597,0.29731,0.12175,0.0078829,0.024927,-1.7345e-4,0,0,0,0,9.8127e-7,0.025,4.9087e-4,1.4276,1.0391e-4,NaN,286.76,98809,339.54,0.05,0.081295 + 4.0375,223.82,35.107,-15.015,35.291,15.028,17.132,0.0012144,17.132,7.0887e-5,3.5939,0.62871,0.7854,3.8078e-6,0.0022187,9.138e-7,0.029882,1.8121e-4,0.060443,0.0012276,1.9829e-5,0.31934,0.23948,3.1946,0.10456,1002593,0,0.31701,0.85453,0.85457,0.43608,0.29703,0.12142,0.026432,0.084782,-3.3343e-4,0,0,0,0,5.5136e-5,0.025,4.9087e-4,1.4198,5.014e-5,NaN,286.7,98700,339.51,0.05,0.082123 + 4.296,232.41,31.379,-13.981,31.557,13.992,18.029,0.0013109,18.029,7.2711e-5,3.3434,0.57017,0.7854,4.0073e-6,0.0025584,8.4968e-7,-0.22264,4.6678e-4,0.060443,0.0012276,1.9829e-5,0.31927,0.23948,3.1917,0.093767,898391,0,0.25455,0.85411,0.85417,0.43618,0.29679,0.12114,0.030491,0.10079,-2.3253e-4,0,0,0,0,-0.0038068,0.025,4.9087e-4,1.3994,3.3713e-5,NaN,286.64,98599,339.47,0.05,0.083133 + 4.5638,240.32,27.754,-13.037,27.94,13.059,18.91,0.0014103,18.91,7.4578e-5,3.2152,0.75849,0.7854,4.2031e-6,0.0031613,8.3573e-7,-0.0031917,3.1234e-5,0.060443,0.0012276,1.9829e-5,0.31916,0.23948,3.1871,0.083257,797118,0,0.20041,0.85374,0.85383,0.43626,0.29659,0.1209,0.03771,0.11996,4.8813e-4,0,0,0,0,-9.9249e-7,0.025,4.9087e-4,1.3844,1.7564e-4,NaN,286.59,98506,339.44,0.05,0.092296 + 4.8138,246.86,24.583,-12.376,24.772,12.384,19.693,0.00152,19.693,7.7186e-5,3.0529,0.43173,0.7854,4.3771e-6,0.0023038,8.4463e-7,-0.089079,4.328e-4,0.060443,0.0012276,1.9829e-5,0.3193,0.23948,3.1929,0.073845,706594,0,0.15748,0.85345,0.8535,0.43632,0.29642,0.12071,0.027436,0.088144,1.8982e-4,0,0,0,0,-9.8286e-4,0.025,4.9087e-4,1.3723,3.0264e-5,NaN,286.55,98430,339.42,0.05,0.093199 + 5.0576,250.36,5.6825,-31.704,6.3359,32.097,19.987,0.0015796,19.987,7.9032e-5,0.38037,5.0083,0.7854,4.4425e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.018668,178569,0,1.3578,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7149,286.52,98390,339.4,0.015578,0.095445 + 5.3184,251.16,1.2148,-11.566,1.8357,11.586,19.799,0.0016014,19.799,8.0887e-5,0.8991,0.67504,0.7854,4.4006e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.0054086,51733,0,0.11397,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.5272,286.52,98380,339.4,0.043157,0.095865 + 5.5689,251.14,-1.2833,-9.3777,0.97558,9.383,19.56,0.0016171,19.56,8.2673e-5,0.99125,0.31721,0.7854,4.3475e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.0028744,27493,0,0.032189,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.5554,286.52,98379,339.4,0.053288,0.096544 + 5.8529,250.47,-3.2725,-5.389,2.8395,5.4672,19.255,0.0016314,19.255,8.4726e-5,1.1808,0.92145,0.7854,4.2797e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.0083661,80024,0,0.27269,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6765,286.52,98384,339.4,0.091455,0.096727 + 6.2093,249.11,-4.1688,-1.8245,3.7987,2.201,18.763,0.0016413,18.763,8.7474e-5,1.5939,1.231,0.7854,4.1704e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011192,107064,0,0.4881,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8934,286.52,98393,339.4,0.22717,0.096815 + 6.7093,247.02,-4.1954,-0.05323,4.18,0.71572,17.877,0.0016446,17.877,9.1995e-5,1.9508,0.71374,0.7854,3.9735e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012315,117817,0,0.59103,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8991,286.53,98403,339.41,0.5,0.09686 + 7.2093,244.93,-4.1716,0.047579,4.1955,0.085393,16.893,0.001644,16.893,9.7322e-5,1.9863,0.070909,0.7854,3.7547e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012361,118276,0,0.59556,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.981,286.54,98428,339.42,0.5,0.096906 + 7.7093,242.84,-4.2025,-0.061812,4.1718,0.10083,15.89,0.0016441,15.89,1.0347e-4,2.0261,0.079665,0.7854,3.5318e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012291,117625,0,0.58896,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0204,286.56,98452,339.42,0.5,0.096952 + 8.2093,240.75,-4.1533,0.098499,4.2084,0.53148,14.942,0.0016441,14.942,1.1003e-4,1.765,0.52228,0.7854,3.3211e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012398,118677,0,0.59946,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8045,286.57,98477,339.43,0.5,0.097697 + 8.7093,238.66,-4.2171,-0.12755,4.1602,0.5751,13.989,0.0016441,13.989,1.1753e-4,2.0454,0.56077,0.7854,3.1094e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012256,117339,0,0.58595,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0056,286.59,98501,339.44,0.5,0.097805 + 9.2093,236.57,-4.1383,0.15759,4.2172,0.17876,12.956,0.0016441,12.956,1.269e-4,2.0875,0.08437,0.7854,2.8797e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012424,118965,0,0.60222,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0811,286.6,98526,339.45,0.5,0.097853 + 9.7093,234.47,-4.2287,-0.18081,4.1507,0.76866,12.006,0.0016441,12.006,1.3694e-4,1.714,0.7471,0.7854,2.6685e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012227,117108,0,0.5835,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7663,286.61,98550,339.46,0.5,0.097898 + 10.209,232.39,-4.111,0.23528,4.2367,0.66335,11.071,0.0016441,11.071,1.485e-4,2.0241,0.62022,0.7854,2.4608e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012481,119555,0,0.60805,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9752,286.63,98575,339.47,0.5,0.097943 + 10.709,230.29,-4.2768,-0.33149,4.1111,0.3393,10.05,0.0016441,10.05,1.6359e-4,2.0603,0.072377,0.7854,2.2338e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01211,116030,0,0.57266,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0555,286.64,98599,339.47,0.5,0.097989 + 11.209,228.21,-4.0496,0.45425,4.2787,0.54939,9.0586,0.0016441,9.0586,1.815e-4,1.9058,0.309,0.7854,2.0134e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012604,120779,0,0.62041,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9315,286.65,98624,339.48,0.5,0.098034 + 11.709,226.11,-4.3481,-0.59685,4.0549,0.76033,8.0468,0.0016441,8.0468,2.0432e-4,2.1413,0.47104,0.7854,1.7885e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011944,114481,0,0.55733,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1129,286.67,98648,339.49,0.5,0.098079 + 12.209,224.04,-3.9448,0.8066,4.3513,0.90552,7.0276,0.0016441,7.0276,2.3395e-4,1.9355,0.41153,0.7854,1.562e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012817,122869,0,0.64191,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9727,286.68,98673,339.5,0.5,0.098124 + 12.672,222.1,-4.4423,-1.0749,3.9451,1.0802,6.1203,0.0016441,6.1203,2.6864e-4,1.9848,0.10644,0.7854,1.3603e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01162,111415,0,0.52775,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9836,286.69,98697,339.51,0.46289,0.098169 + 13.066,220.45,-3.9432,1.2679,4.4424,1.2701,5.3447,0.0016441,5.3447,3.0762e-4,1.9554,0.074737,0.7854,1.188e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013085,125481,0,0.66933,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9548,286.71,98720,339.51,0.39366,0.098214 + 13.499,218.64,-4.4058,-1.0685,3.9482,1.1549,4.5392,0.0016441,4.5392,3.6221e-4,1.7657,0.43812,0.7854,1.0089e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011629,111534,0,0.52876,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7577,286.72,98740,339.52,0.43295,0.098259 + 13.947,216.78,-3.9165,1.0924,4.4068,1.1163,3.7253,0.0016441,3.7253,4.4134e-4,1.8686,0.22979,0.7854,8.2801e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012979,124508,0,0.65886,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8586,286.73,98761,339.53,0.44789,0.098304 + 14.362,215.05,-4.4113,-1.1923,3.9173,1.205,2.9349,0.0016441,2.9349,5.6019e-4,1.9408,0.17396,0.7854,6.5234e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011537,110695,0,0.52072,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9477,286.74,98783,339.54,0.41495,0.098349 + 14.805,213.2,-3.9142,1.1215,4.4116,1.1281,2.0628,0.0016441,2.0628,7.9705e-4,1.9946,0.12141,0.7854,4.5848e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012993,124678,0,0.66051,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9898,286.75,98803,339.54,0.44324,0.098395 + 15.219,211.48,-4.4115,-1.2005,3.9146,1.2071,1.2473,0.0016441,1.2473,0.0013181,1.9425,0.12586,0.7854,2.7724e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011529,110649,0,0.52017,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9374,286.76,98824,339.55,0.41422,0.098439 + 15.647,209.69,-3.9278,1.1295,4.4131,1.1675,0.38833,0.0016441,0.38833,0.0042338,2.0691,0.29571,0.7854,8.6313e-8,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012997,124756,0,0.66119,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0618,286.78,98845,339.56,0.42826,0.098484 + 16.073,207.92,-4.4098,-1.1331,3.9303,1.1754,-0.4636,0.0016441,0.4636,3.138,1.9362,0.31244,0.7854,-1.0304e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011575,111124,0,0.52453,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9276,286.79,98866,339.56,0.42539,0.09853 + 16.508,206.11,-3.9208,1.1235,4.4108,1.1488,-1.3291,0.0016441,1.3291,3.1404,2.0406,0.23986,0.7854,-2.954e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01299,124727,0,0.66075,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.033,286.8,98886,339.57,0.43525,0.098576 + 16.882,204.56,-4.3461,-1.1381,3.9337,1.338,-2.1407,0.0016441,2.1407,3.1408,2.3036,0.70365,0.7854,-4.7581e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011584,111250,0,0.52562,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3589,286.81,98908,339.58,0.37369,0.098622 + 17.382,202.49,-3.9364,0.81933,4.3496,0.92306,-3.3457,0.0016441,3.3457,3.1411,2.5161,0.42513,0.7854,-7.4363e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012809,123026,0,0.64272,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.4775,286.82,98926,339.58,0.5,0.098667 + 17.815,200.68,-4.4071,-1.0859,3.9403,1.1535,-4.3997,0.0016441,4.3997,3.1412,2.3474,0.38921,0.7854,-9.7791e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011603,111469,0,0.52757,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3404,286.83,98950,339.59,0.43346,0.098712 + 18.25,198.87,-3.9207,1.1183,4.4084,1.1496,-5.3955,0.0016441,5.3955,3.1413,2.2315,0.26658,0.7854,-1.1992e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012981,124728,0,0.66047,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2399,286.85,98971,339.6,0.43492,0.098757 + 18.68,197.08,-4.4198,-1.1606,3.9209,1.1628,-6.3616,0.0016441,6.3616,3.1413,2.2622,0.071417,0.7854,-1.414e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011545,110949,0,0.52255,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2639,286.86,98993,339.61,0.43001,0.098802 + 19.034,195.59,-3.993,1.2063,4.4297,1.4134,-7.1158,0.0016441,7.1158,3.1414,2.0016,0.7365,0.7854,-1.5816e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013043,125365,0,0.66708,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9666,286.87,99014,339.61,0.35376,0.098846 + 19.534,193.49,-4.4092,-0.83228,3.9948,0.87432,-8.1501,0.0016441,8.1501,3.1414,2.1356,0.26785,0.7854,-1.8115e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011763,113071,0,0.54262,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1208,286.88,99031,339.62,0.5,0.098891 + 19.967,191.69,-3.9178,1.1349,4.41,1.1548,-9.0948,0.0016441,9.0948,3.1414,2.2279,0.21326,0.7854,-2.0215e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012985,124843,0,0.66141,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2215,286.89,99056,339.63,0.43298,0.098937 + 20.395,189.9,-4.4173,-1.1678,3.9178,1.1689,-10.043,0.0016441,10.043,3.1414,2.2068,0.049374,0.7854,-2.2323e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011535,110926,0,0.5221,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2055,286.9,99077,339.63,0.42776,0.098982 + 20.819,188.14,-3.9182,1.1773,4.4174,1.1793,-10.973,0.0016441,10.973,3.1414,2.1779,0.068126,0.7854,-2.4389e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013006,125088,0,0.66386,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1794,286.92,99098,339.64,0.42399,0.099026 + 21.247,186.35,-4.4156,-1.1625,3.9185,1.1684,-11.915,0.0016441,11.915,3.1415,2.2279,0.11689,0.7854,-2.6484e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011537,110976,0,0.52246,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2309,286.93,99118,339.65,0.42793,0.099071 + 21.667,184.6,-3.9215,1.174,4.4162,1.1878,-12.869,0.0016441,12.869,3.1415,2.304,0.18065,0.7854,-2.8604e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013002,125089,0,0.66373,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3006,286.94,99139,339.65,0.42095,0.099116 + 22.094,182.82,-4.4088,-1.1419,3.9233,1.1716,-13.829,0.0016441,13.829,3.1415,2.192,0.26236,0.7854,-3.0737e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01155,111141,0,0.52391,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1852,286.95,99160,339.66,0.42676,0.099161 + 22.461,181.28,-3.9817,1.1648,4.418,1.3637,-14.585,0.0016441,14.585,3.1415,1.932,0.70914,0.7854,-3.2417e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013007,125172,0,0.66448,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.907,286.96,99181,339.67,0.36665,0.099206 + 22.961,179.18,-4.4192,-0.87499,3.9817,0.87499,-15.551,0.0016441,15.551,3.1415,1.9317,6.317e-4,0.7854,-3.4564e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011722,112825,0,0.5398,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9317,286.97,99199,339.67,0.5,0.099251 + 23.378,177.44,-3.9192,1.1976,4.4192,1.1977,-16.355,0.0016441,16.355,3.1415,1.9237,0.019189,0.7854,-3.6352e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01301,125242,0,0.66507,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.924,286.99,99223,339.68,0.41745,0.099297 + 23.812,175.64,-4.417,-1.149,3.9196,1.1543,-17.178,0.0016441,17.178,3.1415,1.8762,0.1096,0.7854,-3.8182e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011539,111098,0,0.52328,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8741,287,99244,339.69,0.43318,0.099341 + 24.231,173.89,-3.9174,1.1905,4.417,1.1916,-17.961,0.0016441,17.961,3.1415,1.8548,0.05103,0.7854,-3.9922e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013003,125216,0,0.66465,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8557,287.01,99265,339.7,0.41962,0.099385 + 24.662,172.09,-4.4149,-1.154,3.9178,1.1599,-18.772,0.0016441,18.772,3.1415,1.9052,0.11697,0.7854,-4.1723e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011533,111078,0,0.52299,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9077,287.02,99286,339.7,0.43108,0.09943 + 25.008,170.64,-3.9959,1.2118,4.4262,1.4463,-19.477,0.0016441,19.477,3.1415,2.1781,0.78939,0.7854,-4.3292e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013029,125510,0,0.66764,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2215,287.03,99307,339.71,0.34572,0.099476 + 25.508,168.54,-4.3962,-0.80047,3.9965,0.81533,-20.547,0.0016441,20.547,3.1415,2.1006,0.15495,0.7854,-4.5669e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011764,113339,0,0.54438,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1094,287.04,99324,339.72,0.5,0.099521 + 25.89,166.94,-3.97,1.1158,4.4048,1.309,-21.3,0.0016441,21.3,3.1415,1.8392,0.68457,0.7854,-4.7342e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012966,124938,0,0.66142,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8251,287.05,99348,339.73,0.38196,0.099566 + 26.39,164.84,-4.4257,-0.9115,3.9717,0.94703,-22.187,0.0016441,22.187,3.1415,1.7107,0.25698,0.7854,-4.9314e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011691,112666,0,0.53782,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7245,287.06,99367,339.73,0.5,0.099611 + 26.772,163.24,-3.9472,1.2519,4.4283,1.3079,-22.869,0.0016441,22.869,3.1415,1.8554,0.3787,0.7854,-5.0829e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013035,125641,0,0.66875,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8622,287.08,99392,339.74,0.38229,0.099656 + 27.257,161.21,-4.4378,-1.0118,3.9482,1.0311,-23.745,0.0016441,23.745,3.1415,1.7592,0.19857,0.7854,-5.2777e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011621,112032,0,0.53167,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7663,287.09,99410,339.75,0.4849,0.09973 + 27.603,159.75,-3.9793,1.3244,4.4437,1.4442,-24.389,0.0016441,24.389,3.1415,1.9586,0.57594,0.7854,-5.4208e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013079,126114,0,0.67364,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9888,287.1,99434,339.75,0.34621,0.099776 + 28.103,157.66,-4.413,-0.8674,3.9793,0.86792,-25.364,0.0016441,25.364,3.1415,1.9436,0.02997,0.7854,-5.6376e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011712,112946,0,0.54026,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9452,287.11,99451,339.76,0.5,0.099821 + 28.494,156.02,-3.9445,1.1983,4.4166,1.2791,-26.09,0.0016441,26.09,3.1415,1.7688,0.44718,0.7854,-5.7989e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012999,125379,0,0.66567,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7642,287.13,99476,339.77,0.39091,0.099866 + 28.982,153.98,-4.4414,-1.019,3.9449,1.0254,-26.939,0.0016441,26.939,3.1415,1.7132,0.11402,0.7854,-5.9876e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01161,112001,0,0.53114,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7176,287.14,99495,339.77,0.48763,0.099911 + 29.29,152.67,-4.019,1.3689,4.4548,1.6202,-27.509,0.0016441,27.509,3.1415,1.9807,0.86681,0.7854,-6.1142e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013111,126498,0,0.67746,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0577,287.15,99519,339.78,0.3086,0.099957 + 29.79,150.58,-4.3596,-0.68121,4.0193,0.69128,-28.484,0.0016441,28.484,3.1415,1.9219,0.11758,0.7854,-6.3311e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011829,114145,0,0.55156,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9289,287.16,99534,339.79,0.5,0.1 + 30.225,148.77,-3.946,0.95151,4.3675,1.1503,-29.381,0.0016441,29.381,3.1415,2.2028,0.64632,0.7854,-6.5303e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012853,124051,0,0.65138,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1838,287.17,99559,339.8,0.43469,0.10004 + 30.631,147.09,-4.3414,-0.97506,3.9604,1.2332,-30.212,0.0016441,30.212,3.1415,1.8967,0.75498,0.7854,-6.7151e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011655,112505,0,0.5357,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8655,287.18,99580,339.8,0.40546,0.10009 + 31.131,145.03,-3.9184,0.8459,4.3414,0.84611,-31.163,0.0016441,31.163,3.1415,1.9062,0.019059,0.7854,-6.9264e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012776,123343,0,0.64382,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9045,287.19,99600,339.81,0.5,0.10013 + 31.565,143.22,-4.406,-1.123,3.9201,1.1515,-31.966,0.0016441,31.966,3.1415,1.7957,0.25454,0.7854,-7.105e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011536,111392,0,0.52504,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7914,287.21,99624,339.82,0.43421,0.10018 + 31.968,141.54,-3.9311,1.1765,4.4088,1.2387,-32.723,0.0016441,32.723,3.1415,1.9522,0.38761,0.7854,-7.2731e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012974,125295,0,0.66422,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9512,287.22,99645,339.82,0.40365,0.10023 + 32.427,139.62,-4.4188,-1.064,3.9326,1.0909,-33.592,0.0016441,33.592,3.1415,1.8418,0.24081,0.7854,-7.4664e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011572,111778,0,0.52858,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8439,287.23,99665,339.83,0.45832,0.10034 + 32.804,138.04,-3.9482,1.2462,4.4224,1.324,-34.256,0.0016441,34.256,3.1415,1.6729,0.44711,0.7854,-7.6139e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013013,125718,0,0.66856,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.663,287.24,99687,339.84,0.37765,0.10039 + 33.297,135.98,-4.4332,-0.98505,3.9497,1.0155,-35.109,0.0016441,35.109,3.1415,1.7945,0.24689,0.7854,-7.8036e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011622,112294,0,0.53336,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7835,287.25,99706,339.85,0.49236,0.10043 + 33.677,134.39,-3.9335,1.313,4.4332,1.3138,-35.795,0.0016441,35.795,3.1415,1.8113,0.044144,0.7854,-7.9561e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013045,126060,0,0.67206,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8121,287.27,99730,339.85,0.38058,0.10048 + 34.151,132.41,-4.4305,-1.0504,3.9338,1.0565,-36.64,0.0016441,36.64,3.1415,1.7573,0.11408,0.7854,-8.1438e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011575,111873,0,0.52925,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.76,287.28,99749,339.86,0.47325,0.10053 + 34.53,130.82,-3.9355,1.305,4.4312,1.318,-37.293,0.0016441,37.293,3.1415,1.6872,0.18484,0.7854,-8.289e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013038,126036,0,0.67167,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6836,287.29,99772,339.87,0.37935,0.10057 + 35.008,128.82,-4.4324,-1.0384,3.9358,1.0449,-38.114,0.0016441,38.114,3.1415,1.7431,0.11681,0.7854,-8.4714e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01158,111961,0,0.52998,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7396,287.3,99791,339.87,0.4785,0.10062 + 35.314,127.53,-4.0183,1.3538,4.4473,1.6349,-38.69,0.0016441,38.69,3.1416,2.0234,0.91661,0.7854,-8.5995e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013085,126530,0,0.67679,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1071,287.31,99814,339.88,0.30583,0.10066 + 35.814,125.44,-4.3471,-0.65746,4.0209,0.73495,-39.661,0.0016441,39.661,3.1416,1.8592,0.32847,0.7854,-8.8152e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01183,114411,0,0.55331,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8791,287.32,99829,339.89,0.5,0.10071 + 36.291,123.47,-3.914,0.9086,4.3523,1.0489,-40.606,0.0016441,40.606,3.1416,2.109,0.52414,0.7854,-9.0254e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012805,123858,0,0.64839,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0718,287.33,99854,339.89,0.47667,0.10075 + 36.718,121.7,-4.3928,-1.1222,3.9169,1.1718,-41.475,0.0016441,41.475,3.1416,1.9651,0.33734,0.7854,-9.2186e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011524,111486,0,0.52526,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.957,287.35,99877,339.9,0.42668,0.1008 + 37.141,119.94,-3.9136,1.1312,4.3949,1.1803,-42.278,0.0016441,42.278,3.1416,1.8224,0.33686,0.7854,-9.3969e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01293,125107,0,0.66138,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8298,287.36,99898,339.91,0.42362,0.10084 + 37.584,118.09,-4.4123,-1.1272,3.9137,1.13,-43.092,0.0016441,43.092,3.1416,1.8575,0.079392,0.7854,-9.5779e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011514,111426,0,0.52459,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8582,287.37,99918,339.92,0.44249,0.10089 + 37.949,116.57,-3.9583,1.2422,4.4183,1.3679,-43.809,0.0016441,43.809,3.1416,2.0669,0.57285,0.7854,-9.7373e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012998,125808,0,0.66868,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0863,287.38,99940,339.92,0.36553,0.10093 + 38.422,114.59,-4.3877,-0.90877,3.9656,1.0581,-44.725,0.0016441,44.725,3.1416,1.8108,0.54202,0.7854,-9.9409e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011666,112933,0,0.53877,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8258,287.39,99958,339.93,0.47253,0.10098 + 38.842,112.85,-3.919,1.116,4.3909,1.1906,-45.522,0.0016441,45.522,3.1416,1.9849,0.41468,0.7854,-1.0118e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012917,125062,0,0.66063,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9774,287.41,99981,339.94,0.41997,0.10102 + 39.296,110.95,-4.4176,-1.0971,3.9192,1.1003,-46.416,0.0016441,46.416,3.1416,1.9473,0.082869,0.7854,-1.0317e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011529,111643,0,0.52641,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9476,287.42,1e5,339.94,0.45444,0.10107 + 39.628,109.56,-3.9901,1.2886,4.4286,1.5072,-47.105,0.0016441,47.105,3.1416,2.2066,0.78173,0.7854,-1.047e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013027,126170,0,0.67225,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2586,287.43,100024,339.95,0.33175,0.10111 + 40.128,107.47,-4.3779,-0.77545,3.9903,0.78067,-48.197,0.0016441,48.197,3.1416,2.1616,0.090087,0.7854,-1.0713e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011738,113697,0,0.54586,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1668,287.44,100040,339.96,0.5,0.10116 + 40.568,105.65,-3.9062,1.073,4.3805,1.1374,-49.184,0.0016441,49.184,3.1416,2.3275,0.37752,0.7854,-1.0932e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012885,124834,0,0.65795,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3135,287.45,100065,339.97,0.43958,0.1012 + 40.916,104.22,-4.2895,-1.1002,3.928,1.4353,-49.938,0.0016441,49.938,3.1416,2.0064,0.92181,0.7854,-1.11e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011554,111956,0,0.52914,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9139,287.46,100086,339.97,0.34835,0.10125 + 41.416,102.15,-3.9712,0.63642,4.2901,0.65983,-50.963,0.0016441,50.963,3.1416,2.0935,0.17421,0.7854,-1.1327e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012619,122287,0,0.63126,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.078,287.47,100103,339.98,0.5,0.10129 + 41.916,100.06,-4.3914,-0.84034,3.9777,0.98398,-51.946,0.0016441,51.946,3.1416,1.8376,0.5119,0.7854,-1.1546e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.0117,113402,0,0.54279,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8668,287.49,100127,339.99,0.5,0.10134 + 42.329,98.347,-3.9169,1.1481,4.3941,1.2098,-52.673,0.0016441,52.673,3.1416,1.68,0.38119,0.7854,-1.1707e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012924,125292,0,0.6625,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6847,287.5,100151,339.99,0.4133,0.10138 + 42.722,96.726,-4.3362,-1.0678,3.9292,1.2731,-53.386,0.0016441,53.386,3.1416,1.9523,0.69334,0.7854,-1.1866e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011556,112052,0,0.52982,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9908,287.51,100172,340,0.39273,0.10143 + 43.222,94.668,-3.8954,0.88169,4.3404,0.99784,-54.421,0.0016441,54.421,3.1416,2.1859,0.46724,0.7854,-1.2096e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012766,123793,0,0.64661,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1419,287.52,100191,340.01,0.5,0.10147 + 43.643,92.924,-4.395,-1.1874,3.8955,1.1884,-55.336,0.0016441,55.336,3.1416,2.1652,0.049195,0.7854,-1.2299e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011457,111121,0,0.52094,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1637,287.53,100215,340.02,0.42073,0.10152 + 44.063,91.181,-3.9038,1.1688,4.3959,1.1898,-56.266,0.0016441,56.266,3.1416,2.2586,0.22211,0.7854,-1.2506e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012928,125412,0,0.66349,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2542,287.55,100235,340.02,0.42026,0.10156 + 44.497,89.38,-4.4011,-1.1468,3.9041,1.153,-57.234,0.0016441,57.234,3.1416,2.2068,0.11939,0.7854,-1.2721e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011482,111398,0,0.52343,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2048,287.56,100256,340.03,0.43365,0.10161 + 44.911,87.659,-3.9032,1.2013,4.4013,1.2065,-58.139,0.0016441,58.139,3.1416,2.1605,0.11179,0.7854,-1.2922e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012944,125601,0,0.66535,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1621,287.57,100277,340.04,0.41442,0.10165 + 45.346,85.853,-4.402,-1.1468,3.9034,1.1496,-59.086,0.0016441,59.086,3.1416,2.1954,0.080267,0.7854,-1.3133e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011479,111406,0,0.52341,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1967,287.58,100297,340.04,0.43494,0.1017 + 45.663,84.521,-4.0057,1.2507,4.4186,1.5779,-59.734,0.0016441,59.734,3.1416,1.8905,0.96206,0.7854,-1.3277e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012994,126129,0,0.67082,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8124,287.59,100318,340.05,0.31688,0.10174 + 46.163,82.433,-4.3466,-0.68191,4.0066,0.70782,-60.655,0.0016441,60.655,3.1416,1.7957,0.18976,0.7854,-1.3482e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011782,114378,0,0.55161,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8072,287.6,100334,340.06,0.5,0.10179 + 46.592,80.655,-3.9356,0.95757,4.3549,1.1647,-61.487,0.0016441,61.487,3.1416,2.0803,0.66302,0.7854,-1.3667e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012806,124342,0,0.65182,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0634,287.61,100358,340.06,0.42929,0.10184 + 47.089,78.575,-4.4309,-0.99621,3.936,1.0056,-62.504,0.0016441,62.504,3.1416,2.0119,0.13746,0.7854,-1.3893e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011574,112398,0,0.53256,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0189,287.63,100379,340.07,0.49719,0.10188 + 47.444,77.091,-3.9464,1.3671,4.433,1.411,-63.239,0.0016441,63.239,3.1416,2.1357,0.34931,0.7854,-1.4056e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013035,126611,0,0.67567,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1505,287.64,100403,340.08,0.35435,0.10193 + 47.944,75,-4.419,-0.9451,3.9466,0.9497,-64.319,0.0016441,64.319,3.1416,2.1824,0.093387,0.7854,-1.4296e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011605,112732,0,0.53561,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1773,287.65,100421,340.09,0.5,0.10197 + 48.319,73.432,-3.9274,1.3082,4.42,1.3306,-65.122,0.0016441,65.122,3.1416,2.091,0.24324,0.7854,-1.4474e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012997,126274,0,0.67194,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0857,287.66,100445,340.09,0.37577,0.10202 + 48.777,71.527,-4.394,-1.019,3.9313,1.092,-66.038,0.0016441,66.038,3.1416,1.9113,0.39256,0.7854,-1.4678e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011559,112326,0,0.53164,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9156,287.67,100464,340.1,0.45786,0.10206 + 49.194,69.799,-3.9002,1.1854,4.3946,1.2004,-66.85,0.0016441,66.85,3.1416,1.9901,0.18931,0.7854,-1.4859e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012921,125582,0,0.66445,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.987,287.69,100486,340.11,0.41653,0.10211 + 49.629,67.991,-4.3994,-1.1456,3.9004,1.1476,-67.724,0.0016441,67.724,3.1416,2.0194,0.067152,0.7854,-1.5053e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011468,111472,0,0.52348,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0204,287.7,100506,340.11,0.4357,0.10216 + 50.034,66.311,-3.9073,1.2166,4.4003,1.2363,-68.559,0.0016441,68.559,3.1416,2.1082,0.2197,0.7854,-1.5238e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012937,125776,0,0.66638,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1071,287.71,100528,340.12,0.40443,0.1022 + 50.478,64.467,-4.3999,-1.1093,3.9083,1.126,-69.514,0.0016441,69.514,3.1416,2.1939,0.19297,0.7854,-1.5451e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011491,111728,0,0.52579,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1949,287.72,100547,340.13,0.44406,0.10225 + 50.887,62.768,-3.9001,1.2209,4.4,1.2212,-70.41,0.0016441,70.41,3.1416,2.1829,0.026845,0.7854,-1.565e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012936,125801,0,0.66651,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1832,287.73,100569,340.13,0.40943,0.10229 + 51.311,61.015,-4.3804,-1.1347,3.9029,1.1812,-71.304,0.0016441,71.304,3.1416,2.044,0.32821,0.7854,-1.5849e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011474,111603,0,0.5245,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0353,287.74,100589,340.14,0.42328,0.10234 + 51.754,59.186,-3.8813,1.1268,4.3805,1.1288,-72.216,0.0016441,72.216,3.1416,2.0741,0.067993,0.7854,-1.6051e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012878,125277,0,0.66083,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0713,287.75,100609,340.15,0.44294,0.10238 + 52.162,57.501,-4.3792,-1.2206,3.8816,1.2256,-73.053,0.0016441,73.053,3.1416,2.029,0.11065,0.7854,-1.6237e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011411,111025,0,0.51897,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0241,287.77,100631,340.16,0.40796,0.10243 + 52.53,55.965,-3.9562,1.1483,4.3888,1.3573,-73.85,0.0016441,73.85,3.1416,2.2956,0.72365,0.7854,-1.6414e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012902,125548,0,0.66356,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3183,287.78,100651,340.16,0.36837,0.10248 + 53.03,53.878,-4.3919,-0.87131,3.9618,0.99216,-74.938,0.0016441,74.938,3.1416,2.0583,0.47455,0.7854,-1.6656e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011647,113347,0,0.54081,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0854,287.79,100669,340.17,0.5,0.10252 + 53.44,52.179,-3.9024,1.1943,4.393,1.2199,-75.803,0.0016441,75.803,3.1416,2.1603,0.24887,0.7854,-1.6848e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012914,125704,0,0.66506,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1576,287.8,100693,340.18,0.40986,0.10257 + 53.854,50.469,-4.3611,-1.1087,3.9084,1.2085,-76.656,0.0016441,76.656,3.1416,1.9613,0.481,0.7854,-1.7038e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011489,111850,0,0.5265,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9445,287.81,100713,340.18,0.41373,0.10261 + 54.282,48.697,-3.91,1.0529,4.3658,1.1671,-77.45,0.0016441,77.45,3.1416,1.7456,0.50348,0.7854,-1.7214e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012833,124957,0,0.65706,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7591,287.82,100733,340.19,0.42842,0.10266 + 54.74,46.791,-4.4072,-1.0844,3.9104,1.0906,-78.238,0.0016441,78.238,3.1416,1.6925,0.11569,0.7854,-1.739e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011494,111937,0,0.52721,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6937,287.83,100754,340.2,0.45847,0.1027 + 55.132,45.163,-3.9076,1.2757,4.4072,1.2766,-78.904,0.0016441,78.904,3.1416,1.7117,0.048787,0.7854,-1.7538e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012955,126179,0,0.66982,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7119,287.85,100776,340.2,0.39166,0.10275 + 55.578,43.313,-4.393,-1.0891,3.9094,1.1218,-79.694,0.0016441,79.694,3.1416,1.8314,0.26859,0.7854,-1.7713e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011491,111940,0,0.52713,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8321,287.86,100795,340.21,0.44572,0.10279 + 55.98,41.643,-3.9057,1.2115,4.3944,1.2429,-80.453,0.0016441,80.453,3.1416,1.9431,0.27782,0.7854,-1.7882e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012916,125845,0,0.66615,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9421,287.87,100817,340.22,0.40228,0.10284 + 56.429,39.78,-4.3979,-1.0966,3.9067,1.114,-81.345,0.0016441,81.345,3.1416,2.0311,0.19603,0.7854,-1.808e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011483,111891,0,0.52656,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.031,287.88,100837,340.22,0.44882,0.10288 + 56.83,38.116,-3.9019,1.2371,4.3983,1.2472,-82.146,0.0016441,82.146,3.1416,1.9675,0.15881,0.7854,-1.8258e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012927,125990,0,0.66755,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9679,287.89,100858,340.23,0.40089,0.10293 + 57.266,36.31,-4.3848,-1.1075,3.9042,1.1469,-83.033,0.0016441,83.033,3.1416,2.0973,0.29794,0.7854,-1.8455e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011475,111851,0,0.52608,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1011,287.9,100878,340.24,0.43596,0.10297 + 57.635,34.772,-3.9444,1.1925,4.3922,1.3538,-83.763,0.0016441,83.763,3.1416,1.8606,0.64094,0.7854,-1.8618e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012909,125848,0,0.66592,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8418,287.91,100899,340.25,0.36933,0.10302 + 58.135,32.685,-4.4035,-0.91835,3.945,0.9323,-84.714,0.0016441,84.714,3.1416,1.941,0.16069,0.7854,-1.8829e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011594,113048,0,0.53729,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9319,287.92,100917,340.25,0.5,0.10306 + 58.446,31.38,-3.9962,1.3107,4.419,1.609,-85.272,0.0016441,85.272,3.1416,1.651,0.93323,0.7854,-1.8953e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012987,126651,0,0.6743,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.5713,287.94,100942,340.26,0.31075,0.10311 + 58.946,29.299,-4.3271,-0.66184,4.0049,0.89552,-86.173,0.0016441,86.173,3.1416,1.9526,0.60327,0.7854,-1.9153e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01177,114794,0,0.55391,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9146,287.95,100957,340.27,0.5,0.10316 + 59.446,27.248,-3.8783,0.89772,4.3286,0.93972,-87.184,0.0016441,87.184,3.1416,2.0915,0.27778,0.7854,-1.9378e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012721,124092,0,0.64719,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0649,287.96,100981,340.27,0.5,0.1032 + 59.852,25.575,-4.3673,-1.2052,3.88,1.2321,-88.011,0.0016441,88.011,3.1416,1.9875,0.2563,0.7854,-1.9562e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011402,111249,0,0.5201,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9759,287.97,101005,340.28,0.4058,0.10325 + 60.227,24.016,-3.9469,1.1214,4.3769,1.3337,-88.807,0.0016441,88.807,3.1416,2.2581,0.72197,0.7854,-1.9739e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012862,125511,0,0.66194,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.276,287.98,101025,340.29,0.37489,0.10329 + 60.727,21.931,-4.3957,-0.89747,3.9477,0.91504,-89.959,0.0016441,89.959,3.1416,2.3474,0.17844,0.7854,-1.9995e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011601,113218,0,0.53858,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3372,287.99,101043,340.29,0.5,0.10334 + 61.094,20.402,-3.9348,1.2559,4.4007,1.3625,-90.785,0.0016441,90.785,3.1416,2.1535,0.52823,0.7854,-2.0178e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012932,126228,0,0.66939,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1375,288.01,101068,340.3,0.36698,0.10339 + 61.571,18.418,-4.3827,-0.93885,3.9402,1.0481,-91.759,0.0016441,91.759,3.1416,1.9312,0.46592,0.7854,-2.0395e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011578,113033,0,0.53671,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9468,288.02,101086,340.31,0.47705,0.10343 + 61.993,16.672,-3.8834,1.1818,4.3827,1.1835,-92.58,0.0016441,92.58,3.1416,1.9581,0.063582,0.7854,-2.0578e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012878,125747,0,0.66416,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9566,288.03,101109,340.32,0.42249,0.10348 + 62.397,15.009,-4.3551,-1.1681,3.8878,1.2383,-93.338,0.0016441,93.338,3.1416,1.7922,0.4108,0.7854,-2.0746e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011424,111560,0,0.5227,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7734,288.04,101129,340.32,0.40379,0.10352 + 62.846,13.16,-3.8824,1.0532,4.3575,1.1142,-94.178,0.0016441,94.178,3.1416,1.9554,0.36363,0.7854,-2.0933e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012804,125055,0,0.65674,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9381,288.05,101149,340.33,0.44876,0.10357 + 63.262,11.441,-4.3722,-1.1759,3.884,1.2004,-95.014,0.0016441,95.014,3.1416,2.0559,0.24137,0.7854,-2.1118e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011412,111481,0,0.52185,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.064,288.06,101171,340.34,0.41653,0.10361 + 63.699,9.6384,-3.8751,1.1371,4.3725,1.1437,-95.924,0.0016441,95.924,3.1416,2.1093,0.122,0.7854,-2.1321e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012847,125519,0,0.66149,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1047,288.08,101191,340.34,0.43719,0.10366 + 64.096,8.0046,-4.352,-1.2008,3.8788,1.2589,-96.732,0.0016441,96.732,3.1416,1.9591,0.37817,0.7854,-2.15e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011397,111363,0,0.52064,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.939,288.09,101212,340.35,0.39716,0.11179 + 64.578,6.031,-3.8521,1.0389,4.352,1.0392,-97.672,0.0016441,97.672,3.1416,1.9471,0.024977,0.7854,-2.1709e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012787,124963,0,0.65551,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9491,288.1,101231,340.36,0.48115,0.11183 + 64.924,4.6202,-4.296,-1.282,3.8638,1.444,-98.306,0.0016441,98.306,3.1416,1.717,0.66446,0.7854,-2.185e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011352,110962,0,0.51679,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6468,288.11,101254,340.37,0.34627,0.11188 + 65.376,2.757,-3.9371,0.79297,4.3073,1.1047,-99.162,0.0016441,99.162,3.1416,2.0651,0.76916,0.7854,-2.204e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012655,123713,0,0.64233,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0287,288.12,101271,340.37,0.4526,0.11192 + 65.876,0.67307,-4.3987,-0.92312,3.9383,0.94878,-100.22,0.0016441,100.22,3.1416,2.1747,0.21915,0.7854,-2.2276e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01157,113130,0,0.53709,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1622,288.13,101293,340.38,0.5,0.11197 + 66.255,-0.89859,-3.9128,1.2849,4.4004,1.3221,-101.02,0.0016441,101.02,3.1416,2.0569,0.31148,0.7854,-2.2454e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012928,126424,0,0.67065,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0512,288.15,101317,340.39,0.37819,0.11202 + + + + + + Simulation 4 + RK4Simulator + BarrowmanCalculator + + e1dc7488-77c0-403a-b54f-c01db07aba2f + 1.0 + 0.0 + 0.0 + 2.0 + 0.1 + 0.0 + 45.0 + 0.0 + flat + + 0.05 + + + + + + + + + + + + + + 0,0,0,-7.6644,0,7.6644,0,0,0,0,0,0,0.7854,0,1.5708,0,0,0,0.07124,0.0013955,2.0267e-5,NaN,0.25972,NaN,0.0063758,62355,0.15258,0,1.0846,0,0.66872,0.29583,0.12001,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,0.025,4.9087e-4,1.5708,0,NaN,288.15,101325,340.39,0.01,6.2557e-4 + 0.2525,1.9319,22.336,74.934,22.336,74.957,-0.002307,0,0.002307,-3.1416,0.098335,1.8544,0.7854,0,0.079621,0,0.26155,1.3722e-5,0.069158,0.0013671,2.0182e-5,0.30721,0.25631,2.0362,0.065859,643983,5.9959,0.13575,0.85323,0.89864,0.43637,0.2963,0.12056,1.074,2.1767,-2.2642e-4,0,0,0,0,0.010592,0.025,4.9087e-4,1.565,-3.8428e-5,NaN,288.14,101302,340.38,0.01161,0.013108 + 0.49939,9.3866,37.012,49.341,37.03,50.513,0.053043,-2.8144e-6,0.053043,-5.3059e-5,1.1711,10.815,0.7854,1.179e-8,0.038503,-4.5965e-8,0.028691,4.2891e-5,0.067622,0.001345,2.012e-5,0.31328,0.25365,2.385,0.10921,1067221,4.4206,0.35967,0.85473,0.86656,0.43604,0.29714,0.12155,0.48738,1.1624,9.0153e-5,0,0,0,0,4.636e-5,0.025,4.9087e-4,1.4397,3.0041e-5,NaN,288.09,101215,340.35,0.0077852,0.016369 + 0.75301,20.319,49.032,44.746,49.088,44.923,0.55656,1.5659e-5,0.55656,2.8136e-5,2.3409,3.9925,0.7854,1.237e-7,0.0039409,-1.2384e-7,0.46354,-1.7357e-4,0.066289,0.0013251,2.0066e-5,0.31909,0.25126,2.7133,0.14462,1411869,4.257,0.62267,0.8565,0.85663,0.43565,0.29813,0.12272,0.04713,0.12111,2.8821e-4,0,0,0,0,0.0069028,0.025,4.9087e-4,1.4883,6.1059e-5,NaN,288.02,101087,340.31,0.023179,0.020824 + 1.0016,33.9,60.186,43.042,60.301,43.156,1.3404,5.5218e-5,1.3404,4.1195e-5,3.7249,3.1363,0.7854,2.9793e-7,0.0068531,-1.1099e-7,0.21041,-1.5903e-4,0.06495,0.0013043,2.0012e-5,0.31863,0.24874,2.7953,0.17764,1732178,4.3772,0.94057,0.85859,0.859,0.43518,0.29931,0.1241,0.082443,0.2294,-2.9754e-6,0,0,0,0,9.4297e-4,0.025,4.9087e-4,1.4853,3.8641e-5,NaN,287.93,100927,340.26,0.0145,0.024259 + 1.2559,50.534,70.33,34.96,70.506,35.262,2.4591,9.6971e-5,2.4591,3.9434e-5,4.98,4.6053,0.7854,5.4657e-7,4.764e-4,9.1142e-8,-0.14263,-0.001238,0.063633,0.0012829,1.9958e-5,0.31981,0.24617,2.9456,0.20776,2022886,4.1696,1.2868,0.86088,0.86088,0.43467,0.30059,0.12561,0.0056746,0.017019,-3.919e-4,0,0,0,0,-3.1692e-4,0.025,4.9087e-4,1.4712,6.9665e-5,NaN,287.82,100732,340.19,0.037174,0.026483 + 1.4832,67.433,78.299,34.308,78.534,34.673,3.7187,1.5296e-4,3.7187,4.1132e-5,6.0631,5.0196,0.7854,8.2655e-7,9.5501e-4,2.6549e-7,-0.047577,1.4968e-4,0.062509,0.0012641,1.9913e-5,0.31978,0.24389,3.0354,0.23148,2250521,4.3733,1.5981,0.86294,0.86294,0.43422,0.30175,0.12697,0.011395,0.034758,2.1186e-4,0,0,0,0,-2.8415e-5,0.025,4.9087e-4,1.4651,-3.3866e-5,NaN,287.71,100534,340.12,0.05,0.027512 + 1.7332,88.048,86.484,30.766,86.784,31.076,5.3811,1.7559e-4,5.3811,3.2631e-5,7.2065,4.3761,0.7854,1.196e-6,2.3503e-5,-1.1447e-7,-0.022159,-0.0043619,0.061161,0.0012405,1.9858e-5,0.32,0.24105,3.1583,0.2558,2482516,4.448,1.9522,0.86528,0.86528,0.4337,0.30307,0.12851,2.8029e-4,7.6809e-4,1.7897e-4,0,0,0,0,-5.0501e-6,0.025,4.9087e-4,1.4636,5.9626e-5,NaN,287.58,100292,340.04,0.05,0.029544 + 1.9832,109.84,83.662,-39.748,83.969,39.903,7.2353,2.1381e-4,7.2353,2.9551e-5,7.1707,3.5116,0.7854,1.6082e-6,5.9489e-4,-3.5167e-7,-0.013078,-5.6872e-4,0.060443,0.0012276,1.9829e-5,0.31988,0.23948,3.2161,0.24757,2398018,0,1.8221,0.86446,0.86446,0.43388,0.30261,0.12797,0.007099,0.022467,-1.5854e-4,0,0,0,0,-1.8788e-6,0.025,4.9087e-4,1.4623,2.9817e-5,NaN,287.44,100037,339.96,0.05,0.030422 + 2.2332,129.58,74.545,-33.563,74.821,33.625,8.9301,2.1589e-4,8.9301,2.4175e-5,6.428,2.0447,0.7854,1.9849e-6,0.0020253,-2.8946e-7,-0.0053764,5.4678e-5,0.060443,0.0012276,1.9829e-5,0.31956,0.23948,3.2034,0.22071,2134092,0,1.4407,0.86198,0.86201,0.43443,0.30121,0.12633,0.024201,0.077132,-3.1711e-4,0,0,0,0,-3.9972e-7,0.025,4.9087e-4,1.4569,-1.2949e-5,NaN,287.31,99805,339.88,0.026592,0.03162 + 2.4812,147.09,66.846,-28.821,67.1,28.89,10.446,2.2017e-4,10.446,2.1076e-5,5.8256,2.0029,0.7854,2.3219e-6,9.8902e-4,-5.3655e-7,0.0099145,-4.0108e-4,0.060443,0.0012276,1.9829e-5,0.3197,0.23948,3.2089,0.19809,1912397,0,1.1556,0.86011,0.86012,0.43485,0.30016,0.1251,0.011787,0.037956,1.967e-4,0,0,0,0,1.6881e-6,0.025,4.9087e-4,1.4521,-2.9814e-5,NaN,287.19,99600,339.81,0.05,0.032627 + 2.7312,162.94,60.127,-25.094,60.361,25.18,11.837,2.0776e-4,11.837,1.7553e-5,5.3134,2.0799,0.7854,2.6309e-6,0.001179,-1.0175e-6,0.0099982,-3.7974e-4,0.060443,0.0012276,1.9829e-5,0.31963,0.23948,3.2061,0.17826,1718541,0,0.93246,0.85864,0.85865,0.43517,0.29933,0.12413,0.014046,0.0447,-2.61e-4,0,0,0,0,2.1208e-6,0.025,4.9087e-4,1.4519,-6.0292e-5,NaN,287.09,99414,339.75,0.05,0.033522 + 2.9812,177.22,54.224,-22.212,54.442,22.285,13.11,1.6958e-4,13.11,1.2935e-5,4.8717,1.7962,0.7854,2.9138e-6,0.0013097,-1.3424e-6,0.08927,-0.0012817,0.060443,0.0012276,1.9829e-5,0.31958,0.23948,3.204,0.16092,1549421,0,0.75759,0.85748,0.85749,0.43543,0.29868,0.12337,0.015599,0.049318,-2.0956e-4,0,0,0,0,2.0753e-4,0.025,4.9087e-4,1.4452,1.1731e-4,NaN,287,99246,339.69,0.05,0.041649 + 3.2353,190.31,48.883,-19.82,49.089,19.92,14.302,1.532e-4,14.302,1.0711e-5,4.4854,1.9974,0.7854,3.179e-6,0.0046954,-1.1e-6,0.13872,-5.325e-4,0.060443,0.0012276,1.9829e-5,0.31895,0.23948,3.179,0.14527,1397095,0,0.61588,0.85653,0.85672,0.43564,0.29815,0.12274,0.056226,0.17841,-4.649e-4,0,0,0,0,6.1514e-4,0.025,4.9087e-4,1.4395,1.1946e-4,NaN,286.91,99093,339.64,0.026729,0.04323 + 3.4833,201.84,44.184,-17.914,44.382,18.041,15.384,1.44e-4,15.384,9.3603e-6,4.1956,2.1321,0.7854,3.4194e-6,0.0092947,-1.291e-6,0.057338,-1.6579e-4,0.060443,0.0012276,1.9829e-5,0.31813,0.23948,3.146,0.13132,1261673,0,0.50249,0.85577,0.85651,0.43581,0.29773,0.12224,0.11214,0.35279,-2.6768e-4,0,0,0,0,1.2863e-4,0.025,4.9087e-4,1.4439,6.2803e-5,NaN,286.84,98958,339.59,0.016563,0.044947 + 3.7386,212.55,39.777,-16.393,39.973,16.472,16.42,1.2942e-4,16.42,7.882e-6,3.9523,1.605,0.7854,3.6497e-6,0.0068881,-1.3411e-6,-0.058732,-2.4607e-4,0.060443,0.0012276,1.9829e-5,0.31853,0.23948,3.1622,0.11847,1137065,0,0.40794,0.85514,0.85555,0.43595,0.29737,0.12182,0.082741,0.2623,4.7816e-4,0,0,0,0,-1.6588e-4,0.025,4.9087e-4,1.4282,-6.7871e-6,NaN,286.77,98832,339.55,0.027409,0.046739 + 4.0012,222.44,35.625,-15.169,35.819,15.188,17.418,1.0975e-4,17.418,6.3013e-6,3.7271,0.76212,0.7854,3.8714e-6,0.0012993,-1.4197e-6,0.13308,-0.0011592,0.060443,0.0012276,1.9829e-5,0.31951,0.23948,3.2012,0.10625,1018919,0,0.32741,0.8546,0.85462,0.43607,0.29707,0.12147,0.015454,0.048352,-3.4286e-4,0,0,0,0,0.0010589,0.025,4.9087e-4,1.4116,-9.9986e-5,NaN,286.7,98716,339.51,0.044014,0.048325 + 4.2715,231.53,31.694,-14.118,31.882,14.12,18.385,8.1123e-5,18.385,4.4125e-6,3.4612,0.24514,0.7854,4.0863e-6,0.0081582,-1.4013e-6,-0.11162,-8.1022e-5,0.060443,0.0012276,1.9829e-5,0.31829,0.23948,3.1523,0.094614,906577,0,0.25936,0.85414,0.85471,0.43617,0.29681,0.12116,0.098168,0.31031,4.7603e-4,0,0,0,0,-9.3972e-4,0.025,4.9087e-4,1.3997,-3.7153e-5,NaN,286.65,98610,339.48,0.036405,0.061776 + 4.5345,239.39,28.121,-13.091,28.313,13.121,19.279,5.3356e-5,19.279,2.7675e-6,3.2992,0.89516,0.7854,4.2852e-6,0.0068737,-1.3867e-6,0.075313,-1.4023e-4,0.060443,0.0012276,1.9829e-5,0.3185,0.23948,3.1609,0.084151,805751,0,0.20485,0.85377,0.85418,0.43625,0.2966,0.12092,0.082516,0.26039,-7.6755e-5,0,0,0,0,5.4091e-4,0.025,4.9087e-4,1.4011,2.6326e-5,NaN,286.59,98517,339.45,0.05,0.0628 + 4.7845,246.02,24.93,-12.449,25.127,12.459,20.083,3.3988e-5,20.083,1.6924e-6,3.1349,0.48687,0.7854,4.4637e-6,0.0016432,-1.4001e-6,-0.17413,9.9099e-4,0.060443,0.0012276,1.9829e-5,0.31942,0.23948,3.1976,0.074995,717656,0,0.16244,0.85349,0.85351,0.43631,0.29644,0.12073,0.019547,0.066321,-3.5557e-4,0,0,0,0,-0.0036413,0.025,4.9087e-4,1.3658,-6.5544e-5,NaN,286.55,98440,339.42,0.05,0.06366 + 5.0345,251.87,21.893,-11.819,22.101,11.836,20.853,3.946e-6,20.853,1.8923e-7,3.0222,0.62848,0.7854,4.635e-6,0.0049036,-1.3961e-6,0.10028,-3.2759e-4,0.060443,0.0012276,1.9829e-5,0.31884,0.23948,3.1742,0.06639,634972,0,0.1272,0.85325,0.85345,0.43637,0.29631,0.12057,0.058655,0.18422,2.7741e-4,0,0,0,0,0.0015412,0.025,4.9087e-4,1.3369,1.4677e-5,NaN,286.51,98371,339.4,0.05,0.064579 + 5.2845,256.98,18.993,-11.369,19.216,11.374,21.594,-1.6208e-5,21.594,-7.5057e-7,2.918,0.33658,0.7854,4.7996e-6,0.0034851,-1.3999e-6,-0.063657,-2.5611e-4,0.060443,0.0012276,1.9829e-5,0.31908,0.23948,3.1841,0.057868,553211,0,0.09655,0.85304,0.85314,0.43641,0.29619,0.12044,0.041582,0.13346,-4.5658e-5,0,0,0,0,-8.1754e-4,0.025,4.9087e-4,1.3101,5.8645e-6,NaN,286.48,98311,339.38,0.05,0.065642 + 5.5345,261.38,16.216,-10.904,16.455,10.915,22.309,-3.1839e-5,22.309,-1.4272e-6,2.7906,0.48409,0.7854,4.9585e-6,0.0091304,-1.3809e-6,0.20085,2.8651e-4,0.060443,0.0012276,1.9829e-5,0.31809,0.23948,3.1442,0.049959,477413,0,0.071961,0.85287,0.85358,0.43645,0.2961,0.12032,0.10999,0.33529,3.8352e-4,0,0,0,0,0.010921,0.025,4.9087e-4,1.2838,-7.8228e-5,NaN,286.45,98259,339.36,0.05,0.066479 + 5.7845,265.09,13.521,-10.625,13.793,10.627,22.996,-5.8286e-5,22.996,-2.5346e-6,2.7232,0.21984,0.7854,5.1113e-6,0.0053538,-1.3781e-6,-0.15453,-4.3197e-4,0.060443,0.0012276,1.9829e-5,0.31874,0.23948,3.1705,0.042142,402574,0,0.051143,0.85273,0.85298,0.43648,0.29602,0.12023,0.064074,0.21174,9.0029e-5,0,0,0,0,-0.0090863,0.025,4.9087e-4,1.2337,3.2421e-5,NaN,286.43,98216,339.34,0.05,0.06732 + 6.0345,268.15,10.906,-10.288,11.225,10.294,23.669,-7.6117e-5,23.669,-3.2158e-6,2.6577,0.36725,0.7854,5.2609e-6,0.019971,-1.3787e-6,0.16978,6.8876e-6,0.060443,0.0012276,1.9829e-5,0.31623,0.23948,3.07,0.034805,332395,0,0.035247,0.85878,0.86213,0.44267,0.29596,0.12016,0.24499,0.73631,-4.2022e-4,0,0,0,0,0.016081,0.025,4.9087e-4,1.1969,1.4085e-5,NaN,286.41,98180,339.33,0.05,0.068162 + 6.2845,270.55,8.3616,-10.08,8.7483,10.085,24.323,-8.9008e-5,24.323,-3.6594e-6,2.5723,0.30537,0.7854,5.4062e-6,0.030387,-1.3777e-6,0.32111,2.0303e-4,0.060443,0.0012276,1.9829e-5,0.31452,0.23948,3.0014,0.027578,263320,0,0.022847,0.88259,0.89035,0.46658,0.29591,0.1201,0.37923,1.047,3.2288e-4,0,0,0,0,0.091623,0.025,4.9087e-4,1.1355,-4.0925e-5,NaN,286.39,98152,339.32,0.05,0.069 + 6.5345,272.33,5.8705,-9.8484,6.3741,9.8573,24.956,-1.0468e-4,24.956,-4.1945e-6,2.4833,0.41821,0.7854,5.5469e-6,0.095491,-1.376e-6,0.51041,1.4263e-4,0.060443,0.0012276,1.9829e-5,0.30507,0.23948,2.6236,0.021424,204529,0,0.015133,0.9107,0.97738,0.49476,0.29587,0.12006,1.3185,3.0754,-3.0211e-4,0,0,0,0,0.38359,0.025,4.9087e-4,1.0355,-6.147e-5,NaN,286.38,98131,339.32,0.05,0.074078 + 6.7845,273.49,3.4365,-9.6096,4.1737,9.6232,25.563,-1.2436e-4,25.563,-4.8647e-6,2.3686,0.51148,0.7854,5.6818e-6,0.2131,-1.3789e-6,0.76612,5.5882e-5,0.060443,0.0012276,1.9829e-5,0.29227,0.23948,2.1118,0.016431,156842,0,0.010665,0.94309,1.1713,0.52721,0.29585,0.12004,3.4415,5.7986,2.9385e-4,0,0,0,0,1.4694,0.025,4.9087e-4,0.87726,-5.242e-5,NaN,286.37,98117,339.31,0.05,0.074951 + 7.0425,274,0.35634,-11.294,3.3454,12.821,26.045,-1.4446e-4,26.045,-5.5463e-6,0.98461,6.0688,0.7854,5.789e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.0098595,94111,0,0.37768,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0279,286.37,98112,339.31,0.038999,0.075724 + 7.3002,273.78,-1.9367,-7.671,2.5952,8.2723,26.159,-1.5728e-4,26.159,-6.0124e-6,0.010446,3.096,0.7854,5.8144e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.0076486,73008,0,0.22729,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.96,286.37,98113,339.31,0.060443,0.085677 + 7.5379,273.14,-3.2974,-4.7052,3.1962,5.3536,26.08,-1.6523e-4,26.08,-6.3353e-6,0.63787,2.5538,0.7854,5.7968e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.0094196,89917,0,0.34476,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8304,286.37,98118,339.31,0.093396,0.085809 + 7.8554,271.95,-4.0857,-1.902,3.8074,2.583,25.776,-1.7123e-4,25.776,-6.6428e-6,1.2472,1.7476,0.7854,5.7291e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011221,107116,0,0.48924,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7309,286.38,98127,339.31,0.19357,0.08712 + 8.2964,270.11,-4.2732,-0.42518,4.1113,1.1339,25.124,-1.7381e-4,25.124,-6.9182e-6,1.7107,1.0512,0.7854,5.5842e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012116,115675,0,0.57052,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.705,286.38,98135,339.32,0.44095,0.087228 + 8.7964,268.03,-4.0616,0.42325,4.2857,0.88997,24.171,-1.7378e-4,24.171,-7.1898e-6,2.1021,0.78288,0.7854,5.3723e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01263,120599,0,0.62006,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0378,286.39,98157,339.32,0.5,0.087276 + 9.2964,265.92,-4.3531,-0.58292,4.0647,0.68424,23.075,-1.7379e-4,23.075,-7.5315e-6,2.2813,0.35831,0.7854,5.1287e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011978,114397,0,0.55786,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2599,286.41,98181,339.33,0.5,0.087322 + 9.7964,263.85,-3.9597,0.78669,4.3548,0.84319,21.972,-1.7379e-4,21.972,-7.9095e-6,2.1296,0.30346,0.7854,4.8836e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012833,122585,0,0.64048,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1566,286.42,98206,339.34,0.5,0.087367 + 10.227,262.04,-4.4022,-1.0263,3.9672,1.1597,21.104,-1.7379e-4,21.104,-8.2348e-6,1.8967,0.54008,0.7854,4.6907e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011691,111691,0,0.53164,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.886,286.44,98231,339.35,0.43113,0.087412 + 10.704,260.06,-3.9124,1.0288,4.403,1.0502,20.177,-1.7379e-4,20.177,-8.6131e-6,1.9971,0.21083,0.7854,4.4847e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012975,123979,0,0.65498,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9824,286.45,98252,339.36,0.47612,0.087456 + 11.102,258.41,-4.4089,-1.2468,3.9129,1.2555,19.37,-1.7379e-4,19.37,-8.9719e-6,2.0557,0.1472,0.7854,4.3053e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01153,110197,0,0.51739,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0644,286.46,98275,339.36,0.39826,0.087502 + 11.566,256.48,-3.9149,1.065,4.4094,1.0778,18.434,-1.7379e-4,18.434,-9.4274e-6,1.9788,0.16584,0.7854,4.0973e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012993,124195,0,0.65712,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9885,286.47,98294,339.37,0.46391,0.087547 + 11.97,254.79,-4.4128,-1.2334,3.9152,1.2384,17.626,-1.7379e-4,17.626,-9.8595e-6,2.0237,0.11121,0.7854,3.9177e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011536,110291,0,0.51816,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0296,286.48,98317,339.38,0.40374,0.087592 + 12.429,252.88,-3.9131,1.0869,4.4129,1.0875,16.692,-1.7379e-4,16.692,-1.0411e-5,2.0394,0.034096,0.7854,3.7101e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013003,124327,0,0.65837,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0375,286.49,98337,339.39,0.45978,0.087637 + 12.767,251.49,-4.3161,-1.1946,3.9333,1.4821,16.054,-1.7379e-4,16.054,-1.0825e-5,1.7434,0.87715,0.7854,3.5683e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011589,110834,0,0.52316,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6408,286.51,98359,339.39,0.33737,0.087682 + 13.155,249.87,-4.0539,0.67471,4.3396,1.2866,15.294,-1.7379e-4,15.294,-1.1363e-5,2.1692,1.0955,0.7854,3.3993e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012786,122296,0,0.63691,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1946,286.52,98375,339.4,0.38861,0.087726 + 13.655,247.76,-4.3564,-0.60497,4.0555,0.65796,14.177,-1.7379e-4,14.177,-1.2258e-5,2.2985,0.25868,0.7854,3.1511e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011949,114303,0,0.55633,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2832,286.53,98394,339.4,0.5,0.091573 + 14.155,245.69,-3.9467,0.81937,4.3573,0.84709,13.055,-1.7379e-4,13.055,-1.3312e-5,2.1911,0.2149,0.7854,2.9016e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012838,122829,0,0.64233,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2104,286.54,98419,339.41,0.5,0.091701 + 14.588,243.88,-4.4115,-1.0747,3.9514,1.1562,12.147,-1.7379e-4,12.147,-1.4307e-5,2.0067,0.42631,0.7854,2.6998e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011642,111406,0,0.52834,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9984,286.55,98443,339.42,0.43247,0.091748 + 15.045,241.98,-3.9121,1.0917,4.4115,1.0932,11.223,-1.7379e-4,11.223,-1.5485e-5,2.0325,0.056404,0.7854,2.4945e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012997,124396,0,0.65867,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0295,286.56,98465,339.43,0.45738,0.091794 + 15.427,240.39,-4.3782,-1.221,3.9181,1.3099,10.482,-1.7379e-4,10.482,-1.658e-5,1.8515,0.47418,0.7854,2.3298e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011543,110499,0,0.51966,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8164,286.58,98487,339.44,0.38172,0.091838 + 15.885,238.49,-3.9452,0.94537,4.3839,1.0914,9.5764,-1.7379e-4,9.5764,-1.8147e-5,2.1014,0.54546,0.7854,2.1285e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012915,123650,0,0.65065,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0736,286.59,98506,339.44,0.45811,0.091882 + 16.279,236.85,-4.3617,-1.0579,3.9578,1.2697,8.8033,-1.7379e-4,8.8033,-1.9741e-5,1.8249,0.70214,0.7854,1.9567e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01166,111650,0,0.53043,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7847,286.6,98528,339.45,0.39379,0.091927 + 16.753,234.88,-3.949,0.87101,4.3685,1.055,7.8716,-1.7379e-4,7.8716,-2.2078e-5,2.107,0.59537,0.7854,1.7496e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012869,123251,0,0.64632,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0681,286.61,98547,339.46,0.47391,0.091972 + 17.203,233,-4.4259,-1.0597,3.9518,1.1109,6.9571,-1.7379e-4,6.9571,-2.498e-5,1.957,0.33341,0.7854,1.5463e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011641,111512,0,0.52901,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9565,286.62,98570,339.46,0.45007,0.092016 + 17.559,231.5,-3.9984,1.1984,4.4356,1.4017,6.3053,-1.7379e-4,6.3053,-2.7562e-5,1.6976,0.72703,0.7854,1.4015e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013066,125180,0,0.66656,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6646,286.64,98592,339.47,0.35671,0.09206 + 18.059,229.39,-4.4124,-0.82792,4.0051,0.97674,5.3917,-1.7379e-4,5.3917,-3.2232e-5,1.9568,0.51824,0.7854,1.1984e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011798,113045,0,0.54354,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9284,286.65,98610,339.48,0.5,0.092104 + 18.508,227.53,-3.9136,1.1115,4.4125,1.1142,4.5058,-1.7379e-4,4.5058,-3.8569e-5,1.9914,0.07716,0.7854,1.0015e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012998,124565,0,0.65988,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9879,286.66,98635,339.49,0.44875,0.092149 + 18.871,226.03,-4.345,-1.1879,3.9263,1.3767,3.7367,-1.7379e-4,3.7367,-4.6508e-5,2.2441,0.69588,0.7854,8.3054e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011565,110856,0,0.52257,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3074,286.67,98657,339.49,0.36318,0.092192 + 19.173,224.76,-4.0811,0.87551,4.3826,1.6584,3.1241,-1.7379e-4,3.1241,-5.5627e-5,1.8195,1.4085,0.7854,6.9439e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012909,123754,0,0.65119,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6711,286.68,98674,339.5,0.30149,0.092237 + 19.673,222.66,-4.3096,-0.457,4.0835,0.55895,2.1742,-1.7379e-4,2.1742,-7.9932e-5,1.9804,0.32183,0.7854,4.8325e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012028,115318,0,0.56539,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.96,286.69,98689,339.5,0.5,0.092281 + 20.173,220.58,-3.9984,0.62231,4.3125,0.73359,1.2326,-1.7379e-4,1.2326,-1.41e-4,1.7862,0.38843,0.7854,2.7396e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012702,121807,0,0.63073,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8198,286.7,98714,339.51,0.5,0.092326 + 20.673,218.48,-4.4073,-0.81777,4.0054,0.97648,0.27278,-1.7379e-4,0.27278,-6.3711e-4,2.053,0.53363,0.7854,6.0629e-8,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011797,113151,0,0.54421,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0236,286.72,98738,339.52,0.5,0.092372 + 21.125,216.6,-3.9106,1.0982,4.4076,1.1055,-0.66875,-1.7379e-4,0.66875,-3.1413,2.1103,0.12675,0.7854,-1.4864e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012981,124532,0,0.6591,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1042,286.73,98763,339.53,0.4523,0.092417 + 21.521,214.95,-4.3912,-1.2131,3.9138,1.2619,-1.4776,-1.7379e-4,1.4776,-3.1415,1.9726,0.34761,0.7854,-3.2843e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011527,110596,0,0.51978,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9521,286.74,98785,339.54,0.39624,0.092462 + 22.01,212.93,-3.8919,1.022,4.3913,1.0232,-2.4355,-1.7379e-4,2.4355,-3.1415,1.9478,0.050748,0.7854,-5.4133e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012933,124106,0,0.65446,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.952,286.75,98804,339.54,0.48866,0.092507 + 22.394,211.34,-4.3909,-1.2987,3.892,1.3013,-3.1899,-1.7379e-4,3.1899,-3.1415,1.979,0.081329,0.7854,-7.0901e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011462,110013,0,0.5142,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.985,286.77,98828,339.55,0.38423,0.092552 + 22.861,209.4,-3.9107,1.0279,4.3925,1.0703,-4.0819,-1.7379e-4,4.0819,-3.1416,1.8397,0.29822,0.7854,-9.0727e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012936,124176,0,0.65506,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8581,286.78,98846,339.56,0.46717,0.092596 + 23.271,207.69,-4.4078,-1.2128,3.9111,1.2198,-4.825,-1.7379e-4,4.825,-3.1416,1.7861,0.1308,0.7854,-1.0724e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011518,110584,0,0.51944,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7802,286.79,98869,339.56,0.40991,0.092641 + 23.568,206.44,-4.0584,1.1767,4.4342,1.6839,-5.4084,-1.7379e-4,5.4084,-3.1416,2.1438,1.2046,0.7854,-1.2021e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013058,125390,0,0.66778,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2696,286.8,98889,339.57,0.29692,0.092686 + 24.068,204.34,-4.3312,-0.54564,4.0602,0.6111,-6.4459,-1.7379e-4,6.4459,-3.1416,2.0062,0.27516,0.7854,-1.4327e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011957,114825,0,0.55995,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0232,286.81,98904,339.58,0.5,0.09273 + 24.513,202.49,-3.9888,0.77025,4.3442,1.1247,-7.2568,-1.7379e-4,7.2568,-3.1416,1.6418,0.81959,0.7854,-1.6129e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012793,122877,0,0.64115,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6705,286.82,98928,339.58,0.44455,0.092775 + 24.93,200.75,-4.3306,-0.81885,4.0076,1.1979,-8.0182,-1.7379e-4,8.0182,-3.1416,2.0068,0.87434,0.7854,-1.7822e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011801,113374,0,0.54575,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0299,286.83,98950,339.59,0.41739,0.092819 + 25.43,198.68,-3.9589,0.74336,4.3323,0.80083,-8.9844,-1.7379e-4,8.9844,-3.1416,1.8578,0.2979,0.7854,-1.9969e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012757,122575,0,0.63787,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8845,286.85,98970,339.6,0.5,0.092864 + 25.929,196.59,-4.4525,-0.99,3.9596,1.003,-9.9305,-1.7379e-4,9.9305,-3.1416,1.9379,0.16072,0.7854,-2.2072e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011659,112047,0,0.53293,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.93,286.86,98995,339.61,0.49852,0.09291 + 26.291,195.06,-3.9637,1.3492,4.454,1.3803,-10.613,-1.7379e-4,10.613,-3.1416,1.8324,0.2913,0.7854,-2.359e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013115,126058,0,0.67446,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8217,286.87,99019,339.61,0.36225,0.092956 + 26.791,192.96,-4.4457,-0.96402,3.9651,0.99118,-11.558,-1.7379e-4,11.558,-3.1416,1.9476,0.23045,0.7854,-2.5691e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011675,112234,0,0.5346,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9357,286.88,99037,339.62,0.5,0.093001 + 27.123,191.56,-4.0016,1.3369,4.4543,1.5052,-12.167,-1.7379e-4,12.167,-3.1416,1.7179,0.69147,0.7854,-2.7044e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013115,126102,0,0.67479,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6718,286.9,99062,339.63,0.33219,0.093046 + 27.623,189.46,-4.3966,-0.78999,4.0035,0.83672,-12.992,-1.7379e-4,12.992,-3.1416,1.5801,0.2757,0.7854,-2.8876e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011788,113352,0,0.54519,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.5956,286.9,99078,339.63,0.5,0.09309 + 28.004,187.86,-3.9787,1.0986,4.4062,1.3143,-13.645,-1.7379e-4,13.645,-3.1416,1.8546,0.72147,0.7854,-3.0328e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012973,124775,0,0.66053,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.871,286.92,99103,339.64,0.38043,0.093135 + 28.504,185.76,-4.4255,-0.89372,3.9788,0.8973,-14.582,-1.7379e-4,14.582,-3.1416,1.8946,0.080078,0.7854,-3.2412e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011715,112687,0,0.53869,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8903,286.93,99122,339.65,0.5,0.09318 + 28.818,184.43,-4.0285,1.2644,4.4424,1.5922,-15.225,-1.7379e-4,15.225,-3.1416,2.1985,0.96771,0.7854,-3.384e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013079,125836,0,0.67166,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2815,286.94,99146,339.66,0.31403,0.093225 + 29.318,182.34,-4.3571,-0.65725,4.0326,0.77648,-16.273,-1.7379e-4,16.273,-3.1416,1.9918,0.41344,0.7854,-3.6169e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011872,114239,0,0.55352,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0164,286.95,99162,339.66,0.5,0.09327 + 29.818,180.27,-3.9115,0.89125,4.3587,0.9361,-17.304,-1.7379e-4,17.304,-3.1416,2.1349,0.28627,0.7854,-3.8462e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012832,123497,0,0.64679,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1084,286.96,99186,339.67,0.5,0.093341 + 30.239,178.52,-4.4113,-1.1862,3.9115,1.1867,-18.201,-1.7379e-4,18.201,-3.1416,2.1206,0.033943,0.7854,-4.0454e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011515,110845,0,0.52099,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1195,286.98,99211,339.68,0.42133,0.093404 + 30.633,176.87,-3.9507,1.1701,4.4157,1.2702,-18.997,-1.7379e-4,18.997,-3.1416,1.926,0.49429,0.7854,-4.2225e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013,125151,0,0.66408,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9219,286.99,99231,339.69,0.39364,0.09352 + 31.103,174.9,-4.4228,-1.0029,3.9538,1.0621,-19.943,-1.7379e-4,19.943,-3.1416,2.0906,0.34961,0.7854,-4.4326e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011639,112073,0,0.53249,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0829,287,99251,339.69,0.47078,0.093567 + 31.502,173.23,-3.9347,1.2236,4.4242,1.2534,-20.755,-1.7379e-4,20.755,-3.1416,1.9822,0.27183,0.7854,-4.6132e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013024,125425,0,0.66685,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9816,287.01,99274,339.7,0.39892,0.093611 + 31.95,171.36,-4.4149,-1.0722,3.9372,1.1164,-21.612,-1.7379e-4,21.612,-3.1416,1.843,0.31086,0.7854,-4.8036e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01159,111634,0,0.52821,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8421,287.02,99293,339.71,0.44789,0.093656 + 32.372,169.61,-3.915,1.1847,4.4149,1.1847,-22.389,-1.7379e-4,22.389,-3.1416,1.839,0.0094871,0.7854,-4.9763e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012996,125198,0,0.66429,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8392,287.04,99315,339.71,0.42204,0.0937 + 32.786,167.89,-4.3935,-1.1551,3.9182,1.207,-23.181,-1.7379e-4,23.181,-3.1416,1.984,0.3502,0.7854,-5.1522e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011533,111126,0,0.5233,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9974,287.05,99336,339.72,0.41424,0.093746 + 33.246,165.98,-3.8958,1.0821,4.3937,1.0871,-24.104,-1.7379e-4,24.104,-3.1416,2.0323,0.10504,0.7854,-5.3575e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012933,124628,0,0.65812,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0264,287.06,99356,339.73,0.45992,0.093792 + 33.648,164.31,-4.3954,-1.2435,3.8959,1.2446,-24.925,-1.7379e-4,24.925,-3.1416,2.0529,0.05109,0.7854,-5.5399e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011467,110524,0,0.51754,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0556,287.07,99378,339.74,0.40175,0.093837 + 34.065,162.58,-3.9347,1.1047,4.3994,1.1989,-25.74,-1.7379e-4,25.74,-3.1416,1.8586,0.4658,0.7854,-5.7212e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012949,124825,0,0.66006,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8652,287.08,99398,339.74,0.41706,0.093881 + 34.46,160.94,-4.3446,-1.0393,3.9481,1.2675,-26.53,-1.7379e-4,26.53,-3.1416,2.1448,0.72548,0.7854,-5.8967e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011621,112036,0,0.53168,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1842,287.09,99418,339.75,0.39448,0.093926 + 34.96,158.88,-3.9194,0.85045,4.3456,0.88014,-27.574,-1.7379e-4,27.574,-3.1416,2.0314,0.22669,0.7854,-6.1288e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01279,123332,0,0.64424,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0524,287.1,99437,339.75,0.5,0.093971 + 35.372,157.17,-4.3817,-1.1214,3.925,1.2129,-28.451,-1.7379e-4,28.451,-3.1416,2.222,0.4624,0.7854,-6.3236e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011552,111411,0,0.52566,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2401,287.12,99462,339.76,0.41222,0.094016 + 35.855,155.17,-3.8818,1.0343,4.3817,1.0345,-29.522,-1.7379e-4,29.522,-3.1416,2.2122,0.020313,0.7854,-6.5618e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012896,124391,0,0.65521,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2138,287.13,99482,339.77,0.48334,0.094126 + 36.224,153.65,-4.3564,-1.2859,3.8866,1.3546,-30.31,-1.7379e-4,30.31,-3.1416,2.055,0.42595,0.7854,-6.7369e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011439,110354,0,0.51561,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0182,287.14,99505,339.78,0.36912,0.094171 + 36.7,151.68,-3.9167,0.92484,4.3611,1.0517,-31.23,-1.7379e-4,31.23,-3.1416,1.8169,0.50075,0.7854,-6.9415e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012835,123842,0,0.6493,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8517,287.15,99523,339.78,0.47542,0.094215 + 37.109,149.99,-4.3775,-1.1268,3.9226,1.2226,-32.013,-1.7379e-4,32.013,-3.1416,2.0109,0.47434,0.7854,-7.1154e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011544,111404,0,0.52537,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.031,287.16,99546,339.79,0.40898,0.09426 + 37.587,148.01,-3.8886,1.0234,4.3784,1.0465,-32.999,-1.7379e-4,32.999,-3.1416,2.1153,0.21837,0.7854,-7.3345e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012885,124368,0,0.65468,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0992,287.18,99566,339.8,0.4778,0.094305 + 37.978,146.4,-4.3803,-1.2578,3.89,1.2788,-33.808,-1.7379e-4,33.808,-3.1416,2.025,0.2309,0.7854,-7.5144e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011448,110510,0,0.51685,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0102,287.19,99589,339.81,0.39099,0.09435 + 38.456,144.42,-3.8832,1.0393,4.3806,1.0453,-34.79,-1.7379e-4,34.79,-3.1416,2.0783,0.1115,0.7854,-7.7326e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012891,124463,0,0.65555,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.07,287.2,99608,339.81,0.47835,0.094395 + 38.839,142.84,-4.3728,-1.2775,3.8851,1.3046,-35.606,-1.7379e-4,35.606,-3.1416,2.1797,0.2645,0.7854,-7.9139e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011433,110401,0,0.51573,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1988,287.21,99631,339.82,0.38327,0.09444 + 39.326,140.83,-3.8822,1.0069,4.3735,1.0263,-36.644,-1.7379e-4,36.644,-3.1416,2.083,0.1985,0.7854,-8.1447e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01287,124298,0,0.65367,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0994,287.22,99650,339.83,0.48718,0.094484 + 39.715,139.22,-4.3813,-1.2825,3.8824,1.2849,-37.449,-1.7379e-4,37.449,-3.1416,2.0521,0.079288,0.7854,-8.3235e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011424,110357,0,0.5152,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0469,287.23,99673,339.83,0.38913,0.094529 + 40.149,137.42,-3.922,1.0604,4.3852,1.1544,-38.295,-1.7379e-4,38.295,-3.1416,1.8545,0.45636,0.7854,-8.5116e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012904,124662,0,0.65737,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8681,287.25,99692,339.84,0.43311,0.094574 + 40.592,135.57,-4.4107,-1.1012,3.9235,1.1267,-39.141,-1.7379e-4,39.141,-3.1416,1.9602,0.23827,0.7854,-8.6997e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011545,111553,0,0.52633,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9619,287.26,99713,339.85,0.44377,0.094618 + 41.008,133.84,-3.9118,1.2012,4.4108,1.2039,-39.962,-1.7379e-4,39.962,-3.1416,1.9937,0.080551,0.7854,-8.8822e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012979,125427,0,0.66532,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9925,287.27,99735,339.85,0.41531,0.094663 + 41.44,132.05,-4.4064,-1.145,3.9126,1.1576,-40.807,-1.7379e-4,40.807,-3.1416,1.9202,0.17006,0.7854,-9.0701e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011512,111274,0,0.52359,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9169,287.28,99755,339.86,0.43193,0.094708 + 41.862,130.29,-3.9064,1.1831,4.4064,1.1831,-41.619,-1.7379e-4,41.619,-3.1416,1.9195,0.0017558,0.7854,-9.2504e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012965,125336,0,0.66421,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9195,287.29,99776,339.87,0.42261,0.094754 + 42.287,128.52,-4.4021,-1.1664,3.907,1.1767,-42.448,-1.7379e-4,42.448,-3.1416,1.9853,0.1549,0.7854,-9.4348e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011495,111147,0,0.52228,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9895,287.3,99797,339.88,0.42493,0.0948 + 42.661,126.96,-3.9603,1.183,4.4092,1.3391,-43.233,-1.7379e-4,43.233,-3.1416,2.2196,0.62744,0.7854,-9.6093e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012973,125450,0,0.66528,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2367,287.31,99818,339.88,0.3734,0.09486 + 43.161,124.87,-4.4185,-0.91634,3.9641,0.99547,-44.294,-1.7379e-4,44.294,-3.1416,2.0251,0.38895,0.7854,-9.8452e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011663,112800,0,0.53782,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0463,287.32,99836,339.89,0.5,0.094905 + 43.455,123.62,-4.0346,1.304,4.4397,1.6985,-44.843,-1.7379e-4,44.843,-3.1416,1.7047,1.0884,0.7854,-9.9672e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013062,126351,0,0.67473,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.5923,287.34,99860,339.9,0.29437,0.09495 + 43.955,121.53,-4.3263,-0.58334,4.0356,0.6172,-45.721,-1.7379e-4,45.721,-3.1416,1.8055,0.20161,0.7854,-1.0162e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011873,114863,0,0.55757,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7929,287.35,99875,339.9,0.5,0.094994 + 44.455,119.47,-3.9268,0.79899,4.3268,0.81421,-46.643,-1.7379e-4,46.643,-3.1416,1.8839,0.15672,0.7854,-1.0367e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012729,123171,0,0.64106,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8695,287.36,99900,339.91,0.5,0.095039 + 44.923,117.52,-4.4264,-1.0682,3.9269,1.0691,-47.529,-1.7379e-4,47.529,-3.1416,1.9045,0.044151,0.7854,-1.0564e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011552,111804,0,0.52814,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9037,287.37,99924,339.92,0.46766,0.095084 + 45.308,115.91,-3.9264,1.2983,4.4264,1.2984,-48.264,-1.7379e-4,48.264,-3.1416,1.9106,0.015794,0.7854,-1.0727e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013022,126045,0,0.67117,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9108,287.39,99947,339.93,0.38509,0.09513 + 45.733,114.14,-4.3737,-1.0514,3.9335,1.1754,-49.029,-1.7379e-4,49.029,-3.1416,1.6871,0.52554,0.7854,-1.0897e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011571,112023,0,0.5301,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6749,287.4,99966,339.93,0.42537,0.095175 + 46.182,112.29,-3.9042,1.0456,4.3764,1.1135,-49.825,-1.7379e-4,49.825,-3.1416,1.859,0.38298,0.7854,-1.1074e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012874,124654,0,0.6563,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8414,287.41,99986,339.94,0.44903,0.09522 + 46.611,110.5,-4.4026,-1.1625,3.9044,1.1661,-50.614,-1.7379e-4,50.614,-3.1416,1.8196,0.09206,0.7854,-1.125e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011485,111225,0,0.52246,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8175,287.42,100008,339.95,0.42878,0.095266 + 46.997,108.89,-3.9401,1.1976,4.407,1.2946,-51.353,-1.7379e-4,51.353,-3.1416,2.0095,0.49184,0.7854,-1.1414e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012964,125561,0,0.66575,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0164,287.43,100029,339.95,0.38621,0.095312 + 47.491,106.83,-4.4332,-0.99875,3.9408,1.0128,-52.325,-1.7379e-4,52.325,-3.1416,1.9265,0.16808,0.7854,-1.163e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011592,112292,0,0.53243,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9343,287.44,100048,339.96,0.49369,0.095357 + 47.863,105.27,-3.9335,1.3437,4.4332,1.3444,-53.044,-1.7379e-4,53.044,-3.1416,1.9435,0.045586,0.7854,-1.179e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01304,126343,0,0.67392,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9447,287.46,100072,339.97,0.3719,0.095402 + 48.346,103.25,-4.429,-1.0259,3.9339,1.0352,-53.999,-1.7379e-4,53.999,-3.1416,2.0102,0.138,0.7854,-1.2002e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011571,112127,0,0.53075,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0053,287.47,100090,339.97,0.483,0.095447 + 48.722,101.68,-3.9292,1.3264,4.429,1.3271,-54.754,-1.7379e-4,54.754,-3.1416,1.9944,0.041865,0.7854,-1.217e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013027,126258,0,0.67288,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9935,287.48,100114,339.98,0.37677,0.095492 + 49.128,99.998,-4.3415,-1.0154,3.9416,1.2315,-55.621,-1.7379e-4,55.621,-3.1416,2.2773,0.69682,0.7854,-1.2363e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011593,112376,0,0.53299,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3059,287.49,100132,339.99,0.40601,0.095537 + 49.628,97.939,-3.8933,0.89632,4.3424,0.92208,-56.787,-1.7379e-4,56.787,-3.1416,2.3855,0.21642,0.7854,-1.2622e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012772,123819,0,0.64701,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3651,287.5,100152,340,0.5,0.095582 + 50.043,96.222,-4.3902,-1.1982,3.8938,1.2058,-57.764,-1.7379e-4,57.764,-3.1416,2.3295,0.1351,0.7854,-1.2839e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011452,111046,0,0.52034,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3244,287.51,100176,340,0.41467,0.095627 + 50.443,94.558,-3.9302,1.1503,4.3946,1.2504,-58.656,-1.7379e-4,58.656,-3.1416,2.1335,0.49008,0.7854,-1.3037e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012925,125344,0,0.66289,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1331,287.52,100196,340.01,0.39988,0.095672 + 50.894,92.681,-4.3913,-1.0223,3.9349,1.1085,-59.575,-1.7379e-4,59.575,-3.1416,1.9402,0.42864,0.7854,-1.3242e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011573,112246,0,0.53154,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9417,287.54,100216,340.02,0.45105,0.095718 + 51.329,90.88,-3.8916,1.1487,4.3913,1.1494,-60.423,-1.7379e-4,60.423,-3.1416,1.9585,0.042205,0.7854,-1.343e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012915,125285,0,0.66214,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9571,287.55,100238,340.02,0.435,0.095764 + 51.713,89.299,-4.3459,-1.1839,3.8994,1.303,-61.215,-1.7379e-4,61.215,-3.1416,2.1674,0.54423,0.7854,-1.3606e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011468,111265,0,0.52218,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2042,287.56,100259,340.03,0.38373,0.095808 + 52.202,87.283,-3.8882,0.93469,4.3491,1.0211,-62.227,-1.7379e-4,62.227,-3.1416,1.9661,0.41099,0.7854,-1.3831e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01279,124113,0,0.64967,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0011,287.57,100278,340.04,0.48969,0.095853 + 52.558,85.825,-4.3079,-1.1796,3.9035,1.4053,-62.975,-1.7379e-4,62.975,-3.1416,2.2379,0.76395,0.7854,-1.3997e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011479,111412,0,0.52345,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3105,287.58,100301,340.05,0.35578,0.095899 + 52.96,84.157,-3.9916,0.78675,4.3257,1.2436,-63.797,-1.7379e-4,63.797,-3.1416,1.8507,0.96306,0.7854,-1.418e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012721,123476,0,0.6429,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8463,287.59,100318,340.05,0.40207,0.095944 + 53.46,82.068,-4.3626,-0.7421,3.9941,0.80772,-64.762,-1.7379e-4,64.762,-3.1416,2.0102,0.31891,0.7854,-1.4394e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011745,114025,0,0.54819,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9911,287.6,100338,340.06,0.5,0.095988 + 53.942,80.084,-3.8718,1.0186,4.3634,1.0376,-65.708,-1.7379e-4,65.708,-3.1416,1.9149,0.19768,0.7854,-1.4605e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012831,124588,0,0.65438,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9305,287.62,100362,340.07,0.48189,0.096033 + 54.321,78.527,-4.3537,-1.2727,3.8751,1.3205,-66.407,-1.7379e-4,66.407,-3.1416,1.7816,0.3521,0.7854,-1.476e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011395,110663,0,0.51622,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7551,287.63,100386,340.07,0.37863,0.096077 + 54.821,76.472,-3.8656,0.97627,4.3538,0.97892,-67.289,-1.7379e-4,67.289,-3.1416,1.7455,0.072062,0.7854,-1.4956e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012802,124348,0,0.65173,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7525,287.64,100404,340.08,0.5,0.096122 + 55.203,74.899,-4.364,-1.3039,3.8658,1.3079,-67.949,-1.7379e-4,67.949,-3.1416,1.7067,0.10149,0.7854,-1.5103e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011367,110429,0,0.51393,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6994,287.65,100428,340.09,0.3823,0.096167 + 55.645,73.071,-3.9041,1.0401,4.3677,1.1306,-68.747,-1.7379e-4,68.747,-3.1416,1.9028,0.44323,0.7854,-1.528e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012843,124779,0,0.65612,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8851,287.66,100447,340.09,0.44223,0.09621 + 56.07,71.31,-4.382,-1.1247,3.9072,1.1765,-69.525,-1.7379e-4,69.525,-3.1416,1.756,0.3454,0.7854,-1.5453e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011488,111638,0,0.52514,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7474,287.68,100468,340.1,0.42499,0.096255 + 56.458,69.699,-3.9403,1.141,4.3887,1.2913,-70.25,-1.7379e-4,70.25,-3.1416,1.9901,0.60477,0.7854,-1.5614e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012904,125415,0,0.66268,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9981,287.69,100489,340.11,0.3872,0.096371 + 56.949,67.645,-4.4129,-0.96094,3.943,1.0166,-71.189,-1.7379e-4,71.189,-3.1416,1.8269,0.33185,0.7854,-1.5823e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011593,112692,0,0.535,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8423,287.7,100508,340.11,0.49182,0.096418 + 57.333,66.046,-3.92,1.2846,4.4137,1.3031,-71.873,-1.7379e-4,71.873,-3.1416,1.743,0.2188,0.7854,-1.5975e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012977,126164,0,0.67048,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7399,287.71,100532,340.12,0.38369,0.096463 + 57.745,64.343,-4.3453,-1.0319,3.9303,1.2129,-72.646,-1.7379e-4,72.646,-3.1416,2.0058,0.63748,0.7854,-1.6147e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011555,112360,0,0.53173,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0278,287.72,100550,340.13,0.41223,0.096508 + 58.235,62.328,-3.8774,0.95503,4.3478,1.0204,-73.672,-1.7379e-4,73.672,-3.1416,2.1818,0.35929,0.7854,-1.6375e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012782,124310,0,0.6508,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1508,287.73,100570,340.14,0.49001,0.096553 + 58.521,61.172,-4.2041,-1.1419,3.9223,1.7475,-74.242,-1.7379e-4,74.242,-3.1416,1.8033,1.3229,0.7854,-1.6502e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011531,112163,0,0.52976,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.5898,287.74,100594,340.14,0.28611,0.096599 + 59.021,59.103,-4.0719,0.26427,4.2041,0.26964,-75.151,-1.7379e-4,75.151,-3.1416,1.8301,0.053529,0.7854,-1.6703e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01236,120233,0,0.60869,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8257,287.75,100608,340.15,0.5,0.096644 + 59.521,57.024,-4.2426,-0.34126,4.0788,0.64592,-76.134,-1.7379e-4,76.134,-3.1416,2.1043,0.54841,0.7854,-1.6922e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011991,116666,0,0.57304,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.066,287.77,100632,340.16,0.5,0.096689 + 60.021,54.961,-4.011,0.46323,4.2467,0.6461,-77.13,-1.7379e-4,77.13,-3.1416,1.8791,0.45039,0.7854,-1.7143e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012484,121487,0,0.6213,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9182,287.78,100656,340.16,0.5,0.096735 + 60.521,52.877,-4.3265,-0.6311,4.0124,0.67839,-78.038,-1.7379e-4,78.038,-3.1416,1.7547,0.24884,0.7854,-1.7345e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011795,114804,0,0.55477,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7703,287.79,100680,340.17,0.5,0.096781 + 61.021,50.823,-3.8875,0.87809,4.3308,0.99924,-78.975,-1.7379e-4,78.975,-3.1416,1.9931,0.47689,0.7854,-1.7554e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012731,123934,0,0.64643,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9478,287.81,100705,340.18,0.5,0.096826 + 61.442,49.081,-4.3873,-1.1872,3.8875,1.1875,-79.812,-1.7379e-4,79.812,-3.1416,1.9804,0.030155,0.7854,-1.7739e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011427,111264,0,0.52096,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9795,287.82,100729,340.19,0.42104,0.096871 + 61.868,47.322,-3.8883,1.1737,4.3874,1.1761,-80.647,-1.7379e-4,80.647,-3.1416,1.9484,0.075312,0.7854,-1.7925e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012897,125589,0,0.66367,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9503,287.83,100749,340.2,0.42514,0.096916 + 62.278,45.628,-4.3701,-1.1744,3.8911,1.2188,-81.419,-1.7379e-4,81.419,-3.1416,1.8147,0.32595,0.7854,-1.8097e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011438,111397,0,0.5221,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8016,287.84,100770,340.2,0.41024,0.096962 + 62.736,43.74,-3.8701,1.0911,4.3701,1.0911,-82.25,-1.7379e-4,82.25,-3.1416,1.8144,5.8373e-4,0.7854,-1.8282e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012845,125127,0,0.65866,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8145,287.85,100790,340.21,0.45825,0.097007 + 63.1,42.251,-4.32,-1.2374,3.8796,1.3752,-82.95,-1.7379e-4,82.95,-3.1416,2.0326,0.60004,0.7854,-1.8437e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011403,111098,0,0.51919,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0854,287.87,100812,340.22,0.36359,0.097052 + 63.6,40.197,-3.8964,0.84713,4.3205,0.86118,-83.985,-1.7379e-4,83.985,-3.1416,2.11,0.15492,0.7854,-1.8667e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012699,123737,0,0.64398,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0954,287.88,100829,340.22,0.5,0.097098 + 64.038,38.379,-4.3957,-1.1385,3.8965,1.1403,-84.905,-1.7379e-4,84.905,-3.1416,2.0821,0.063646,0.7854,-1.8871e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011453,111614,0,0.52391,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0814,287.89,100854,340.23,0.43849,0.097144 + 64.427,36.762,-3.9161,1.2323,4.398,1.2849,-85.687,-1.7379e-4,85.687,-3.1416,1.9406,0.36362,0.7854,-1.9045e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012926,125996,0,0.66756,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9373,287.9,100875,340.24,0.38914,0.09719 + 64.855,34.991,-4.3574,-1.0311,3.9238,1.1684,-86.568,-1.7379e-4,86.568,-3.1416,2.1758,0.54947,0.7854,-1.9241e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011532,112423,0,0.53143,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1859,287.91,100894,340.24,0.42795,0.097235 + 65.326,33.055,-3.8676,1.0405,4.3582,1.0621,-87.569,-1.7379e-4,87.569,-3.1416,2.0753,0.21352,0.7854,-1.9464e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012809,124887,0,0.65573,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.09,287.92,100915,340.25,0.47075,0.09728 + 65.72,31.432,-4.3645,-1.2599,3.8681,1.2676,-88.398,-1.7379e-4,88.398,-3.1416,2.1304,0.13979,0.7854,-1.9648e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011368,110858,0,0.51663,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1385,287.94,100937,340.26,0.39444,0.097326 + 66.068,29.982,-3.9769,1.1152,4.3796,1.4384,-89.084,-1.7379e-4,89.084,-3.1416,1.8146,0.90843,0.7854,-1.98e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012871,125533,0,0.6624,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7674,287.95,100956,340.27,0.34761,0.097372 + 66.568,27.898,-4.3584,-0.763,3.9794,0.82782,-90.031,-1.7379e-4,90.031,-3.1416,1.9752,0.32112,0.7854,-2.0011e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011695,114074,0,0.54694,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9558,287.96,100973,340.27,0.5,0.097462 + 67.042,25.952,-3.8606,1.051,4.3585,1.0557,-90.956,-1.7379e-4,90.956,-3.1416,1.9279,0.099853,0.7854,-2.0216e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012809,124963,0,0.65627,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9351,287.97,100998,340.28,0.47362,0.097509 + 67.419,24.405,-4.3429,-1.279,3.8639,1.326,-91.658,-1.7379e-4,91.658,-3.1416,1.7959,0.35007,0.7854,-2.0372e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011355,110796,0,0.51584,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7694,287.98,101021,340.29,0.37707,0.097645 + 67.84,22.666,-3.9236,0.99629,4.3507,1.1881,-92.471,-1.7379e-4,92.471,-3.1416,2.0683,0.64731,0.7854,-2.0553e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012785,124770,0,0.65411,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0561,287.99,101039,340.29,0.42084,0.097691 + 68.273,20.875,-4.349,-0.98242,3.9328,1.1546,-93.423,-1.7379e-4,93.423,-3.1416,2.331,0.60659,0.7854,-2.0765e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011557,112803,0,0.53459,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.338,288,101059,340.3,0.43305,0.097736 + 68.767,18.85,-3.8495,1.0112,4.3491,1.0122,-94.569,-1.7379e-4,94.569,-3.1416,2.309,0.044599,0.7854,-2.102e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01278,124758,0,0.65384,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3131,288.01,101080,340.31,0.49397,0.097781 + 69.138,17.33,-4.3417,-1.3261,3.851,1.3472,-95.443,-1.7379e-4,95.443,-3.1416,2.3972,0.23765,0.7854,-2.1214e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011316,110488,0,0.51276,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.4168,288.03,101104,340.31,0.37113,0.097827 + 69.638,15.281,-3.8524,0.97858,4.3419,0.98357,-96.629,-1.7379e-4,96.629,-3.1416,2.3477,0.09892,0.7854,-2.1477e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012758,124584,0,0.65189,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3573,288.04,101122,340.32,0.5,0.097871 + 69.967,13.943,-4.2721,-1.2744,3.8703,1.5181,-97.357,-1.7379e-4,97.357,-3.1416,2.076,0.82495,0.7854,-2.1639e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011372,111072,0,0.51809,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9752,288.05,101146,340.33,0.32936,0.097916 + 70.467,11.888,-3.9512,0.64189,4.2734,0.6928,-98.363,-1.7379e-4,98.363,-3.1416,1.9457,0.26068,0.7854,-2.1863e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012557,122653,0,0.63171,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9694,288.06,101161,340.33,0.5,0.097961 + 70.967,9.8033,-4.3857,-0.869,3.9516,0.87964,-99.318,-1.7379e-4,99.318,-3.1416,1.8774,0.13641,0.7854,-2.2075e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011611,113434,0,0.54025,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8853,288.07,101186,340.34,0.5,0.098006 + 71.38,8.0939,-3.8873,1.206,4.3858,1.2099,-100.1,-1.7379e-4,100.1,-3.1416,1.9179,0.097834,0.7854,-2.2249e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012886,125918,0,0.66564,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9164,288.09,101210,340.35,0.41325,0.098052 + 71.813,6.3048,-4.386,-1.1531,3.8875,1.1561,-100.92,-1.7379e-4,100.92,-3.1416,1.8824,0.082127,0.7854,-2.2432e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011422,111624,0,0.52304,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.881,288.1,101230,340.36,0.43251,0.098096 + 72.163,4.8448,-3.9539,1.2343,4.3953,1.428,-101.63,-1.7379e-4,101.63,-3.1416,2.1338,0.71825,0.7854,-2.2588e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012914,126223,0,0.66873,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1677,288.11,101251,340.36,0.35013,0.098142 + 72.663,2.7622,-4.3762,-0.84462,3.9571,0.91847,-102.65,-1.7379e-4,102.65,-3.1416,1.9534,0.36083,0.7854,-2.2815e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011626,113651,0,0.54211,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9746,288.12,101268,340.37,0.5,0.098187 + 73.004,1.3382,-3.9675,1.1974,4.389,1.4647,-103.37,-1.7379e-4,103.37,-3.1416,2.2414,0.84363,0.7854,-2.2975e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012895,126077,0,0.66705,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2889,288.13,101293,340.38,0.34136,0.098233 + 73.462,-0.55847,-4.3125,-0.7531,3.9825,1.0914,-104.48,-1.7379e-4,104.48,-3.1416,2.6033,0.78994,0.7854,-2.3221e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.0117,114412,0,0.54928,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.5876,288.14,101309,340.38,0.45813,0.098278 + + + + + + Simulation 5 + RK4Simulator + BarrowmanCalculator + + 5d9a2765-395d-4d9c-9678-38eed0988c72 + 1.0 + 0.0 + 0.0 + 2.0 + 0.1 + 0.0 + 45.0 + 0.0 + flat + + 0.05 + + + + + + + + + + + + + + 0,0,0,-7.6644,0,7.6644,0,0,0,0,0,0,0.7854,0,1.5708,0,0,0,0.07124,0.0013955,2.0267e-5,NaN,0.25972,NaN,0.006561,64165,0.15258,0,1.0793,0,0.66352,0.29583,0.12001,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,0.025,4.9087e-4,1.5708,0,NaN,288.15,101325,340.39,0.01,0.0035114 + 0.24963,1.8681,22.111,75.653,22.111,75.683,-0.00227,0,0.00227,3.1416,0.10395,2.1509,0.7854,0,0.089367,0,0.26575,-2.2689e-6,0.069176,0.0013673,2.0183e-5,0.3059,0.25634,1.9824,0.065252,638045,6.0462,0.13478,0.85322,0.90889,0.43637,0.29629,0.12055,1.2232,2.4133,4.561e-4,0,0,0,0,0.01114,0.025,4.9087e-4,1.5653,3.1272e-5,NaN,288.14,101303,340.38,0.01067,0.0080708 + 0.49898,9.3692,36.971,49.194,36.992,50.586,0.055551,-1.1696e-6,0.055551,-2.1054e-5,1.2533,11.787,0.7854,1.2347e-8,0.046912,-1.3974e-8,0.1029,-2.174e-5,0.06762,0.001345,2.012e-5,0.31198,0.25365,2.3331,0.10906,1065692,4.4216,0.36086,0.85472,0.87192,0.43604,0.29713,0.12155,0.60189,1.404,2.079e-4,0,0,0,0,5.9806e-4,0.025,4.9087e-4,1.4349,-4.6839e-5,NaN,288.09,101215,340.35,0.0064638,0.011511 + 0.74515,19.928,48.665,44.539,48.721,44.783,0.55442,-1.9535e-5,0.55442,-3.5235e-5,2.3468,4.6689,0.7854,1.2323e-7,0.0011641,-3.9607e-8,0.53829,-0.0010958,0.066333,0.0013258,2.0068e-5,0.31958,0.25134,2.7298,0.14358,1401720,4.2408,0.6136,0.85644,0.85645,0.43566,0.2981,0.12268,0.013855,0.028565,1.7883e-4,0,0,0,0,0.0094444,0.025,4.9087e-4,1.4824,1.5267e-5,NaN,288.02,101091,340.31,0.024397,0.029123 + 0.99878,33.718,60.04,43.07,60.16,43.226,1.3745,-1.693e-5,1.3745,-1.2318e-5,3.8058,3.6661,0.7854,3.055e-7,0.0056066,-1.2551e-7,0.30781,-1.101e-4,0.064966,0.0013045,2.0012e-5,0.31885,0.24877,2.8028,0.17727,1728612,4.3778,0.93652,0.85857,0.85884,0.43519,0.29929,0.12409,0.067303,0.1866,1.4279e-4,0,0,0,0,0.0020264,0.025,4.9087e-4,1.4803,3.3551e-5,NaN,287.93,100930,340.26,0.017036,0.033664 + 1.238,49.261,69.696,34.303,69.875,34.673,2.4501,-1.3164e-5,2.4501,-5.373e-6,4.9925,5.0528,0.7854,5.4457e-7,0.0023973,-1.5981e-7,-0.2761,-5.2123e-5,0.06371,0.0012842,1.9961e-5,0.31947,0.24633,2.9256,0.20584,2004408,4.1045,1.2632,0.86072,0.86077,0.43471,0.30051,0.12551,0.028648,0.084577,1.2499e-4,0,0,0,0,-0.0012098,0.025,4.9087e-4,1.4716,4.6995e-6,NaN,287.83,100747,340.19,0.028718,0.037272 + 1.4889,67.85,78.48,34.212,78.724,34.588,3.8639,1.0757e-6,3.8639,2.7841e-7,6.2037,5.0864,0.7854,8.5881e-7,0.0017222,-1.1431e-7,0.043045,-1.4901e-4,0.062398,0.0012622,1.9908e-5,0.31964,0.24366,3.0392,0.23189,2254415,4.3708,1.6038,0.86297,0.863,0.43421,0.30177,0.12699,0.020577,0.062465,3.0031e-4,0,0,0,0,2.3178e-5,0.025,4.9087e-4,1.4696,-6.0445e-5,NaN,287.71,100529,340.12,0.028764,0.039809 + 1.7322,87.932,86.455,30.843,86.758,31.105,5.4992,6.703e-6,5.4992,1.2189e-6,7.2412,4.0284,0.7854,1.2223e-6,4.4052e-4,-4.8238e-7,0.03016,-0.0012964,0.061166,0.0012406,1.9858e-5,0.31993,0.24106,3.1548,0.25563,2480862,4.448,1.9495,0.86526,0.86526,0.43371,0.30306,0.12849,0.0052574,0.01669,-7.2385e-5,0,0,0,0,9.3676e-6,0.025,4.9087e-4,1.4673,1.0063e-4,NaN,287.58,100294,340.04,0.05,0.040994 + 1.9668,108.44,84.321,-40.261,84.631,40.409,7.2492,3.5577e-5,7.2492,4.9077e-6,7.2426,3.4542,0.7854,1.6113e-6,1.0316e-4,-7.0281e-7,0.033794,0.0029639,0.060443,0.0012276,1.9829e-5,0.31997,0.23948,3.2198,0.24958,2417817,0,1.8526,0.86466,0.86466,0.43384,0.30272,0.1281,0.0012301,0.0039887,-4.441e-4,0,0,0,0,1.2344e-5,0.025,4.9087e-4,1.4593,-8.3433e-5,NaN,287.45,100053,339.96,0.05,0.042114 + 2.2168,128.33,75.094,-33.898,75.373,33.989,8.9608,6.4614e-6,8.9608,7.2108e-7,6.4716,2.478,0.7854,1.9917e-6,0.0010433,-1.7955e-7,-0.027089,-4.5642e-4,0.060443,0.0012276,1.9829e-5,0.31974,0.23948,3.2105,0.22243,2151024,0,1.4638,0.86213,0.86214,0.4344,0.3013,0.12643,0.012447,0.039713,-1.7817e-4,0,0,0,0,-9.9901e-6,0.025,4.9087e-4,1.4539,8.7129e-5,NaN,287.32,99820,339.88,0.05,0.04295 + 2.4628,145.83,67.376,-29.114,67.632,29.183,10.477,-4.5506e-5,10.477,-4.3435e-6,5.8753,2.0025,0.7854,2.3286e-6,8.2575e-4,-1.6607e-7,0.03806,4.9899e-4,0.060443,0.0012276,1.9829e-5,0.31973,0.23948,3.2102,0.19957,1926901,0,1.1732,0.86022,0.86023,0.43482,0.30023,0.12518,0.0098389,0.031734,2.2652e-4,0,0,0,0,2.4509e-5,0.025,4.9087e-4,1.456,1.0578e-4,NaN,287.2,99615,339.81,0.05,0.04379 + 2.7144,161.9,60.549,-25.292,60.787,25.385,11.888,-9.1051e-5,11.888,-7.6593e-6,5.3768,2.1739,0.7854,2.6422e-6,0.0018269,-4.3013e-7,-0.049106,2.0188e-4,0.060443,0.0012276,1.9829e-5,0.31952,0.23948,3.2015,0.17942,1729909,0,0.94489,0.85872,0.85875,0.43515,0.29938,0.12418,0.02179,0.070095,-4.6275e-4,0,0,0,0,-5.0497e-5,0.025,4.9087e-4,1.4567,-2.3834e-5,NaN,287.1,99426,339.75,0.044447,0.044854 + 2.9625,176.18,54.639,-22.381,54.862,22.459,13.166,-1.4079e-4,13.166,-1.0694e-5,4.9456,1.8725,0.7854,2.9263e-6,0.0021348,-3.2379e-7,0.036394,-3.8775e-4,0.060443,0.0012276,1.9829e-5,0.31943,0.23948,3.1982,0.16202,1560131,0,0.76813,0.85755,0.85759,0.43541,0.29872,0.12341,0.025463,0.080972,-3.2013e-4,0,0,0,0,3.4027e-5,0.025,4.9087e-4,1.4527,1.6677e-5,NaN,287,99259,339.69,0.047266,0.045886 + 3.2098,189.03,49.391,-20.125,49.604,20.163,14.342,-1.6292e-4,14.342,-1.1359e-5,4.5888,1.2241,0.7854,3.1878e-6,0.001084,-2.849e-7,-0.032005,1.8858e-4,0.060443,0.0012276,1.9829e-5,0.3196,0.23948,3.2047,0.1467,1410980,0,0.62806,0.85662,0.85663,0.43562,0.2982,0.1228,0.0129,0.04173,1.0758e-4,0,0,0,0,-3.2107e-5,0.025,4.9087e-4,1.4377,-3.2417e-5,NaN,286.92,99108,339.64,0.05,0.046729 + 3.4598,200.77,44.611,-18.278,44.813,18.296,15.446,-1.9029e-4,15.446,-1.232e-5,4.2497,0.81948,0.7854,3.433e-6,0.0039636,-2.9275e-7,-0.080338,-1.265e-5,0.060443,0.0012276,1.9829e-5,0.31907,0.23948,3.1835,0.13281,1276081,0,0.51369,0.85585,0.85599,0.43579,0.29777,0.12229,0.047389,0.15104,3.9785e-4,0,0,0,0,-2.4689e-4,0.025,4.9087e-4,1.4191,-1.4902e-5,NaN,286.84,98970,339.6,0.03782,0.047572 + 3.6976,210.88,40.461,-16.738,40.657,16.763,16.426,-2.1581e-4,16.426,-1.3138e-5,3.9797,0.90702,0.7854,3.6509e-6,0.0017752,-1.9563e-7,-0.041038,-1.3193e-4,0.060443,0.0012276,1.9829e-5,0.31944,0.23948,3.1984,0.12057,1157402,0,0.42248,0.85524,0.85527,0.43592,0.29743,0.12189,0.021139,0.067673,-4.8087e-4,0,0,0,0,-7.8188e-5,0.025,4.9087e-4,1.416,6.2617e-5,NaN,286.78,98852,339.56,0.05,0.048482 + 3.9476,220.49,36.452,-15.374,36.643,15.412,17.393,-2.0991e-4,17.393,-1.2069e-5,3.7388,1.0814,0.7854,3.8658e-6,0.002826,-1.639e-7,0.040591,1.4429e-4,0.060443,0.0012276,1.9829e-5,0.31924,0.23948,3.1904,0.10867,1042279,0,0.34262,0.8547,0.85477,0.43604,0.29713,0.12154,0.033705,0.10718,-4.0125e-4,0,0,0,0,9.4183e-5,0.025,4.9087e-4,1.4184,-7.84e-5,NaN,286.72,98739,339.52,0.05,0.049347 + 4.1976,229.13,32.739,-14.348,32.929,14.363,18.299,-2.1093e-4,18.299,-1.1527e-5,3.5365,0.63875,0.7854,4.0673e-6,0.002204,-1.5882e-7,-0.025654,-2.167e-4,0.060443,0.0012276,1.9829e-5,0.31934,0.23948,3.1944,0.097816,937457,0,0.27716,0.85426,0.85431,0.43614,0.29688,0.12124,0.026253,0.084121,-4.8e-4,0,0,0,0,-4.644e-5,0.025,4.9087e-4,1.4004,3.4223e-5,NaN,286.66,98638,339.49,0.05,0.05019 + 4.4476,236.88,29.276,-13.429,29.466,13.445,19.159,-2.0761e-4,19.159,-1.0836e-5,3.3373,0.64844,0.7854,4.2584e-6,4.365e-4,-1.9259e-7,-0.10849,2.2868e-4,0.060443,0.0012276,1.9829e-5,0.31965,0.23948,3.2067,0.087705,839964,0,0.22251,0.85389,0.8539,0.43622,0.29667,0.121,0.0051827,0.01751,2.6263e-4,0,0,0,0,-0.0010333,0.025,4.9087e-4,1.3877,-9.0584e-6,NaN,286.61,98547,339.46,0.05,0.051029 + 4.6929,243.66,26.073,-12.651,26.27,12.672,19.963,-2.083e-4,19.963,-1.0434e-5,3.211,0.72466,0.7854,4.4371e-6,0.0041313,-2.0718e-7,0.0026489,1.8618e-4,0.060443,0.0012276,1.9829e-5,0.31898,0.23948,3.18,0.078308,749518,0,0.17721,0.85359,0.85373,0.43629,0.2965,0.1208,0.049358,0.1568,-3.2171e-4,0,0,0,0,7.7282e-7,0.025,4.9087e-4,1.3792,1.4911e-6,NaN,286.57,98467,339.43,0.05,0.051881 + 4.9429,249.79,22.993,-12.049,23.192,12.06,20.742,-2.0983e-4,20.742,-1.0116e-5,3.0276,0.51467,0.7854,4.6103e-6,4.187e-4,-2.6744e-7,0.2146,0.0033498,0.060443,0.0012276,1.9829e-5,0.31964,0.23948,3.2062,0.069422,664094,0,0.1391,0.85333,0.85333,0.43635,0.29635,0.12063,0.0049699,0.00953,-3.594e-4,0,0,0,0,0.0064548,0.025,4.9087e-4,1.3511,-4.0942e-5,NaN,286.53,98395,339.4,0.05,0.052809 + 5.177,254.85,20.218,-11.58,20.436,11.584,21.445,-2.1967e-4,21.445,-1.0243e-5,2.9793,0.28797,0.7854,4.7664e-6,0.0058133,-3.1161e-7,0.039038,4.8798e-5,0.060443,0.0012276,1.9829e-5,0.31867,0.23948,3.1677,0.061277,585908,0,0.10832,0.85312,0.85341,0.4364,0.29624,0.12049,0.06964,0.22072,-2.2761e-4,0,0,0,0,2.7419e-4,0.025,4.9087e-4,1.3285,5.6608e-5,NaN,286.49,98336,339.39,0.05,0.053653 + 5.4242,259.5,17.43,-11.029,17.66,11.05,22.166,-2.1854e-4,22.166,-9.8592e-6,2.8417,0.6902,0.7854,4.9268e-6,0.019378,-2.9608e-7,0.14154,2.2089e-4,0.060443,0.0012276,1.9829e-5,0.31634,0.23948,3.0743,0.053181,508287,0,0.081797,0.85294,0.85607,0.43644,0.29613,0.12037,0.23751,0.7257,1.5843e-4,0,0,0,0,0.004786,0.025,4.9087e-4,1.3277,-7.2412e-5,NaN,286.46,98282,339.37,0.049437,0.054494 + 5.6736,263.51,14.715,-10.77,14.965,10.772,22.858,-2.2958e-4,22.858,-1.0044e-5,2.7255,0.20939,0.7854,5.0805e-6,0.006671,-2.92e-7,-0.2865,1.5339e-4,0.060443,0.0012276,1.9829e-5,0.31851,0.23948,3.1613,0.045398,433746,0,0.059378,0.85279,0.85317,0.43647,0.29605,0.12027,0.080021,0.28018,-2.0983e-4,0,0,0,0,-0.02691,0.025,4.9087e-4,1.2634,-1.4908e-5,NaN,286.44,98235,339.35,0.05,0.055335 + 5.9236,266.85,12.062,-10.463,12.354,10.466,23.532,-2.4232e-4,23.532,-1.0297e-5,2.6709,0.21344,0.7854,5.2304e-6,0.0040982,-2.8386e-7,-0.21881,-7.5386e-4,0.060443,0.0012276,1.9829e-5,0.31896,0.23948,3.1793,0.03808,363711,0,0.041742,0.85267,0.85281,0.4365,0.29598,0.12019,0.04894,0.17797,-3.7105e-4,0,0,0,0,-0.022311,0.025,4.9087e-4,1.1998,3.0265e-5,NaN,286.42,98195,339.34,0.05,0.056228 + 6.1736,269.54,9.4878,-10.164,9.8349,10.17,24.19,-2.4838e-4,24.19,-1.0268e-5,2.5896,0.35724,0.7854,5.3767e-6,0.025726,-2.8205e-7,0.31476,1.1508e-4,0.060443,0.0012276,1.9829e-5,0.31528,0.23948,3.0318,0.031203,297956,0,0.028756,0.86972,0.87526,0.45366,0.29593,0.12013,0.31862,0.89763,-4.8958e-4,0,0,0,0,0.068767,0.025,4.9087e-4,1.1366,-1.0647e-5,NaN,286.4,98164,339.33,0.05,0.057089 + 6.4236,271.6,6.9673,-9.9767,7.4077,9.9814,24.828,-2.5478e-4,24.828,-1.0262e-5,2.5161,0.30646,0.7854,5.5184e-6,0.042309,-2.8428e-7,0.42391,2.0418e-4,0.060443,0.0012276,1.9829e-5,0.31263,0.23948,2.9259,0.024383,232791,0,0.018268,0.89598,0.91082,0.48002,0.29589,0.12008,0.53834,1.371,3.2615e-4,0,0,0,0,0.20427,0.025,4.9087e-4,1.0435,-4.0965e-5,NaN,286.38,98140,339.32,0.05,0.057999 + 6.6736,273.03,4.5052,-9.7399,5.1138,9.749,25.446,-2.6486e-4,25.446,-1.0409e-5,2.4196,0.42098,0.7854,5.6557e-6,0.13014,-2.8536e-7,0.63504,8.3203e-5,0.060443,0.0012276,1.9829e-5,0.3008,0.23948,2.453,0.018821,179664,0,0.012421,0.92613,1.0396,0.51022,0.29586,0.12005,1.888,3.8621,1.6611e-4,0,0,0,0,0.76944,0.025,4.9087e-4,0.91321,-4.0529e-5,NaN,286.38,98123,339.31,0.05,0.058952 + 6.9236,273.86,2.1007,-9.466,3.1199,9.479,26.037,-2.777e-4,26.037,-1.0666e-5,2.3066,0.49588,0.7854,5.7871e-6,0.2838,-2.8326e-7,0.87479,1.1368e-5,0.060443,0.0012276,1.9829e-5,0.2865,0.23948,1.8807,0.014503,138431,0,0.0088363,0.95945,1.2457,0.54358,0.29584,0.12003,4.9664,6.8816,3.7221e-4,0,0,0,0,2.4591,0.025,4.9087e-4,0.72486,-2.9052e-5,NaN,286.37,98113,339.31,0.05,0.059818 + 7.1736,274.09,-0.21137,-9.0112,2.1849,9.0268,26.597,-2.9167e-4,26.597,-1.0966e-5,2.1747,0.53173,0.7854,5.9117e-6,0.52569,-2.8103e-7,1.0549,-2.6923e-5,0.060443,0.0012276,1.9829e-5,0.27362,0.23948,1.3655,0.013308,127024,0,0.0064058,0.97117,1.0725,0.55531,0.29584,0.12002,9.6127,1.8195,-3.1013e-4,0,0,0,0,4.2468,0.025,4.9087e-4,0.47886,-1.7296e-5,NaN,286.37,98110,339.31,0.049636,0.060783 + 7.4222,273.77,-2.3739,-8.2706,3.1402,8.2816,27.122,-3.0557e-4,27.122,-1.1266e-5,2.0556,0.42714,0.7854,6.0284e-6,0.72923,-2.8128e-7,1.0054,-3.8725e-5,0.060443,0.0012276,1.9829e-5,0.26803,0.23948,1.1421,0.014329,136781,0,0.0050328,0.96107,0.72673,0.54519,0.29584,0.12003,13.885,-3.5021,-4.0239e-5,0,0,0,0,3.3275,0.025,4.9087e-4,0.21915,-8.2419e-6,NaN,286.37,98114,339.31,0.05,0.061995 + 7.6722,272.91,-4.526,-8.6951,4.9484,8.6953,27.627,-3.1885e-4,27.627,-1.1541e-5,2.0005,0.064903,0.7854,6.1406e-6,0.81914,-2.8024e-7,1.0625,-4.6249e-5,0.060443,0.0012276,1.9829e-5,0.31096,0.23948,2.8594,0.017772,169652,0,0.0059181,0.93319,0.55552,0.51729,0.29586,0.12004,6.2857,3.6898,4.1338e-5,0,0,0,0,2.4155,0.025,4.9087e-4,-0.02976,2.714e-8,NaN,286.38,98124,339.31,0.049282,0.06289 + 7.9031,271.64,-6.3036,-6.2427,6.6332,6.305,28.092,-3.3023e-4,28.092,-1.1755e-5,2.0649,0.88445,0.7854,6.2438e-6,0.72545,-2.8047e-7,1.1323,-5.1126e-5,0.060443,0.0012276,1.9829e-5,0.26811,0.23948,1.1451,0.021815,208273,0,0.01113,0.90859,0.69328,0.49266,0.29587,0.12006,13.803,-1.8782,-9.0343e-5,0,0,0,0,1.8208,0.025,4.9087e-4,-0.29352,7.919e-6,NaN,286.38,98139,339.32,0.046242,0.063794 + 8.1566,269.84,-7.926,-6.7508,8.2774,6.9269,28.652,-3.3902e-4,28.652,-1.1832e-5,2.386,1.5522,0.7854,6.3685e-6,0.4665,-2.8412e-7,1.3827,-7.3917e-5,0.060443,0.0012276,1.9829e-5,0.27601,0.23948,1.4611,0.026782,255734,0,0.025399,0.88571,1.0494,0.46972,0.2959,0.12009,8.4923,6.0237,-3.6583e-4,0,0,0,0,1.8013,0.025,4.9087e-4,-0.5931,1.7648e-5,NaN,286.4,98160,339.33,0.023514,0.064894 + 8.4147,267.55,-9.9331,-9.118,10.294,9.1196,29.317,-3.4078e-4,29.317,-1.1624e-5,2.7005,0.16724,0.7854,6.5161e-6,0.06219,-2.8193e-7,2.1512,-4.6279e-4,0.060443,0.0012276,1.9829e-5,0.30965,0.23948,2.8068,0.03233,308779,0,0.031595,0.86611,0.89557,0.45003,0.29594,0.12014,0.81661,-0.69971,2.0436e-4,0,0,0,0,2.9919,0.025,4.9087e-4,-1.0702,3.077e-5,NaN,286.41,98187,339.34,0.024339,0.068431 + 8.661,264.82,-12.246,-9.2236,12.461,9.887,29.953,-3.4225e-4,29.953,-1.1426e-5,2.3051,3.5605,0.7854,6.6576e-6,0.25235,-2.6401e-7,-0.84506,-3.5463e-6,0.060443,0.0012276,1.9829e-5,0.28893,0.23948,1.9779,0.038294,365830,0,0.054118,0.85267,1.093,0.4365,0.29598,0.12019,4.2665,8.7678,-3.4779e-4,0,0,0,0,-0.32906,0.025,4.9087e-4,-1.482,-7.9051e-4,NaN,286.43,98219,339.35,0.0078967,0.071764 + 8.9112,261.56,-10.746,62.576,11.532,64.824,30.402,-3.8353e-4,30.402,-1.2616e-5,0.71511,16.923,0.7854,6.7572e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.033981,324717,0,4.4929,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7797,286.45,98256,339.36,0.0077132,0.076771 + 9.1933,259.63,-5.0245,7.2757,5.5627,7.7743,30.271,-4.1075e-4,30.271,-1.3569e-5,1.0832,2.7394,0.7854,6.7282e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.016391,156657,0,1.0456,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7879,286.46,98276,339.36,0.064314,0.077294 + 9.5467,258.05,-4.1036,1.8321,4.5685,2.0247,29.81,-4.2175e-4,29.81,-1.4148e-5,1.4706,0.86181,0.7854,6.6257e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013462,128668,0,0.70534,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.5952,286.47,98286,339.37,0.24695,0.077379 + 9.9814,256.23,-4.2452,-0.32577,4.1313,1.1501,29.066,-4.247e-4,29.066,-1.4612e-5,1.9502,1.103,0.7854,6.4604e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012173,116364,0,0.57685,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9481,286.47,98299,339.37,0.43474,0.077422 + 10.481,254.14,-4.1084,0.27366,4.2452,0.27514,28.095,-4.2469e-4,28.095,-1.5116e-5,1.9359,0.028536,0.7854,6.2445e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012509,119590,0,0.60921,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9381,286.48,98320,339.38,0.5,0.077466 + 10.981,252.04,-4.289,-0.36124,4.1095,0.42143,27.099,-4.2469e-4,27.099,-1.5672e-5,2.0444,0.21704,0.7854,6.0233e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012108,115785,0,0.57099,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0303,286.5,98344,339.39,0.5,0.077511 + 11.481,249.96,-4.0452,0.48758,4.2893,0.50404,26.061,-4.2469e-4,26.061,-1.6296e-5,2.1083,0.12775,0.7854,5.7926e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012638,120873,0,0.62219,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0976,286.51,98369,339.4,0.5,0.077555 + 11.981,247.86,-4.3643,-0.63821,4.0496,0.76809,24.954,-4.2469e-4,24.954,-1.7019e-5,2.322,0.42737,0.7854,5.5464e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011931,114136,0,0.5547,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2969,286.53,98393,339.4,0.5,0.077599 + 12.336,246.37,-4.0421,0.90968,4.3864,1.4116,24.199,-4.2469e-4,24.199,-1.755e-5,1.9396,1.0794,0.7854,5.3786e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012924,123649,0,0.65093,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8823,286.54,98418,339.41,0.35422,0.077643 + 12.836,244.27,-4.3628,-0.64133,4.0499,0.85909,23.301,-4.2469e-4,23.301,-1.8227e-5,1.6538,0.5716,0.7854,5.1789e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011932,114178,0,0.55499,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6875,286.55,98435,339.42,0.5,0.077687 + 13.336,242.19,-3.9365,0.85254,4.3631,0.86183,22.458,-4.2469e-4,22.458,-1.8911e-5,1.7169,0.12614,0.7854,4.9916e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012854,123025,0,0.64425,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7055,286.56,98460,339.43,0.5,0.077731 + 13.779,240.34,-4.4363,-1.1273,3.9365,1.1276,21.694,-4.2469e-4,21.694,-1.9577e-5,1.73,0.029562,0.7854,4.8218e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011597,111016,0,0.52454,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7303,286.58,98484,339.43,0.4434,0.077775 + 14.12,238.9,-4.0122,1.2456,4.4473,1.4684,21.06,-4.2469e-4,21.06,-2.0166e-5,1.9948,0.77762,0.7854,4.6808e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013102,125439,0,0.66962,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0422,286.59,98506,339.44,0.3405,0.07782 + 14.62,236.8,-4.402,-0.77958,4.0157,0.8666,20.015,-4.2469e-4,20.015,-2.1219e-5,2.184,0.37847,0.7854,4.4486e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01183,113279,0,0.54603,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.163,286.6,98523,339.45,0.5,0.077863 + 15.029,235.08,-3.9644,1.068,4.4084,1.2203,19.17,-4.2469e-4,19.17,-2.2155e-5,1.9422,0.59029,0.7854,4.2607e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012987,124378,0,0.6582,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9451,286.61,98548,339.46,0.40974,0.077908 + 15.527,232.99,-4.4604,-0.99704,3.9648,1.0051,18.219,-4.2469e-4,18.219,-2.331e-5,1.8792,0.12664,0.7854,4.0495e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01168,111875,0,0.53247,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8852,286.62,98568,339.46,0.49749,0.077954 + 15.857,231.59,-4.0099,1.3654,4.4681,1.5154,17.563,-4.2469e-4,17.563,-2.4181e-5,2.096,0.65731,0.7854,3.9037e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013162,126098,0,0.67637,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1416,286.64,98592,339.47,0.32995,0.077999 + 16.357,229.49,-4.4003,-0.78088,4.0146,0.89647,16.57,-4.2469e-4,16.57,-2.563e-5,1.8759,0.44031,0.7854,3.683e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011826,113313,0,0.54613,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9004,286.64,98609,339.48,0.5,0.078044 + 16.828,227.53,-3.9039,1.0522,4.4006,1.0598,15.699,-4.2469e-4,15.699,-2.7052e-5,1.8159,0.1272,0.7854,3.4894e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012963,124228,0,0.65632,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8243,286.66,98634,339.48,0.47178,0.078089 + 17.178,226.09,-4.333,-1.2271,3.9182,1.4298,15.109,-4.2469e-4,15.109,-2.8108e-5,1.5592,0.73391,0.7854,3.3583e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011541,110626,0,0.5204,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.4819,286.67,98656,339.49,0.3497,0.078133 + 17.678,224.01,-3.9703,0.72539,4.333,0.72542,14.33,-4.2469e-4,14.33,-2.9636e-5,1.5558,0.0068407,0.7854,3.1852e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012763,122353,0,0.63652,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.5564,286.68,98673,339.5,0.5,0.078177 + 18.044,222.5,-4.3007,-0.90346,3.9966,1.3673,13.693,-4.2469e-4,13.693,-3.1015e-5,1.9311,1.0263,0.7854,3.0435e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011772,112872,0,0.54163,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0135,286.69,98698,339.51,0.36568,0.078222 + 18.544,220.42,-4.0134,0.57468,4.3016,0.61362,12.754,-4.2469e-4,12.754,-3.3298e-5,1.8236,0.2151,0.7854,2.8348e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01267,121500,0,0.62755,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.842,286.7,98715,339.51,0.5,0.078265 + 18.991,218.55,-4.3387,-0.7268,4.0308,1.1171,11.853,-4.2469e-4,11.853,-3.583e-5,2.2033,0.8483,0.7854,2.6345e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011872,113870,0,0.55114,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1986,286.72,98740,339.52,0.4476,0.078309 + 19.491,216.48,-3.9583,0.76081,4.3388,0.76461,10.761,-4.2469e-4,10.761,-3.9466e-5,2.1652,0.076209,0.7854,2.3918e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012779,122588,0,0.63868,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.172,286.73,98762,339.53,0.5,0.078352 + 19.986,214.4,-4.4575,-1.0091,3.9584,1.0107,9.6829,-4.2469e-4,9.6829,-4.386e-5,2.1934,0.057154,0.7854,2.1522e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011658,111857,0,0.5317,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1909,286.74,98786,339.54,0.49469,0.078397 + 20.345,212.88,-3.9703,1.3559,4.4592,1.3917,8.9151,-4.2469e-4,8.9151,-4.7637e-5,2.0808,0.31348,0.7854,1.9815e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013133,126031,0,0.6749,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0682,286.76,98811,339.54,0.35928,0.078442 + 20.845,210.78,-4.4459,-0.95106,3.971,0.96547,7.8954,-4.2469e-4,7.8954,-5.3789e-5,1.9977,0.16614,0.7854,1.7549e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011695,112246,0,0.53529,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0063,286.77,98828,339.55,0.5,0.078486 + 21.23,209.16,-3.9471,1.2977,4.446,1.301,7.1345,-4.2469e-4,7.1345,-5.9527e-5,1.9621,0.092594,0.7854,1.5858e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013093,125693,0,0.67114,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9607,286.78,98853,339.56,0.38433,0.078531 + 21.703,207.18,-4.4451,-1.0522,3.9474,1.0564,6.2164,-4.2469e-4,6.2164,-6.8318e-5,1.9175,0.094347,0.7854,1.3817e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011625,111610,0,0.52911,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9196,286.79,98872,339.57,0.47331,0.078575 + 22.088,205.56,-3.9451,1.2974,4.4451,1.2974,5.4775,-4.2469e-4,5.4775,-7.7533e-5,1.9169,0.0016054,0.7854,1.2175e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01309,125703,0,0.6711,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9168,286.8,98895,339.57,0.38537,0.07862 + 22.548,203.64,-4.4291,-1.0542,3.9471,1.0892,4.6264,-4.2469e-4,4.6264,-9.1798e-5,1.7913,0.27359,0.7854,1.0283e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011623,111633,0,0.52922,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7935,286.81,98914,339.58,0.45907,0.078664 + 22.946,201.97,-3.9408,1.2245,4.4304,1.2538,3.8906,-4.2469e-4,3.8906,-1.0916e-4,1.8987,0.26941,0.7854,8.6476e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013046,125322,0,0.66689,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8995,286.83,98937,339.59,0.39878,0.078708 + 23.402,200.06,-4.4295,-1.0717,3.9422,1.0965,3.0007,-4.2469e-4,3.0007,-1.4153e-4,2.0045,0.2319,0.7854,6.6697e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011608,111525,0,0.52809,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0033,286.84,98956,339.59,0.45598,0.078753 + 23.771,198.51,-3.9725,1.2405,4.435,1.3573,2.225,-4.2469e-4,2.225,-1.9088e-4,2.2073,0.55063,0.7854,4.9454e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013059,125486,0,0.66849,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2253,286.85,98979,339.6,0.36839,0.078797 + 24.271,196.41,-4.4342,-0.92347,3.9758,0.99254,1.1668,-4.2469e-4,1.1668,-3.6398e-4,2.0254,0.36376,0.7854,2.5934e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011707,112507,0,0.53731,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0446,286.86,98997,339.61,0.5,0.078841 + 24.668,194.75,-3.9358,1.2547,4.4344,1.2587,0.37004,-4.2469e-4,0.37004,-0.0011477,1.9859,0.099372,0.7854,8.2248e-8,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013057,125505,0,0.66855,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9856,286.87,99021,339.62,0.39724,0.078885 + 25.095,192.97,-4.3973,-1.0794,3.941,1.1695,-0.52017,-4.2469e-4,0.52017,-3.1408,2.1783,0.44994,0.7854,-1.1562e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011604,111555,0,0.52814,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1889,286.88,99041,339.62,0.42755,0.078929 + 25.554,191.06,-3.9035,1.0758,4.3978,1.0893,-1.5019,-4.2469e-4,1.5019,-3.1413,2.0997,0.17136,0.7854,-3.3383e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012949,124503,0,0.65779,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1091,286.9,99062,339.63,0.45899,0.078974 + 25.893,189.67,-4.3049,-1.1857,3.9238,1.4767,-2.1624,-4.2469e-4,2.1624,-3.1414,1.8016,0.88029,0.7854,-4.8063e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011553,111100,0,0.52373,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.701,286.91,99084,339.64,0.33858,0.079019 + 26.393,187.6,-3.9887,0.63247,4.3078,0.73658,-3.1104,-4.2469e-4,3.1104,-3.1415,1.9904,0.37753,0.7854,-6.9134e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012683,121985,0,0.63133,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9573,286.92,99100,339.64,0.5,0.079062 + 26.893,185.5,-4.4099,-0.84241,3.9916,0.90815,-4.148,-4.2469e-4,4.148,-3.1415,2.16,0.33923,0.7854,-9.2196e-7,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011752,113049,0,0.54215,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1413,286.93,99125,339.65,0.5,0.079108 + 27.323,183.71,-3.9167,1.1466,4.4106,1.1624,-5.0948,-4.2469e-4,5.0948,-3.1415,2.2422,0.19103,0.7854,-1.1324e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012985,124937,0,0.66209,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2369,286.94,99149,339.66,0.43015,0.079153 + 27.752,181.93,-4.4162,-1.1659,3.9168,1.167,-6.0508,-4.2469e-4,6.0508,-3.1415,2.2204,0.050713,0.7854,-1.3449e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011531,110965,0,0.52222,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2192,286.96,99170,339.67,0.42845,0.079197 + 28.152,180.25,-3.9407,1.1868,4.419,1.2478,-6.9096,-4.2469e-4,6.9096,-3.1415,2.0661,0.38517,0.7854,-1.5358e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013009,125209,0,0.66483,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0657,286.97,99191,339.67,0.40071,0.079242 + 28.619,178.3,-4.4334,-1.0553,3.9415,1.0708,-7.8941,-4.2469e-4,7.8941,-3.1415,2.1509,0.18153,0.7854,-1.7546e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011604,111695,0,0.52901,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1478,286.98,99211,339.68,0.46694,0.079286 + 28.969,176.82,-3.9829,1.2868,4.4403,1.4281,-8.6092,-4.2469e-4,8.6092,-3.1415,1.934,0.61934,0.7854,-1.9135e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013072,125850,0,0.67151,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9033,286.99,99234,339.69,0.35011,0.079331 + 29.469,174.72,-4.4099,-0.85398,3.9881,0.97,-9.5187,-4.2469e-4,9.5187,-3.1415,1.704,0.46003,0.7854,-2.1157e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01174,113047,0,0.54178,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7293,287,99251,339.69,0.5,0.079375 + 29.867,173.06,-3.9463,1.1661,4.4139,1.2578,-10.233,-4.2469e-4,10.233,-3.1416,1.8914,0.4714,0.7854,-2.2745e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012994,125137,0,0.66378,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8935,287.01,99276,339.7,0.39753,0.07942 + 30.323,171.16,-4.4093,-1.0163,3.9507,1.0975,-11.138,-4.2469e-4,11.138,-3.1416,2.0801,0.41423,0.7854,-2.4756e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01163,112018,0,0.53185,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0774,287.03,99295,339.71,0.4556,0.079464 + 30.737,169.43,-3.9279,1.1618,4.4113,1.2065,-11.972,-4.2469e-4,11.972,-3.1416,1.9453,0.32531,0.7854,-2.661e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012985,125096,0,0.6632,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9493,287.04,99318,339.71,0.41442,0.079508 + 31.18,167.58,-4.4161,-1.1016,3.9294,1.1281,-12.858,-4.2469e-4,12.858,-3.1416,2.053,0.24295,0.7854,-2.858e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011567,111445,0,0.52631,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0549,287.05,99338,339.72,0.44323,0.079553 + 31.598,165.84,-3.917,1.1948,4.4162,1.1969,-13.71,-4.2469e-4,13.71,-3.1416,2.0231,0.071571,0.7854,-3.0472e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012999,125271,0,0.66492,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0243,287.06,99360,339.73,0.41775,0.079598 + 32.012,164.12,-4.39,-1.1412,3.921,1.2064,-14.582,-4.2469e-4,14.582,-3.1416,2.1853,0.39139,0.7854,-3.241e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011541,111239,0,0.52425,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2,287.07,99380,339.74,0.41445,0.079642 + 32.398,162.51,-3.9697,1.0908,4.3991,1.2979,-15.371,-4.2469e-4,15.371,-3.1416,1.9144,0.70324,0.7854,-3.4165e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012948,124819,0,0.65999,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.902,287.08,99400,339.74,0.38525,0.079686 + 32.898,160.41,-4.4261,-0.91263,3.9697,0.9127,-16.327,-4.2469e-4,16.327,-3.1416,1.9085,0.011723,0.7854,-3.629e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011684,112650,0,0.53753,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9091,287.09,99419,339.75,0.5,0.07973 + 33.297,158.74,-3.9261,1.2516,4.4261,1.2518,-17.088,-4.2469e-4,17.088,-3.1416,1.8998,0.021729,0.7854,-3.798e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013027,125620,0,0.66834,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8998,287.11,99444,339.76,0.39944,0.079774 + 33.746,156.87,-4.4213,-1.1025,3.9267,1.1132,-17.925,-4.2469e-4,17.925,-3.1416,1.8306,0.15411,0.7854,-3.9842e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011557,111462,0,0.52613,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8303,287.12,99463,339.76,0.44916,0.079818 + 34.135,155.25,-3.9407,1.2373,4.4236,1.2871,-18.61,-4.2469e-4,18.61,-3.1416,1.6929,0.35457,0.7854,-4.1364e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013019,125584,0,0.66782,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6887,287.13,99485,339.77,0.38847,0.079862 + 34.486,153.8,-4.2842,-0.97661,3.9675,1.4213,-19.269,-4.2469e-4,19.269,-3.1416,2.0562,1.0327,0.7854,-4.2829e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011677,112651,0,0.5373,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1538,287.14,99504,339.78,0.35178,0.079906 + 34.986,151.73,-3.9979,0.57271,4.2892,0.75969,-20.235,-4.2469e-4,20.235,-3.1416,1.8066,0.49912,0.7854,-4.4976e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012623,121797,0,0.62803,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8501,287.15,99521,339.78,0.5,0.079949 + 35.486,149.63,-4.3853,-0.77482,3.9988,0.7994,-21.163,-4.2469e-4,21.163,-3.1416,1.9049,0.19668,0.7854,-4.7038e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011768,113570,0,0.54599,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8937,287.16,99545,339.79,0.5,0.079993 + 35.923,147.82,-3.9194,1.0678,4.3885,1.146,-21.954,-4.2469e-4,21.954,-3.1416,1.7234,0.41598,0.7854,-4.8797e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012915,124657,0,0.65771,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7372,287.18,99570,339.8,0.43632,0.080038 + 36.326,146.15,-4.3659,-1.1069,3.9274,1.2394,-22.695,-4.2469e-4,22.695,-3.1416,1.9483,0.55753,0.7854,-5.0443e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011558,111576,0,0.52686,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9746,287.19,99591,339.81,0.40341,0.080083 + 36.826,144.09,-3.881,0.96984,4.3668,0.9924,-23.696,-4.2469e-4,23.696,-3.1416,2.0536,0.21043,0.7854,-5.2667e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01285,124073,0,0.65144,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0336,287.2,99611,339.81,0.5,0.080126 + 37.213,142.49,-4.3809,-1.2908,3.881,1.291,-24.492,-4.2469e-4,24.492,-3.1416,2.062,0.021865,0.7854,-5.4439e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011421,110290,0,0.51467,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0635,287.21,99635,339.82,0.38729,0.080195 + 37.628,140.76,-3.9413,1.0609,4.3871,1.2067,-25.298,-4.2469e-4,25.298,-3.1416,1.8238,0.57484,0.7854,-5.6228e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01291,124685,0,0.65774,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8303,287.22,99654,339.83,0.41437,0.080239 + 38.117,138.71,-4.4412,-1.0214,3.9413,1.0215,-26.192,-4.2469e-4,26.192,-3.1416,1.8327,0.018058,0.7854,-5.8217e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011598,112032,0,0.53096,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8319,287.24,99674,339.83,0.48947,0.080283 + 38.378,137.6,-4.0702,1.4234,4.4703,1.9181,-26.714,-4.2469e-4,26.714,-3.1416,2.1678,1.2856,0.7854,-5.9376e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013154,127087,0,0.68316,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3411,287.25,99698,339.84,0.26068,0.080328 + 38.878,135.51,-4.2856,-0.43081,4.0719,0.50973,-27.832,-4.2469e-4,27.832,-3.1416,2.304,0.27245,0.7854,-6.1861e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011982,115772,0,0.56689,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2861,287.26,99711,339.85,0.5,0.080372 + 39.378,133.45,-3.9837,0.60377,4.2932,0.8648,-28.906,-4.2469e-4,28.906,-3.1416,1.9945,0.61914,0.7854,-6.4249e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012632,122082,0,0.6303,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0491,287.27,99736,339.86,0.5,0.080417 + 39.878,131.35,-4.3954,-0.82335,3.9845,0.84363,-29.927,-4.2469e-4,29.927,-3.1416,2.0864,0.1839,0.7854,-6.6517e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011724,113324,0,0.54304,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.076,287.28,99760,339.86,0.5,0.080462 + 40.237,129.85,-3.9803,1.1563,4.4064,1.3929,-30.726,-4.2469e-4,30.726,-3.1416,2.3652,0.77651,0.7854,-6.8293e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012965,125342,0,0.66425,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3978,287.3,99784,339.87,0.35897,0.080508 + 40.737,127.76,-4.3912,-0.82182,3.9873,0.97945,-31.842,-4.2469e-4,31.842,-3.1416,2.0987,0.53285,0.7854,-7.0773e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011732,113433,0,0.54398,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1291,287.31,99802,339.88,0.5,0.080551 + 41.179,125.92,-3.8988,1.1136,4.3919,1.1309,-32.75,-4.2469e-4,32.75,-3.1416,2.0116,0.19703,0.7854,-7.2793e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012922,124965,0,0.66011,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0195,287.32,99827,339.89,0.44212,0.080596 + 41.594,124.2,-4.395,-1.1963,3.8994,1.2056,-33.572,-4.2469e-4,33.572,-3.1416,1.9498,0.14916,0.7854,-7.4619e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011473,110968,0,0.52046,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9441,287.33,99848,339.89,0.41473,0.080641 + 42.033,122.38,-3.8971,1.135,4.3952,1.1397,-34.417,-4.2469e-4,34.417,-3.1416,1.9041,0.10404,0.7854,-7.6498e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012931,125093,0,0.66133,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.908,287.34,99868,339.9,0.43871,0.080686 + 42.427,120.75,-4.3672,-1.1917,3.902,1.2675,-35.202,-4.2469e-4,35.202,-3.1416,2.0745,0.43189,0.7854,-7.8242e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01148,111071,0,0.52132,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0995,287.35,99890,339.91,0.39446,0.080731 + 42.853,118.98,-3.9342,1.0157,4.3736,1.1729,-36.139,-4.2469e-4,36.139,-3.1416,2.3245,0.58661,0.7854,-8.0326e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012867,124510,0,0.65505,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3112,287.37,99909,339.91,0.42628,0.080775 + 43.322,117.03,-4.4179,-1.031,3.936,1.0656,-37.2,-4.2469e-4,37.2,-3.1416,2.1981,0.26943,0.7854,-8.2684e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011579,112069,0,0.53063,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2037,287.38,99930,339.92,0.46922,0.080819 + 43.687,115.5,-3.9552,1.2686,4.4228,1.3706,-37.968,-4.2469e-4,37.968,-3.1416,2.0089,0.51883,0.7854,-8.439e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013011,125947,0,0.67011,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9911,287.39,99952,339.93,0.3648,0.080863 + 44.187,113.4,-4.4243,-0.9382,3.9553,0.94074,-38.981,-4.2469e-4,38.981,-3.1416,2.0434,0.069012,0.7854,-8.6641e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011635,112647,0,0.53601,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0396,287.4,99970,339.93,0.5,0.080907 + 44.567,111.82,-3.9327,1.2945,4.4253,1.3167,-39.774,-4.2469e-4,39.774,-3.1416,2.1347,0.24063,0.7854,-8.8405e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013018,126054,0,0.6711,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1393,287.41,99995,339.94,0.37974,0.080952 + 45.047,109.81,-4.4296,-1.0352,3.933,1.0416,-40.785,-4.2469e-4,40.785,-3.1416,2.079,0.11605,0.7854,-9.0652e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011569,112045,0,0.53018,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0827,287.42,100014,339.95,0.48001,0.080996 + 45.425,108.23,-3.93,1.3225,4.4296,1.3237,-41.575,-4.2469e-4,41.575,-3.1416,2.1009,0.057925,0.7854,-9.2407e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01303,126212,0,0.67264,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1021,287.44,100037,339.96,0.37772,0.081039 + 45.899,106.25,-4.4245,-1.0433,3.9307,1.055,-42.553,-4.2469e-4,42.553,-3.1416,2.0266,0.15671,0.7854,-9.4581e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011562,112009,0,0.52972,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0306,287.45,100056,339.96,0.47393,0.081084 + 46.283,104.65,-3.9246,1.3006,4.4245,1.301,-43.33,-4.2469e-4,43.33,-3.1416,2.0143,0.031999,0.7854,-9.6307e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013014,126100,0,0.67132,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0139,287.46,100079,339.97,0.38431,0.081129 + 46.751,102.69,-4.423,-1.0654,3.9248,1.0688,-44.281,-4.2469e-4,44.281,-3.1416,2.0546,0.086102,0.7854,-9.8422e-6,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011544,111873,0,0.52833,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.053,287.47,100098,339.98,0.46779,0.081173 + 47.136,101.09,-3.923,1.2969,4.423,1.2969,-45.073,-4.2469e-4,45.073,-3.1416,2.0515,0.0079794,0.7854,-1.0018e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.013009,126092,0,0.67109,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0514,287.48,100121,339.98,0.38552,0.081218 + 47.591,99.191,-4.4077,-1.0659,3.9249,1.0997,-45.978,-4.2469e-4,45.978,-3.1416,1.9285,0.27062,0.7854,-1.0219e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011544,111906,0,0.52853,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9301,287.49,100139,339.99,0.45467,0.081262 + 47.998,97.497,-3.909,1.2239,4.4078,1.2272,-46.756,-4.2469e-4,46.756,-3.1416,1.8921,0.089377,0.7854,-1.0392e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012964,125692,0,0.6667,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8928,287.51,100162,340,0.40744,0.081306 + 48.432,95.699,-4.3966,-1.1258,3.9107,1.1544,-47.599,-4.2469e-4,47.599,-3.1416,2.0027,0.25528,0.7854,-1.058e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011502,111531,0,0.52488,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0071,287.52,100182,340,0.43312,0.081351 + 48.84,94.001,-3.9157,1.177,4.3987,1.2239,-48.445,-4.2469e-4,48.445,-3.1416,2.1397,0.33533,0.7854,-1.0768e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012937,125465,0,0.66415,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1369,287.53,100203,340.01,0.40854,0.081395 + 49.289,92.132,-4.4071,-1.0943,3.9168,1.1135,-49.385,-4.2469e-4,49.385,-3.1416,2.0472,0.20599,0.7854,-1.0977e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011519,111736,0,0.52671,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0471,287.54,100223,340.02,0.44904,0.08144 + 49.696,90.44,-3.9072,1.2282,4.4071,1.2284,-50.221,-4.2469e-4,50.221,-3.1416,2.0575,0.025471,0.7854,-1.1162e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012961,125741,0,0.66694,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0573,287.55,100244,340.03,0.40702,0.081484 + 50.132,88.628,-4.4,-1.1295,3.9082,1.1461,-51.137,-4.2469e-4,51.137,-3.1416,2.1422,0.19405,0.7854,-1.1366e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011494,111520,0,0.52456,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1449,287.56,100264,340.03,0.43628,0.081529 + 50.549,86.897,-3.9012,1.1965,4.4001,1.1993,-52.023,-4.2469e-4,52.023,-3.1416,2.1081,0.081849,0.7854,-1.1563e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01294,125574,0,0.66504,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1094,287.57,100286,340.04,0.41692,0.081573 + 50.95,85.242,-4.3585,-1.141,3.9078,1.2475,-52.827,-4.2469e-4,52.827,-3.1416,1.9059,0.50446,0.7854,-1.1742e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011492,111538,0,0.52462,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8809,287.59,100306,340.05,0.40079,0.081617 + 51.231,84.063,-4.0546,1.0838,4.3952,1.7831,-53.417,-4.2469e-4,53.417,-3.1416,2.3029,1.4159,0.7854,-1.1873e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012925,125465,0,0.66375,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.4726,287.6,100325,340.05,0.28042,0.081706 + 51.731,81.978,-4.2838,-0.45856,4.055,0.4783,-54.586,-4.2469e-4,54.586,-3.1416,2.3709,0.13598,0.7854,-1.2133e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011924,115765,0,0.56505,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.3619,287.6,100339,340.06,0.5,0.081752 + 52.231,79.915,-3.9683,0.63113,4.2845,0.65846,-55.795,-4.2469e-4,55.795,-3.1416,2.4648,0.18773,0.7854,-1.2401e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012599,122338,0,0.63095,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.448,287.62,100364,340.07,0.5,0.081796 + 52.713,77.903,-4.3679,-0.82811,3.9778,1.0361,-56.912,-4.2469e-4,56.912,-3.1416,2.1643,0.62261,0.7854,-1.265e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011697,113597,0,0.54395,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1896,287.63,100388,340.07,0.4826,0.081841 + 53.184,75.963,-3.8741,1.0485,4.3684,1.0617,-57.913,-4.2469e-4,57.913,-3.1416,2.0858,0.16673,0.7854,-1.2872e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012845,124772,0,0.65616,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0972,287.64,100411,340.08,0.47094,0.081886 + 53.561,74.413,-4.3476,-1.2558,3.8789,1.3261,-58.669,-4.2469e-4,58.669,-3.1416,1.9252,0.42598,0.7854,-1.304e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011406,110807,0,0.51744,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8928,287.66,100434,340.09,0.37705,0.08193 + 54.042,72.432,-3.8869,0.95771,4.3507,1.0393,-59.642,-4.2469e-4,59.642,-3.1416,2.1193,0.40353,0.7854,-1.3256e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012793,124298,0,0.65105,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0882,287.67,100452,340.1,0.48111,0.081974 + 54.407,70.936,-4.317,-1.1792,3.8996,1.3707,-60.368,-4.2469e-4,60.368,-3.1416,1.8644,0.69891,0.7854,-1.3418e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011466,111427,0,0.52314,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8044,287.68,100475,340.1,0.36477,0.082018 + 54.843,69.131,-3.954,0.83158,4.3288,1.1456,-61.107,-4.2469e-4,61.107,-3.1416,1.5205,0.78799,0.7854,-1.3582e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012728,123706,0,0.64473,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.5446,287.69,100493,340.11,0.43644,0.082149 + 55.295,67.253,-4.3517,-0.87967,3.9652,1.1061,-61.863,-4.2469e-4,61.863,-3.1416,1.8236,0.67057,0.7854,-1.375e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011658,113330,0,0.54106,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8175,287.7,100514,340.12,0.45203,0.082199 + 55.795,65.2,-3.8629,0.97754,4.3517,0.97814,-62.779,-4.2469e-4,62.779,-3.1416,1.8407,0.034222,0.7854,-1.3954e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012794,124395,0,0.6518,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8374,287.71,100536,340.12,0.5,0.082244 + 56.173,63.65,-4.3547,-1.3038,3.8644,1.3256,-63.49,-4.2469e-4,63.49,-3.1416,1.9309,0.2393,0.7854,-1.4112e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011362,110484,0,0.5141,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9494,287.73,100560,340.13,0.37719,0.08229 + 56.632,61.754,-3.893,1.0041,4.3579,1.0875,-64.334,-4.2469e-4,64.334,-3.1416,1.739,0.41755,0.7854,-1.4299e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012812,124608,0,0.65389,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7627,287.74,100578,340.14,0.45977,0.082334 + 57.022,60.152,-4.3416,-1.1528,3.9014,1.285,-65.053,-4.2469e-4,65.053,-3.1416,1.9599,0.56768,0.7854,-1.4459e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01147,111569,0,0.52415,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9944,287.75,100601,340.15,0.3891,0.082379 + 57.422,58.489,-3.9554,0.96369,4.3533,1.2476,-65.902,-4.2469e-4,65.902,-3.1416,2.2774,0.79236,0.7854,-1.4648e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012798,124509,0,0.65272,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2793,287.76,100620,340.15,0.40077,0.082423 + 57.84,56.762,-4.3109,-0.85065,3.9728,1.1962,-66.781,-4.2469e-4,66.781,-3.1416,1.9259,0.84097,0.7854,-1.4843e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011679,113639,0,0.54367,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.906,287.77,100639,340.16,0.418,0.082468 + 58.34,54.706,-3.9128,0.79622,4.3147,0.91165,-67.799,-4.2469e-4,67.799,-3.1416,2.1479,0.444,0.7854,-1.507e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012684,123436,0,0.6414,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1064,287.78,100659,340.17,0.5,0.082512 + 58.724,53.126,-4.3133,-1.0431,3.9283,1.3022,-68.567,-4.2469e-4,68.567,-3.1416,1.8485,0.77958,0.7854,-1.524e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011548,112399,0,0.53175,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7998,287.79,100683,340.17,0.38397,0.082557 + 59.224,51.07,-3.911,0.80459,4.3143,0.83423,-69.518,-4.2469e-4,69.518,-3.1416,1.9587,0.2204,0.7854,-1.5452e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012682,123457,0,0.64148,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9381,287.8,100702,340.18,0.5,0.082602 + 59.628,49.405,-4.3387,-1.0591,3.9215,1.2384,-70.257,-4.2469e-4,70.257,-3.1416,1.6996,0.64174,0.7854,-1.5616e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011528,112237,0,0.53011,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6718,287.82,100726,340.19,0.40375,0.082647 + 60.128,47.352,-3.8732,0.93102,4.3387,0.93124,-71.104,-4.2469e-4,71.104,-3.1416,1.6897,0.019891,0.7854,-1.5804e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012754,124191,0,0.64899,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6916,287.83,100746,340.19,0.5,0.082692 + 60.458,46.009,-4.2683,-1.1981,3.8956,1.5159,-71.712,-4.2469e-4,71.712,-3.1416,1.996,0.9286,0.7854,-1.5939e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011451,111527,0,0.52331,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1075,287.84,100770,340.2,0.32985,0.082737 + 60.958,43.949,-3.9732,0.59027,4.2688,0.6102,-72.729,-4.2469e-4,72.729,-3.1416,2.0733,0.1547,0.7854,-1.6165e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012548,122223,0,0.62846,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0595,287.85,100785,340.21,0.5,0.082781 + 61.458,41.864,-4.3677,-0.789,3.9767,0.87573,-73.719,-4.2469e-4,73.719,-3.1416,1.8833,0.37999,0.7854,-1.6385e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011689,113878,0,0.54551,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9059,287.86,100810,340.22,0.5,0.082826 + 61.848,40.243,-3.9374,1.1024,4.3755,1.2811,-74.503,-4.2469e-4,74.503,-3.1416,2.138,0.65258,0.7854,-1.656e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012861,125318,0,0.66052,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1446,287.88,100834,340.22,0.39029,0.082871 + 62.348,38.155,-4.4146,-0.95438,3.9379,0.96387,-75.589,-4.2469e-4,75.589,-3.1416,2.2055,0.1349,0.7854,-1.6801e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011574,112798,0,0.53509,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1981,287.89,100853,340.23,0.5,0.082915 + 62.725,36.584,-3.9158,1.3224,4.4148,1.3255,-76.415,-4.2469e-4,76.415,-3.1416,2.1712,0.090927,0.7854,-1.6984e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012976,126478,0,0.67267,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1694,287.9,100878,340.24,0.37721,0.08296 + 63.202,34.6,-4.4155,-1.0493,3.9159,1.05,-77.444,-4.2469e-4,77.444,-3.1416,2.1527,0.038897,0.7854,-1.7213e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011509,112197,0,0.52929,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1538,287.91,100896,340.24,0.47617,0.083004 + 63.575,33.042,-3.9179,1.3307,4.4158,1.3373,-78.258,-4.2469e-4,78.258,-3.1416,2.2021,0.13206,0.7854,-1.7394e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012978,126540,0,0.67319,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.205,287.93,100919,340.25,0.3739,0.08305 + 64.006,31.26,-4.357,-1.0196,3.9257,1.161,-79.155,-4.2469e-4,79.155,-3.1416,1.9629,0.55525,0.7854,-1.7594e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011537,112510,0,0.53214,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9545,287.94,100937,340.26,0.43066,0.083094 + 64.485,29.293,-3.8586,1.0408,4.3572,1.0442,-80.105,-4.2469e-4,80.105,-3.1416,2.0031,0.083925,0.7854,-1.7805e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012805,124892,0,0.65564,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9966,287.95,100958,340.27,0.47885,0.083139 + 64.87,27.711,-4.3582,-1.2973,3.8587,1.2984,-80.872,-4.2469e-4,80.872,-3.1416,1.9823,0.054028,0.7854,-1.7975e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.01134,110621,0,0.51431,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9786,287.96,100981,340.27,0.38509,0.083183 + 65.251,26.129,-3.9485,1.0759,4.3686,1.3128,-81.682,-4.2469e-4,81.682,-3.1416,2.2689,0.75237,0.7854,-1.8155e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012838,125254,0,0.65931,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2836,287.97,101000,340.28,0.38085,0.083228 + 65.751,24.043,-4.3954,-0.89397,3.9485,0.89523,-82.822,-4.2469e-4,82.822,-3.1416,2.2926,0.047578,0.7854,-1.8409e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011604,113223,0,0.53869,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2899,287.98,101019,340.29,0.5,0.083272 + 66.093,22.613,-3.9627,1.2641,4.405,1.4608,-83.564,-4.2469e-4,83.564,-3.1416,2.0421,0.73194,0.7854,-1.8573e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012945,126333,0,0.67059,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.002,287.99,101043,340.29,0.34229,0.083316 + 66.516,20.866,-4.2943,-0.78354,3.9817,1.1815,-84.349,-4.2469e-4,84.349,-3.1416,1.6679,0.88437,0.7854,-1.8748e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011701,114205,0,0.54797,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.6537,288,101060,340.3,0.42318,0.083361 + 67.016,18.812,-3.9222,0.74431,4.2958,0.79378,-85.217,-4.2469e-4,85.217,-3.1416,1.8058,0.27584,0.7854,-1.8941e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012623,123230,0,0.63792,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.7801,288.01,101080,340.31,0.5,0.083413 + 67.477,16.899,-4.3809,-0.99587,3.9269,1.0854,-86.095,-4.2469e-4,86.095,-3.1416,2.0047,0.43174,0.7854,-1.9136e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011539,112664,0,0.53316,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.998,288.03,101104,340.31,0.46065,0.083458 + 67.897,15.162,-3.8867,1.1758,4.3815,1.1895,-86.922,-4.2469e-4,86.922,-3.1416,1.929,0.17999,0.7854,-1.932e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012875,125726,0,0.66388,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9329,288.04,101127,340.32,0.42033,0.083504 + 68.324,13.397,-4.3822,-1.1606,3.8873,1.1711,-87.731,-4.2469e-4,87.731,-3.1416,1.8622,0.15647,0.7854,-1.95e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011422,111560,0,0.52265,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8587,288.05,101147,340.33,0.42693,0.083549 + 68.713,11.783,-3.9177,1.1946,4.3863,1.2859,-88.491,-4.2469e-4,88.491,-3.1416,2.0472,0.47577,0.7854,-1.9669e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012888,125896,0,0.66555,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0517,288.06,101168,340.34,0.38884,0.083595 + 69.201,9.7497,-4.4156,-1.0204,3.9179,1.0246,-89.501,-4.2469e-4,89.501,-3.1416,2.0925,0.092737,0.7854,-1.9893e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011512,112466,0,0.53107,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0886,288.07,101187,340.34,0.48801,0.08364 + 69.527,8.3834,-3.9668,1.3768,4.4237,1.5338,-90.147,-4.2469e-4,90.147,-3.1416,1.8721,0.67589,0.7854,-2.0037e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012997,127005,0,0.67717,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.8256,288.09,101211,340.35,0.32599,0.083685 + 70.027,6.3013,-4.3618,-0.79012,3.9695,0.85848,-91.125,-4.2469e-4,91.125,-3.1416,2.04,0.33572,0.7854,-2.0254e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011663,113978,0,0.54534,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.0198,288.1,101227,340.36,0.5,0.083729 + 70.476,4.4547,-3.872,1.092,4.3628,1.1147,-92.063,-4.2469e-4,92.063,-3.1416,2.1404,0.22395,0.7854,-2.0462e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012818,125288,0,0.65886,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.1296,288.11,101251,340.36,0.44853,0.083774 + 70.877,2.8039,-4.3589,-1.2138,3.8741,1.2465,-92.944,-4.2469e-4,92.944,-3.1416,2.2542,0.28361,0.7854,-2.0658e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011382,111272,0,0.51963,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.2682,288.12,101273,340.37,0.40112,0.083819 + 71.34,0.90212,-3.8596,1.0788,4.359,1.0804,-93.981,-4.2469e-4,93.981,-3.1416,2.2274,0.057793,0.7854,-2.0889e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.012806,125213,0,0.65794,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,2.231,288.13,101292,340.38,0.4628,0.083864 + 71.694,-0.54504,-4.3012,-1.2452,3.8711,1.4098,-94.73,-4.2469e-4,94.73,-3.1416,1.993,0.66114,0.7854,-2.1055e-5,NaN,NaN,NaN,NaN,0.060443,NaN,NaN,NaN,NaN,NaN,0.011373,111215,0,0.519,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,1.9294,288.14,101314,340.39,0.35466,0.083909 + + + + + diff --git a/core/test/net/sf/openrocket/IntegrationTest.java b/core/test/net/sf/openrocket/IntegrationTest.java index 2a1d52995..599257017 100644 --- a/core/test/net/sf/openrocket/IntegrationTest.java +++ b/core/test/net/sf/openrocket/IntegrationTest.java @@ -1,8 +1,13 @@ package net.sf.openrocket; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.awt.event.ActionEvent; +import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -15,7 +20,6 @@ import net.sf.openrocket.database.motor.MotorDatabase; import net.sf.openrocket.database.motor.ThrustCurveMotorSetDatabase; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; -import net.sf.openrocket.file.DatabaseMotorFinder; import net.sf.openrocket.file.GeneralRocketLoader; import net.sf.openrocket.file.RocketLoadException; import net.sf.openrocket.file.motor.GeneralMotorLoader; @@ -111,10 +115,10 @@ public class IntegrationTest extends BaseTestCase { System.setProperty("openrocket.unittest", "true"); // Load the rocket - GeneralRocketLoader loader = new GeneralRocketLoader(); + GeneralRocketLoader loader = new GeneralRocketLoader(new File("simplerocket.ork")); InputStream is = this.getClass().getResourceAsStream("simplerocket.ork"); assertNotNull("Problem in unit test, cannot find simplerocket.ork", is); - document = loader.load(is, new DatabaseMotorFinder()); + document = loader.load(is); is.close(); undoAction = UndoRedoAction.newUndoAction(document); diff --git a/core/test/net/sf/openrocket/database/ThrustCurveMotorSetTest.java b/core/test/net/sf/openrocket/database/ThrustCurveMotorSetTest.java index 40bf5e942..a544e44b2 100644 --- a/core/test/net/sf/openrocket/database/ThrustCurveMotorSetTest.java +++ b/core/test/net/sf/openrocket/database/ThrustCurveMotorSetTest.java @@ -1,6 +1,10 @@ package net.sf.openrocket.database; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.Arrays; import java.util.Collections; @@ -14,31 +18,31 @@ import net.sf.openrocket.util.Coordinate; import org.junit.Test; public class ThrustCurveMotorSetTest { - - + + private static final ThrustCurveMotor motor1 = new ThrustCurveMotor( Manufacturer.getManufacturer("A"), - "F12X", "Desc", Motor.Type.UNKNOWN, new double[] { }, - 0.024, 0.07, new double[] { 0, 1, 2 }, new double[] {0, 1, 0}, - new Coordinate[] {Coordinate.NUL, Coordinate.NUL, Coordinate.NUL}, "digestA"); + "F12X", "Desc", Motor.Type.UNKNOWN, new double[] {}, + 0.024, 0.07, new double[] { 0, 1, 2 }, new double[] { 0, 1, 0 }, + new Coordinate[] { Coordinate.NUL, Coordinate.NUL, Coordinate.NUL }, "digestA"); private static final ThrustCurveMotor motor2 = new ThrustCurveMotor( Manufacturer.getManufacturer("A"), "F12H", "Desc", Motor.Type.SINGLE, new double[] { 5 }, - 0.024, 0.07, new double[] { 0, 1, 2 }, new double[] {0, 1, 0}, - new Coordinate[] {Coordinate.NUL, Coordinate.NUL, Coordinate.NUL}, "digestB"); + 0.024, 0.07, new double[] { 0, 1, 2 }, new double[] { 0, 1, 0 }, + new Coordinate[] { Coordinate.NUL, Coordinate.NUL, Coordinate.NUL }, "digestB"); private static final ThrustCurveMotor motor3 = new ThrustCurveMotor( Manufacturer.getManufacturer("A"), "F12", "Desc", Motor.Type.UNKNOWN, new double[] { 0, Motor.PLUGGED }, - 0.024, 0.07, new double[] { 0, 1, 2 }, new double[] {0, 2, 0}, - new Coordinate[] {Coordinate.NUL, Coordinate.NUL, Coordinate.NUL}, "digestC"); - + 0.024, 0.07, new double[] { 0, 1, 2 }, new double[] { 0, 2, 0 }, + new Coordinate[] { Coordinate.NUL, Coordinate.NUL, Coordinate.NUL }, "digestC"); + private static final ThrustCurveMotor motor4 = new ThrustCurveMotor( Manufacturer.getManufacturer("A"), "F12", "Desc", Motor.Type.HYBRID, new double[] { 0 }, - 0.024, 0.07, new double[] { 0, 1, 2 }, new double[] {0, 2, 0}, - new Coordinate[] {Coordinate.NUL, Coordinate.NUL, Coordinate.NUL}, "digestD"); + 0.024, 0.07, new double[] { 0, 1, 2 }, new double[] { 0, 2, 0 }, + new Coordinate[] { Coordinate.NUL, Coordinate.NUL, Coordinate.NUL }, "digestD"); @Test @@ -50,7 +54,7 @@ public class ThrustCurveMotorSetTest { assertEquals("J115", ThrustCurveMotorSet.simplifyDesignation("384-J115")); assertEquals("A2", ThrustCurveMotorSet.simplifyDesignation("A2T")); assertEquals("1/2A2T", ThrustCurveMotorSet.simplifyDesignation("1/2A2T")); - assertEquals("Micro Maxx II", ThrustCurveMotorSet.simplifyDesignation("Micro Maxx II")); + assertEquals("MicroMaxxII", ThrustCurveMotorSet.simplifyDesignation("Micro Maxx II")); } @Test diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/BodyTubeHandlerTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/BodyTubeHandlerTest.java index 8b8bc3b62..0c7b19469 100644 --- a/core/test/net/sf/openrocket/file/rocksim/importt/BodyTubeHandlerTest.java +++ b/core/test/net/sf/openrocket/file/rocksim/importt/BodyTubeHandlerTest.java @@ -30,7 +30,7 @@ public class BodyTubeHandlerTest extends RocksimTestBase { public void testConstructor() throws Exception { try { - new BodyTubeHandler(null, new WarningSet()); + new BodyTubeHandler(null, null, new WarningSet()); Assert.fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { @@ -38,7 +38,7 @@ public class BodyTubeHandlerTest extends RocksimTestBase { } Stage stage = new Stage(); - BodyTubeHandler handler = new BodyTubeHandler(stage, new WarningSet()); + BodyTubeHandler handler = new BodyTubeHandler(null, stage, new WarningSet()); BodyTube component = (BodyTube) getField(handler, "bodyTube"); assertContains(component, stage.getChildren()); } @@ -50,8 +50,8 @@ public class BodyTubeHandlerTest extends RocksimTestBase { */ @Test public void testOpenElement() throws Exception { - Assert.assertEquals(PlainTextHandler.INSTANCE, new BodyTubeHandler(new Stage(), new WarningSet()).openElement(null, null, null)); - Assert.assertNotNull(new BodyTubeHandler(new Stage(), new WarningSet()).openElement("AttachedParts", null, null)); + Assert.assertEquals(PlainTextHandler.INSTANCE, new BodyTubeHandler(null, new Stage(), new WarningSet()).openElement(null, null, null)); + Assert.assertNotNull(new BodyTubeHandler(null, new Stage(), new WarningSet()).openElement("AttachedParts", null, null)); } /** @@ -63,7 +63,7 @@ public class BodyTubeHandlerTest extends RocksimTestBase { @Test public void testCloseElement() throws Exception { Stage stage = new Stage(); - BodyTubeHandler handler = new BodyTubeHandler(stage, new WarningSet()); + BodyTubeHandler handler = new BodyTubeHandler(null, stage, new WarningSet()); BodyTube component = (BodyTube) getField(handler, "bodyTube"); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); @@ -134,7 +134,7 @@ public class BodyTubeHandlerTest extends RocksimTestBase { */ @Test public void testGetComponent() throws Exception { - Assert.assertTrue(new BodyTubeHandler(new Stage(), new WarningSet()).getComponent() instanceof BodyTube); + Assert.assertTrue(new BodyTubeHandler(null, new Stage(), new WarningSet()).getComponent() instanceof BodyTube); } /** @@ -144,7 +144,7 @@ public class BodyTubeHandlerTest extends RocksimTestBase { */ @Test public void testGetMaterialType() throws Exception { - Assert.assertEquals(Material.Type.BULK, new BodyTubeHandler(new Stage(), new WarningSet()).getMaterialType()); + Assert.assertEquals(Material.Type.BULK, new BodyTubeHandler(null, new Stage(), new WarningSet()).getMaterialType()); } } diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/FinSetHandlerTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/FinSetHandlerTest.java index 503654bc1..a963e52d4 100644 --- a/core/test/net/sf/openrocket/file/rocksim/importt/FinSetHandlerTest.java +++ b/core/test/net/sf/openrocket/file/rocksim/importt/FinSetHandlerTest.java @@ -28,7 +28,7 @@ public class FinSetHandlerTest extends TestCase { @org.junit.Test public void testAsOpenRocket() throws Exception { - FinSetHandler dto = new FinSetHandler(new BodyTube()); + FinSetHandler dto = new FinSetHandler(null, new BodyTube()); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); @@ -93,7 +93,7 @@ public class FinSetHandlerTest extends TestCase { */ @org.junit.Test public void testToCoordinates() throws Exception { - FinSetHandler holder = new FinSetHandler(new BodyTube()); + FinSetHandler holder = new FinSetHandler(null, new BodyTube()); Method method = FinSetHandler.class.getDeclaredMethod("toCoordinates", String.class, WarningSet.class); method.setAccessible(true); diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/InnerBodyTubeHandlerTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/InnerBodyTubeHandlerTest.java index f2533c355..b361c33b2 100644 --- a/core/test/net/sf/openrocket/file/rocksim/importt/InnerBodyTubeHandlerTest.java +++ b/core/test/net/sf/openrocket/file/rocksim/importt/InnerBodyTubeHandlerTest.java @@ -29,7 +29,7 @@ public class InnerBodyTubeHandlerTest extends RocksimTestBase { public void testConstructor() throws Exception { try { - new InnerBodyTubeHandler(null, new WarningSet()); + new InnerBodyTubeHandler(null, null, new WarningSet()); Assert.fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { @@ -37,7 +37,7 @@ public class InnerBodyTubeHandlerTest extends RocksimTestBase { } BodyTube tube = new BodyTube(); - InnerBodyTubeHandler handler = new InnerBodyTubeHandler(tube, new WarningSet()); + InnerBodyTubeHandler handler = new InnerBodyTubeHandler(null, tube, new WarningSet()); InnerTube component = (InnerTube) getField(handler, "bodyTube"); assertContains(component, tube.getChildren()); } @@ -49,8 +49,8 @@ public class InnerBodyTubeHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testOpenElement() throws Exception { - Assert.assertEquals(PlainTextHandler.INSTANCE, new InnerBodyTubeHandler(new BodyTube(), new WarningSet()).openElement(null, null, null)); - Assert.assertNotNull(new InnerBodyTubeHandler(new BodyTube(), new WarningSet()).openElement("AttachedParts", null, null)); + Assert.assertEquals(PlainTextHandler.INSTANCE, new InnerBodyTubeHandler(null, new BodyTube(), new WarningSet()).openElement(null, null, null)); + Assert.assertNotNull(new InnerBodyTubeHandler(null, new BodyTube(), new WarningSet()).openElement("AttachedParts", null, null)); } /** @@ -62,7 +62,7 @@ public class InnerBodyTubeHandlerTest extends RocksimTestBase { @org.junit.Test public void testCloseElement() throws Exception { BodyTube tube = new BodyTube(); - InnerBodyTubeHandler handler = new InnerBodyTubeHandler(tube, new WarningSet()); + InnerBodyTubeHandler handler = new InnerBodyTubeHandler(null, tube, new WarningSet()); InnerTube component = (InnerTube) getField(handler, "bodyTube"); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); @@ -126,7 +126,7 @@ public class InnerBodyTubeHandlerTest extends RocksimTestBase { @org.junit.Test public void testSetRelativePosition() throws Exception { BodyTube tube = new BodyTube(); - InnerBodyTubeHandler handler = new InnerBodyTubeHandler(tube, new WarningSet()); + InnerBodyTubeHandler handler = new InnerBodyTubeHandler(null, tube, new WarningSet()); InnerTube component = (InnerTube) getField(handler, "bodyTube"); handler.setRelativePosition(RocketComponent.Position.ABSOLUTE); Assert.assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition()); @@ -139,7 +139,7 @@ public class InnerBodyTubeHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetComponent() throws Exception { - Assert.assertTrue(new InnerBodyTubeHandler(new BodyTube(), new WarningSet()).getComponent() instanceof InnerTube); + Assert.assertTrue(new InnerBodyTubeHandler(null, new BodyTube(), new WarningSet()).getComponent() instanceof InnerTube); } /** @@ -149,7 +149,7 @@ public class InnerBodyTubeHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetMaterialType() throws Exception { - Assert.assertEquals(Material.Type.BULK, new InnerBodyTubeHandler(new BodyTube(), new WarningSet()).getMaterialType()); + Assert.assertEquals(Material.Type.BULK, new InnerBodyTubeHandler(null, new BodyTube(), new WarningSet()).getMaterialType()); } } diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/LaunchLugHandlerTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/LaunchLugHandlerTest.java index 937c8cbc4..df4e5400f 100644 --- a/core/test/net/sf/openrocket/file/rocksim/importt/LaunchLugHandlerTest.java +++ b/core/test/net/sf/openrocket/file/rocksim/importt/LaunchLugHandlerTest.java @@ -30,7 +30,7 @@ public class LaunchLugHandlerTest extends RocksimTestBase { public void testConstructor() throws Exception { try { - new LaunchLugHandler(null, new WarningSet()); + new LaunchLugHandler(null, null, new WarningSet()); Assert.fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { @@ -38,7 +38,7 @@ public class LaunchLugHandlerTest extends RocksimTestBase { } BodyTube tube = new BodyTube(); - LaunchLugHandler handler = new LaunchLugHandler(tube, new WarningSet()); + LaunchLugHandler handler = new LaunchLugHandler(null, tube, new WarningSet()); LaunchLug component = (LaunchLug) getField(handler, "lug"); assertContains(component, tube.getChildren()); } @@ -50,7 +50,7 @@ public class LaunchLugHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testOpenElement() throws Exception { - Assert.assertEquals(PlainTextHandler.INSTANCE, new LaunchLugHandler(new BodyTube(), new WarningSet()).openElement(null, null, null)); + Assert.assertEquals(PlainTextHandler.INSTANCE, new LaunchLugHandler(null, new BodyTube(), new WarningSet()).openElement(null, null, null)); } /** @@ -62,7 +62,7 @@ public class LaunchLugHandlerTest extends RocksimTestBase { @org.junit.Test public void testCloseElement() throws Exception { BodyTube tube = new BodyTube(); - LaunchLugHandler handler = new LaunchLugHandler(tube, new WarningSet()); + LaunchLugHandler handler = new LaunchLugHandler(null, tube, new WarningSet()); LaunchLug component = (LaunchLug) getField(handler, "lug"); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); @@ -117,7 +117,7 @@ public class LaunchLugHandlerTest extends RocksimTestBase { @org.junit.Test public void testSetRelativePosition() throws Exception { BodyTube tube = new BodyTube(); - LaunchLugHandler handler = new LaunchLugHandler(tube, new WarningSet()); + LaunchLugHandler handler = new LaunchLugHandler(null, tube, new WarningSet()); LaunchLug component = (LaunchLug) getField(handler, "lug"); handler.setRelativePosition(RocketComponent.Position.ABSOLUTE); Assert.assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition()); @@ -130,7 +130,7 @@ public class LaunchLugHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetComponent() throws Exception { - Assert.assertTrue(new LaunchLugHandler(new BodyTube(), new WarningSet()).getComponent() instanceof LaunchLug); + Assert.assertTrue(new LaunchLugHandler(null, new BodyTube(), new WarningSet()).getComponent() instanceof LaunchLug); } /** @@ -140,7 +140,7 @@ public class LaunchLugHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetMaterialType() throws Exception { - Assert.assertEquals(Material.Type.BULK, new LaunchLugHandler(new BodyTube(), new WarningSet()).getMaterialType()); + Assert.assertEquals(Material.Type.BULK, new LaunchLugHandler(null, new BodyTube(), new WarningSet()).getMaterialType()); } diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/MassObjectHandlerTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/MassObjectHandlerTest.java index 89cf327ab..99094d8e2 100644 --- a/core/test/net/sf/openrocket/file/rocksim/importt/MassObjectHandlerTest.java +++ b/core/test/net/sf/openrocket/file/rocksim/importt/MassObjectHandlerTest.java @@ -29,7 +29,7 @@ public class MassObjectHandlerTest extends RocksimTestBase { public void testConstructor() throws Exception { try { - new MassObjectHandler(null, new WarningSet()); + new MassObjectHandler(null, null, new WarningSet()); Assert.fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { @@ -37,7 +37,7 @@ public class MassObjectHandlerTest extends RocksimTestBase { } BodyTube tube = new BodyTube(); - MassObjectHandler handler = new MassObjectHandler(tube, new WarningSet()); + MassObjectHandler handler = new MassObjectHandler(null, tube, new WarningSet()); MassComponent mass = (MassComponent) getField(handler, "mass"); MassComponent current = (MassComponent) getField(handler, "current"); Assert.assertEquals(mass, current); @@ -50,7 +50,7 @@ public class MassObjectHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testOpenElement() throws Exception { - Assert.assertEquals(PlainTextHandler.INSTANCE, new MassObjectHandler(new BodyTube(), new WarningSet()).openElement(null, null, null)); + Assert.assertEquals(PlainTextHandler.INSTANCE, new MassObjectHandler(null, new BodyTube(), new WarningSet()).openElement(null, null, null)); } /** @@ -65,7 +65,7 @@ public class MassObjectHandlerTest extends RocksimTestBase { HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); - MassObjectHandler handler = new MassObjectHandler(tube, new WarningSet()); + MassObjectHandler handler = new MassObjectHandler(null, tube, new WarningSet()); MassComponent component = (MassComponent) getField(handler, "mass"); handler.closeElement("Len", attributes, "-1", warnings); @@ -98,7 +98,7 @@ public class MassObjectHandlerTest extends RocksimTestBase { @org.junit.Test public void testSetRelativePosition() throws Exception { BodyTube tube = new BodyTube(); - MassObjectHandler handler = new MassObjectHandler(tube, new WarningSet()); + MassObjectHandler handler = new MassObjectHandler(null, tube, new WarningSet()); MassComponent component = (MassComponent) getField(handler, "mass"); handler.setRelativePosition(RocketComponent.Position.ABSOLUTE); Assert.assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition()); @@ -111,7 +111,7 @@ public class MassObjectHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetComponent() throws Exception { - Assert.assertTrue(new MassObjectHandler(new BodyTube(), new WarningSet()).getComponent() instanceof MassComponent); + Assert.assertTrue(new MassObjectHandler(null, new BodyTube(), new WarningSet()).getComponent() instanceof MassComponent); } /** @@ -121,6 +121,6 @@ public class MassObjectHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetMaterialType() throws Exception { - Assert.assertEquals(Material.Type.LINE, new MassObjectHandler(new BodyTube(), new WarningSet()).getMaterialType()); + Assert.assertEquals(Material.Type.LINE, new MassObjectHandler(null, new BodyTube(), new WarningSet()).getMaterialType()); } } diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/NoseConeHandlerTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/NoseConeHandlerTest.java index 03943d8b7..6b88cb82f 100644 --- a/core/test/net/sf/openrocket/file/rocksim/importt/NoseConeHandlerTest.java +++ b/core/test/net/sf/openrocket/file/rocksim/importt/NoseConeHandlerTest.java @@ -32,7 +32,7 @@ public class NoseConeHandlerTest extends RocksimTestBase { public void testConstructor() throws Exception { try { - new NoseConeHandler(null, new WarningSet()); + new NoseConeHandler(null, null, new WarningSet()); Assert.fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { @@ -40,7 +40,7 @@ public class NoseConeHandlerTest extends RocksimTestBase { } Stage stage = new Stage(); - NoseConeHandler handler = new NoseConeHandler(stage, new WarningSet()); + NoseConeHandler handler = new NoseConeHandler(null, stage, new WarningSet()); NoseCone component = (NoseCone) getField(handler, "noseCone"); assertContains(component, stage.getChildren()); } @@ -52,8 +52,8 @@ public class NoseConeHandlerTest extends RocksimTestBase { */ @Test public void testOpenElement() throws Exception { - Assert.assertEquals(PlainTextHandler.INSTANCE, new NoseConeHandler(new Stage(), new WarningSet()).openElement(null, null, null)); - Assert.assertNotNull(new NoseConeHandler(new Stage(), new WarningSet()).openElement("AttachedParts", null, null)); + Assert.assertEquals(PlainTextHandler.INSTANCE, new NoseConeHandler(null, new Stage(), new WarningSet()).openElement(null, null, null)); + Assert.assertNotNull(new NoseConeHandler(null, new Stage(), new WarningSet()).openElement("AttachedParts", null, null)); } /** @@ -69,7 +69,7 @@ public class NoseConeHandlerTest extends RocksimTestBase { HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); - NoseConeHandler handler = new NoseConeHandler(stage, warnings); + NoseConeHandler handler = new NoseConeHandler(null, stage, warnings); NoseCone component = (NoseCone) getField(handler, "noseCone"); handler.closeElement("ShapeCode", attributes, "0", warnings); @@ -188,7 +188,7 @@ public class NoseConeHandlerTest extends RocksimTestBase { */ @Test public void testGetComponent() throws Exception { - Assert.assertTrue(new NoseConeHandler(new Stage(), new WarningSet()).getComponent() instanceof NoseCone); + Assert.assertTrue(new NoseConeHandler(null, new Stage(), new WarningSet()).getComponent() instanceof NoseCone); } /** @@ -198,6 +198,6 @@ public class NoseConeHandlerTest extends RocksimTestBase { */ @Test public void testGetMaterialType() throws Exception { - Assert.assertEquals(Material.Type.BULK, new NoseConeHandler(new Stage(), new WarningSet()).getMaterialType()); + Assert.assertEquals(Material.Type.BULK, new NoseConeHandler(null, new Stage(), new WarningSet()).getMaterialType()); } } diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/ParachuteHandlerTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/ParachuteHandlerTest.java index 135a94fcd..431d33c95 100644 --- a/core/test/net/sf/openrocket/file/rocksim/importt/ParachuteHandlerTest.java +++ b/core/test/net/sf/openrocket/file/rocksim/importt/ParachuteHandlerTest.java @@ -26,7 +26,7 @@ public class ParachuteHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testOpenElement() throws Exception { - Assert.assertEquals(PlainTextHandler.INSTANCE, new ParachuteHandler(new BodyTube(), new WarningSet()).openElement(null, null, null)); + Assert.assertEquals(PlainTextHandler.INSTANCE, new ParachuteHandler(null, new BodyTube(), new WarningSet()).openElement(null, null, null)); } /** @@ -38,7 +38,7 @@ public class ParachuteHandlerTest extends RocksimTestBase { public void testCloseElement() throws Exception { BodyTube tube = new BodyTube(); - ParachuteHandler handler = new ParachuteHandler(tube, new WarningSet()); + ParachuteHandler handler = new ParachuteHandler(null, tube, new WarningSet()); Parachute component = (Parachute) getField(handler, "chute"); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); @@ -89,7 +89,7 @@ public class ParachuteHandlerTest extends RocksimTestBase { public void testConstructor() throws Exception { try { - new ParachuteHandler(null, new WarningSet()); + new ParachuteHandler(null, null, new WarningSet()); Assert.fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { @@ -97,7 +97,7 @@ public class ParachuteHandlerTest extends RocksimTestBase { } BodyTube tube = new BodyTube(); - ParachuteHandler handler = new ParachuteHandler(tube, new WarningSet()); + ParachuteHandler handler = new ParachuteHandler(null, tube, new WarningSet()); Parachute component = (Parachute) getField(handler, "chute"); assertContains(component, tube.getChildren()); } @@ -110,7 +110,7 @@ public class ParachuteHandlerTest extends RocksimTestBase { @org.junit.Test public void testSetRelativePosition() throws Exception { BodyTube tube = new BodyTube(); - ParachuteHandler handler = new ParachuteHandler(tube, new WarningSet()); + ParachuteHandler handler = new ParachuteHandler(null, tube, new WarningSet()); Parachute component = (Parachute) getField(handler, "chute"); handler.setRelativePosition(RocketComponent.Position.ABSOLUTE); Assert.assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition()); @@ -123,7 +123,7 @@ public class ParachuteHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetComponent() throws Exception { - Assert.assertTrue(new ParachuteHandler(new BodyTube(), new WarningSet()).getComponent() instanceof Parachute); + Assert.assertTrue(new ParachuteHandler(null, new BodyTube(), new WarningSet()).getComponent() instanceof Parachute); } /** @@ -133,7 +133,7 @@ public class ParachuteHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetMaterialType() throws Exception { - Assert.assertEquals(Material.Type.SURFACE, new ParachuteHandler(new BodyTube(), new WarningSet()).getMaterialType()); + Assert.assertEquals(Material.Type.SURFACE, new ParachuteHandler(null, new BodyTube(), new WarningSet()).getMaterialType()); } /** @@ -144,7 +144,7 @@ public class ParachuteHandlerTest extends RocksimTestBase { @org.junit.Test public void testEndHandler() throws Exception { BodyTube tube = new BodyTube(); - ParachuteHandler handler = new ParachuteHandler(tube, new WarningSet()); + ParachuteHandler handler = new ParachuteHandler(null, tube, new WarningSet()); Parachute component = (Parachute) getField(handler, "chute"); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/RingHandlerTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/RingHandlerTest.java index 00abac15a..8b06b848b 100644 --- a/core/test/net/sf/openrocket/file/rocksim/importt/RingHandlerTest.java +++ b/core/test/net/sf/openrocket/file/rocksim/importt/RingHandlerTest.java @@ -31,7 +31,7 @@ public class RingHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testOpenElement() throws Exception { - Assert.assertEquals(PlainTextHandler.INSTANCE, new RingHandler(new BodyTube(), new WarningSet()).openElement(null, null, null)); + Assert.assertEquals(PlainTextHandler.INSTANCE, new RingHandler(null, new BodyTube(), new WarningSet()).openElement(null, null, null)); } /** @@ -43,7 +43,7 @@ public class RingHandlerTest extends RocksimTestBase { public void testCloseElement() throws Exception { BodyTube tube = new BodyTube(); - RingHandler handler = new RingHandler(tube, new WarningSet()); + RingHandler handler = new RingHandler(null, tube, new WarningSet()); CenteringRing component = (CenteringRing) getField(handler, "ring"); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); @@ -86,9 +86,8 @@ public class RingHandlerTest extends RocksimTestBase { @Test public void testBulkhead() throws Exception { BodyTube tube = new BodyTube(); - RingHandler handler = new RingHandler(tube, new WarningSet()); - @SuppressWarnings("unused") - CenteringRing component = (CenteringRing) getField(handler, "ring"); + RingHandler handler = new RingHandler(null, tube, new WarningSet()); + CenteringRing component = (CenteringRing) getField(handler, "ring"); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); @@ -123,7 +122,7 @@ public class RingHandlerTest extends RocksimTestBase { @Test public void testTubeCoupler() throws Exception { BodyTube tube = new BodyTube(); - RingHandler handler = new RingHandler(tube, new WarningSet()); + RingHandler handler = new RingHandler(null, tube, new WarningSet()); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); @@ -157,7 +156,7 @@ public class RingHandlerTest extends RocksimTestBase { @Test public void testEngineBlock() throws Exception { BodyTube tube = new BodyTube(); - RingHandler handler = new RingHandler(tube, new WarningSet()); + RingHandler handler = new RingHandler(null, tube, new WarningSet()); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); @@ -194,7 +193,7 @@ public class RingHandlerTest extends RocksimTestBase { @Test public void testRing() throws Exception { BodyTube tube = new BodyTube(); - RingHandler handler = new RingHandler(tube, new WarningSet()); + RingHandler handler = new RingHandler(null, tube, new WarningSet()); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); @@ -229,7 +228,7 @@ public class RingHandlerTest extends RocksimTestBase { public void testConstructor() throws Exception { try { - new RingHandler(null, new WarningSet()); + new RingHandler(null, null, new WarningSet()); Assert.fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { @@ -237,9 +236,8 @@ public class RingHandlerTest extends RocksimTestBase { } BodyTube tube = new BodyTube(); - RingHandler handler = new RingHandler(tube, new WarningSet()); - @SuppressWarnings("unused") - CenteringRing component = (CenteringRing) getField(handler, "ring"); + RingHandler handler = new RingHandler(null, tube, new WarningSet()); + CenteringRing component = (CenteringRing) getField(handler, "ring"); } /** @@ -250,7 +248,7 @@ public class RingHandlerTest extends RocksimTestBase { @org.junit.Test public void testSetRelativePosition() throws Exception { BodyTube tube = new BodyTube(); - RingHandler handler = new RingHandler(tube, new WarningSet()); + RingHandler handler = new RingHandler(null, tube, new WarningSet()); CenteringRing component = (CenteringRing) getField(handler, "ring"); handler.setRelativePosition(RocketComponent.Position.ABSOLUTE); Assert.assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition()); @@ -264,7 +262,7 @@ public class RingHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetComponent() throws Exception { - Assert.assertTrue(new RingHandler(new BodyTube(), new WarningSet()).getComponent() instanceof CenteringRing); + Assert.assertTrue(new RingHandler(null, new BodyTube(), new WarningSet()).getComponent() instanceof CenteringRing); } /** @@ -274,7 +272,7 @@ public class RingHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetMaterialType() throws Exception { - Assert.assertEquals(Material.Type.BULK, new RingHandler(new BodyTube(), new WarningSet()).getMaterialType()); + Assert.assertEquals(Material.Type.BULK, new RingHandler(null, new BodyTube(), new WarningSet()).getMaterialType()); } diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/RocksimContentHandlerTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/RocksimContentHandlerTest.java deleted file mode 100644 index 0eb1d6253..000000000 --- a/core/test/net/sf/openrocket/file/rocksim/importt/RocksimContentHandlerTest.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * RocksimContentHandlerTest.java - */ -package net.sf.openrocket.file.rocksim.importt; - -import org.junit.Assert; - -/** - * RocksimContentHandler Tester. - * - */ -public class RocksimContentHandlerTest { - - /** - * - * Method: getDocument() - * - * @throws Exception thrown if something goes awry - */ - @org.junit.Test - public void testGetDocument() throws Exception { - RocksimContentHandler handler = new RocksimContentHandler(); - Assert.assertNotNull(handler.getDocument()); - } - -} diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/RocksimLoaderTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/RocksimLoaderTest.java index 48ea77aff..64c358cca 100644 --- a/core/test/net/sf/openrocket/file/rocksim/importt/RocksimLoaderTest.java +++ b/core/test/net/sf/openrocket/file/rocksim/importt/RocksimLoaderTest.java @@ -9,7 +9,9 @@ import java.io.IOException; import java.io.InputStream; import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.document.OpenRocketDocumentFactory; import net.sf.openrocket.file.DatabaseMotorFinder; +import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.RocketLoadException; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.LaunchLug; @@ -34,8 +36,11 @@ public class RocksimLoaderTest { InputStream stream = this.getClass().getResourceAsStream("PodFins.rkt"); Assert.assertNotNull("Could not open PodFins.rkt", stream); try { - OpenRocketDocument doc = loader.loadFromStream(new BufferedInputStream(stream), new DatabaseMotorFinder()); - Assert.assertNotNull(doc); + OpenRocketDocument doc = OpenRocketDocumentFactory.createEmptyRocket(); + DocumentLoadingContext context = new DocumentLoadingContext(); + context.setOpenRocketDocument(doc); + context.setMotorFinder(new DatabaseMotorFinder()); + loader.loadFromStream(context, new BufferedInputStream(stream)); Rocket rocket = doc.getRocket(); Assert.assertNotNull(rocket); } catch (IllegalStateException ise) { @@ -64,7 +69,12 @@ public class RocksimLoaderTest { stream = this.getClass().getResourceAsStream("rocksimTestRocket2.rkt"); Assert.assertNotNull("Could not open rocksimTestRocket2.rkt", stream); - doc = loader.loadFromStream(new BufferedInputStream(stream), new DatabaseMotorFinder()); + + doc = OpenRocketDocumentFactory.createEmptyRocket(); + DocumentLoadingContext context = new DocumentLoadingContext(); + context.setOpenRocketDocument(doc); + context.setMotorFinder(new DatabaseMotorFinder()); + loader.loadFromStream(context, new BufferedInputStream(stream)); Assert.assertNotNull(doc); rocket = doc.getRocket(); @@ -87,7 +97,12 @@ public class RocksimLoaderTest { stream = this.getClass().getResourceAsStream("rocksimTestRocket3.rkt"); Assert.assertNotNull("Could not open rocksimTestRocket3.rkt", stream); - doc = loader.loadFromStream(new BufferedInputStream(stream), new DatabaseMotorFinder()); + + doc = OpenRocketDocumentFactory.createEmptyRocket(); + context = new DocumentLoadingContext(); + context.setOpenRocketDocument(doc); + context.setMotorFinder(new DatabaseMotorFinder()); + loader.loadFromStream(context, new BufferedInputStream(stream)); Assert.assertNotNull(doc); rocket = doc.getRocket(); @@ -133,7 +148,12 @@ public class RocksimLoaderTest { InputStream stream = RocksimLoaderTest.class.getResourceAsStream("rocksimTestRocket1.rkt"); try { Assert.assertNotNull("Could not open rocksimTestRocket1.rkt", stream); - return theLoader.loadFromStream(new BufferedInputStream(stream), new DatabaseMotorFinder()); + OpenRocketDocument doc = OpenRocketDocumentFactory.createEmptyRocket(); + DocumentLoadingContext context = new DocumentLoadingContext(); + context.setOpenRocketDocument(doc); + context.setMotorFinder(new DatabaseMotorFinder()); + theLoader.loadFromStream(context, new BufferedInputStream(stream)); + return doc; } finally { stream.close(); } @@ -143,7 +163,12 @@ public class RocksimLoaderTest { InputStream stream = RocksimLoaderTest.class.getResourceAsStream("rocksimTestRocket3.rkt"); try { Assert.assertNotNull("Could not open rocksimTestRocket3.rkt", stream); - return theLoader.loadFromStream(new BufferedInputStream(stream), new DatabaseMotorFinder()); + OpenRocketDocument doc = OpenRocketDocumentFactory.createEmptyRocket(); + DocumentLoadingContext context = new DocumentLoadingContext(); + context.setOpenRocketDocument(doc); + context.setMotorFinder(new DatabaseMotorFinder()); + theLoader.loadFromStream(context, new BufferedInputStream(stream)); + return doc; } finally { stream.close(); } diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/StreamerHandlerTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/StreamerHandlerTest.java index fe2b9c9c1..d6370e29c 100644 --- a/core/test/net/sf/openrocket/file/rocksim/importt/StreamerHandlerTest.java +++ b/core/test/net/sf/openrocket/file/rocksim/importt/StreamerHandlerTest.java @@ -28,7 +28,7 @@ public class StreamerHandlerTest extends RocksimTestBase { */ @Test public void testOpenElement() throws Exception { - Assert.assertEquals(PlainTextHandler.INSTANCE, new StreamerHandler(new BodyTube(), new WarningSet()).openElement(null, null, null)); + Assert.assertEquals(PlainTextHandler.INSTANCE, new StreamerHandler(null, new BodyTube(), new WarningSet()).openElement(null, null, null)); } /** @@ -40,7 +40,7 @@ public class StreamerHandlerTest extends RocksimTestBase { public void testCloseElement() throws Exception { BodyTube tube = new BodyTube(); - StreamerHandler handler = new StreamerHandler(tube, new WarningSet()); + StreamerHandler handler = new StreamerHandler(null, tube, new WarningSet()); Streamer component = (Streamer) getField(handler, "streamer"); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); @@ -85,7 +85,7 @@ public class StreamerHandlerTest extends RocksimTestBase { public void testConstructor() throws Exception { try { - new StreamerHandler(null, new WarningSet()); + new StreamerHandler(null, null, new WarningSet()); Assert.fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { @@ -93,7 +93,7 @@ public class StreamerHandlerTest extends RocksimTestBase { } BodyTube tube = new BodyTube(); - StreamerHandler handler = new StreamerHandler(tube, new WarningSet()); + StreamerHandler handler = new StreamerHandler(null, tube, new WarningSet()); Streamer component = (Streamer) getField(handler, "streamer"); assertContains(component, tube.getChildren()); } @@ -106,7 +106,7 @@ public class StreamerHandlerTest extends RocksimTestBase { @Test public void testSetRelativePosition() throws Exception { BodyTube tube = new BodyTube(); - StreamerHandler handler = new StreamerHandler(tube, new WarningSet()); + StreamerHandler handler = new StreamerHandler(null, tube, new WarningSet()); Streamer component = (Streamer) getField(handler, "streamer"); handler.setRelativePosition(RocketComponent.Position.ABSOLUTE); Assert.assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition()); @@ -119,7 +119,7 @@ public class StreamerHandlerTest extends RocksimTestBase { */ @Test public void testGetComponent() throws Exception { - Assert.assertTrue(new StreamerHandler(new BodyTube(), new WarningSet()).getComponent() instanceof Streamer); + Assert.assertTrue(new StreamerHandler(null, new BodyTube(), new WarningSet()).getComponent() instanceof Streamer); } /** @@ -129,7 +129,7 @@ public class StreamerHandlerTest extends RocksimTestBase { */ @Test public void testGetMaterialType() throws Exception { - Assert.assertEquals(Material.Type.SURFACE, new StreamerHandler(new BodyTube(), new WarningSet()).getMaterialType()); + Assert.assertEquals(Material.Type.SURFACE, new StreamerHandler(null, new BodyTube(), new WarningSet()).getMaterialType()); } /** @@ -140,7 +140,7 @@ public class StreamerHandlerTest extends RocksimTestBase { @Test public void testEndHandler() throws Exception { BodyTube tube = new BodyTube(); - StreamerHandler handler = new StreamerHandler(tube, new WarningSet()); + StreamerHandler handler = new StreamerHandler(null, tube, new WarningSet()); Streamer component = (Streamer) getField(handler, "streamer"); HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); diff --git a/core/test/net/sf/openrocket/file/rocksim/importt/TransitionHandlerTest.java b/core/test/net/sf/openrocket/file/rocksim/importt/TransitionHandlerTest.java index 28087d170..a2298e8f5 100644 --- a/core/test/net/sf/openrocket/file/rocksim/importt/TransitionHandlerTest.java +++ b/core/test/net/sf/openrocket/file/rocksim/importt/TransitionHandlerTest.java @@ -29,7 +29,7 @@ public class TransitionHandlerTest extends RocksimTestBase { public void testConstructor() throws Exception { try { - new TransitionHandler(null, new WarningSet()); + new TransitionHandler(null, null, new WarningSet()); Assert.fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { @@ -37,7 +37,7 @@ public class TransitionHandlerTest extends RocksimTestBase { } Stage stage = new Stage(); - TransitionHandler handler = new TransitionHandler(stage, new WarningSet()); + TransitionHandler handler = new TransitionHandler(null, stage, new WarningSet()); Transition component = (Transition) getField(handler, "transition"); assertContains(component, stage.getChildren()); } @@ -49,7 +49,7 @@ public class TransitionHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testOpenElement() throws Exception { - Assert.assertEquals(PlainTextHandler.INSTANCE, new TransitionHandler(new Stage(), new WarningSet()).openElement(null, null, null)); + Assert.assertEquals(PlainTextHandler.INSTANCE, new TransitionHandler(null, new Stage(), new WarningSet()).openElement(null, null, null)); } /** @@ -64,7 +64,7 @@ public class TransitionHandlerTest extends RocksimTestBase { HashMap attributes = new HashMap(); WarningSet warnings = new WarningSet(); - TransitionHandler handler = new TransitionHandler(stage, new WarningSet()); + TransitionHandler handler = new TransitionHandler(null, stage, new WarningSet()); Transition component = (Transition) getField(handler, "transition"); handler.closeElement("ShapeCode", attributes, "0", warnings); @@ -214,7 +214,7 @@ public class TransitionHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetComponent() throws Exception { - Assert.assertTrue(new TransitionHandler(new Stage(), new WarningSet()).getComponent() instanceof Transition); + Assert.assertTrue(new TransitionHandler(null, new Stage(), new WarningSet()).getComponent() instanceof Transition); } /** @@ -224,7 +224,7 @@ public class TransitionHandlerTest extends RocksimTestBase { */ @org.junit.Test public void testGetMaterialType() throws Exception { - Assert.assertEquals(Material.Type.BULK, new TransitionHandler(new Stage(), new WarningSet()).getMaterialType()); + Assert.assertEquals(Material.Type.BULK, new TransitionHandler(null, new Stage(), new WarningSet()).getMaterialType()); } diff --git a/core/test/net/sf/openrocket/plugin/Example2Plugin.java b/core/test/net/sf/openrocket/plugin/Example2Plugin.java new file mode 100644 index 000000000..a705139bd --- /dev/null +++ b/core/test/net/sf/openrocket/plugin/Example2Plugin.java @@ -0,0 +1,9 @@ +package net.sf.openrocket.plugin; + +/** + * Example plugin interface for testing purposes. + */ +@Plugin +public interface Example2Plugin { + +} diff --git a/core/src/net/sf/openrocket/plugin/ExamplePlugin.java b/core/test/net/sf/openrocket/plugin/ExamplePlugin.java similarity index 62% rename from core/src/net/sf/openrocket/plugin/ExamplePlugin.java rename to core/test/net/sf/openrocket/plugin/ExamplePlugin.java index b69950e80..a3e78481c 100644 --- a/core/src/net/sf/openrocket/plugin/ExamplePlugin.java +++ b/core/test/net/sf/openrocket/plugin/ExamplePlugin.java @@ -1,8 +1,9 @@ package net.sf.openrocket.plugin; +/** + * Example plugin for testing purposes. + */ @Plugin public interface ExamplePlugin { - public void doit(); - } diff --git a/core/src/net/sf/openrocket/plugin/ExamplePluginImpl.java b/core/test/net/sf/openrocket/plugin/ExamplePluginImpl.java similarity index 50% rename from core/src/net/sf/openrocket/plugin/ExamplePluginImpl.java rename to core/test/net/sf/openrocket/plugin/ExamplePluginImpl.java index 05a0aeb5b..b63d4df3f 100644 --- a/core/src/net/sf/openrocket/plugin/ExamplePluginImpl.java +++ b/core/test/net/sf/openrocket/plugin/ExamplePluginImpl.java @@ -1,10 +1,9 @@ package net.sf.openrocket.plugin; +/** + * ExamplePlugin implementation for testing purposes. + */ +@Plugin public class ExamplePluginImpl implements ExamplePlugin { - @Override - public void doit() { - System.out.println("ExamplePluginImpl.doit() called"); - } - } diff --git a/core/test/net/sf/openrocket/plugin/MultiPluginImpl.java b/core/test/net/sf/openrocket/plugin/MultiPluginImpl.java new file mode 100644 index 000000000..a48398536 --- /dev/null +++ b/core/test/net/sf/openrocket/plugin/MultiPluginImpl.java @@ -0,0 +1,10 @@ +package net.sf.openrocket.plugin; + +/** + * Plugin that implements both ExamplePlugin and Example2Plugin + * for testing purposes. + */ +@Plugin +public class MultiPluginImpl implements ExamplePlugin, Example2Plugin { + +} diff --git a/core/test/net/sf/openrocket/plugin/NotAnExamplePluginImpl.java b/core/test/net/sf/openrocket/plugin/NotAnExamplePluginImpl.java new file mode 100644 index 000000000..3f717389b --- /dev/null +++ b/core/test/net/sf/openrocket/plugin/NotAnExamplePluginImpl.java @@ -0,0 +1,9 @@ +package net.sf.openrocket.plugin; + +/** + * An implementation of ExamplePlugin that is not annotated with @Plugin + * for testing purposes. This should not be injected into the test class. + */ +public class NotAnExamplePluginImpl implements ExamplePlugin { + +} diff --git a/core/test/net/sf/openrocket/plugin/PluginTest.java b/core/test/net/sf/openrocket/plugin/PluginTest.java new file mode 100644 index 000000000..8e425b622 --- /dev/null +++ b/core/test/net/sf/openrocket/plugin/PluginTest.java @@ -0,0 +1,26 @@ +package net.sf.openrocket.plugin; + +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * Test the plugin loading system using Guice. + * + * This is more of an integration than a unit test. It uses the + * PluginModule to load a Guice injector, and then verifies that it + * has found the appropriate plugins. + */ +public class PluginTest { + + @Test + public void testPluginModule() { + + Injector injector = Guice.createInjector(new PluginModule()); + PluginTester tester = injector.getInstance(PluginTester.class); + tester.testPlugins(); + + } + +} diff --git a/core/test/net/sf/openrocket/plugin/PluginTester.java b/core/test/net/sf/openrocket/plugin/PluginTester.java new file mode 100644 index 000000000..87ee2f202 --- /dev/null +++ b/core/test/net/sf/openrocket/plugin/PluginTester.java @@ -0,0 +1,38 @@ +package net.sf.openrocket.plugin; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import net.sf.openrocket.util.ArrayList; + +import com.google.inject.Inject; + +public class PluginTester { + + @Inject + private Set examplePlugins; + @Inject + private Set example2Plugins; + + + public void testPlugins() { + assertContains(examplePlugins, ExamplePluginImpl.class, MultiPluginImpl.class, JarPluginImpl.class); + assertContains(example2Plugins, MultiPluginImpl.class); + } + + + private void assertContains(Set set, Class... classes) { + assertEquals(classes.length, set.size()); + + List> list = new ArrayList>(Arrays.asList(classes)); + for (Object o : set) { + Class c = o.getClass(); + assertTrue(list.contains(c)); + list.remove(c); + } + } +} diff --git a/core/test/net/sf/openrocket/rocketcomponent/ComponentCompare.java b/core/test/net/sf/openrocket/rocketcomponent/ComponentCompare.java index aa3fc486d..f37d71d6c 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/ComponentCompare.java +++ b/core/test/net/sf/openrocket/rocketcomponent/ComponentCompare.java @@ -1,6 +1,9 @@ package net.sf.openrocket.rocketcomponent; -import static org.junit.Assert.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.lang.reflect.Method; import java.util.Iterator; @@ -15,10 +18,10 @@ public class ComponentCompare { private static final String[] IGNORED_METHODS = { "getClass", "getChildCount", "getChildren", "getNextComponent", "getID", "getPreviousComponent", "getParent", "getRocket", "getRoot", "getStage", - "getStageNumber", "getComponentName", + "getStageNumber", "getComponentName", "getDefaultFlightConfiguration", // Rocket specific methods: "getModID", "getMassModID", "getAerodynamicModID", "getTreeModID", "getFunctionalModID", - "getMotorConfigurationIDs", "getDefaultConfiguration", "getMotorMounts" + "getFlightConfigurationIDs", "getDefaultConfiguration", "getMotorMounts" }; @@ -38,7 +41,7 @@ public class ComponentCompare { } - + public static void assertDeepEquality(RocketComponent c1, RocketComponent c2) { assertEquality(c1, c2); @@ -54,7 +57,7 @@ public class ComponentCompare { } - + public static void assertDeepSimilarity(RocketComponent c1, RocketComponent c2, boolean allowNameDifference) { assertSimilarity(c1, c2, allowNameDifference); @@ -71,7 +74,7 @@ public class ComponentCompare { } - + /** * Check whether the two components are similar. Two components are similar * if each of the getXXX and isXXX methods that both object types have return @@ -114,7 +117,7 @@ public class ComponentCompare { if (allowNameDifference && name.equals("getName")) continue; - + // Check for method in other class Method m2; try { diff --git a/core/test/net/sf/openrocket/simulation/customexpression/TestExpressions.java b/core/test/net/sf/openrocket/simulation/customexpression/TestExpressions.java index fd7769172..9df240497 100644 --- a/core/test/net/sf/openrocket/simulation/customexpression/TestExpressions.java +++ b/core/test/net/sf/openrocket/simulation/customexpression/TestExpressions.java @@ -1,22 +1,22 @@ package net.sf.openrocket.simulation.customexpression; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.document.OpenRocketDocumentFactory; + import org.junit.Test; -import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.rocketcomponent.Rocket; - public class TestExpressions { - + @Test public void testExpressions() { // TODO Auto-generated constructor stub - OpenRocketDocument doc = new OpenRocketDocument(new Rocket()); + OpenRocketDocument doc = OpenRocketDocumentFactory.createNewRocket(); //CustomExpression exp = new CustomExpression(doc, "Kinetic energy", "Ek", "J", ".5*m*Vt^2"); CustomExpression exp = new CustomExpression(doc, "Average mass", "Mavg", "kg", "mean(m[0:t])"); - System.out.println( exp.getExpressionString() ); + System.out.println(exp.getExpressionString()); } } diff --git a/core/test/net/sf/openrocket/util/TestMutex.java b/core/test/net/sf/openrocket/util/TestMutex.java index 94d5a9f58..aa220a510 100644 --- a/core/test/net/sf/openrocket/util/TestMutex.java +++ b/core/test/net/sf/openrocket/util/TestMutex.java @@ -1,6 +1,10 @@ package net.sf.openrocket.util; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import org.junit.Before; import org.junit.Test; @@ -53,7 +57,7 @@ public class TestMutex { } - + private volatile int testState = 0; private volatile String failure = null; @@ -106,6 +110,7 @@ public class TestMutex { testState = 6; } catch (Exception e) { + e.printStackTrace(); failure = "Exception occurred in thread: " + e; return; } diff --git a/core/test/net/sf/openrocket/util/TextUtilTest.java b/core/test/net/sf/openrocket/util/TextUtilTest.java index 4c040fbbe..e3b2558a3 100644 --- a/core/test/net/sf/openrocket/util/TextUtilTest.java +++ b/core/test/net/sf/openrocket/util/TextUtilTest.java @@ -1,8 +1,8 @@ package net.sf.openrocket.util; import static java.lang.Math.PI; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import java.nio.charset.Charset; import java.util.Random; @@ -17,19 +17,19 @@ public class TextUtilTest { Charset us_ascii = Charset.forName("US-ASCII"); byte[] ZIP_SIGNATURE_CORRECT = "PK".getBytes(us_ascii); - byte[] ZIP_SIGNATURE_TEST = TextUtil.convertStringToBytes( "PK", us_ascii); + byte[] ZIP_SIGNATURE_TEST = TextUtil.asciiBytes("PK"); - assertArrayEquals( ZIP_SIGNATURE_CORRECT, ZIP_SIGNATURE_TEST ); + assertArrayEquals(ZIP_SIGNATURE_CORRECT, ZIP_SIGNATURE_TEST); byte[] OPENROCKET_SIGNATURE_CORRECT = "&")); + assertEquals(""'", TextUtil.escapeXML("\"'")); + assertEquals("foo\n\r\tbar", TextUtil.escapeXML("foo\n\r\tbar")); + assertEquals("foo�bar", TextUtil.escapeXML("foo" + ((char) 0) + ((char) 1) + ((char) 31) + ((char) 127) + "bar")); + } + }