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 extends SimulationEngine> simulationEngineClass = BasicEventSimulationEngine.class;
private Class extends SimulationStepper> simulationStepperClass = RK4SimulationStepper.class;
private Class extends AerodynamicCalculator> aerodynamicCalculatorClass = BarrowmanCalculator.class;
@SuppressWarnings("unused")
private Class extends MassCalculator> 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 extends RocketComponent> 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 extends Enum> 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 extends Enum> 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 extends RocketComponent> 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) + "" + tag + ">";
+ return str + " density=\"" + mat.getDensity() + "\">" + TextUtil.escapeXML(baseName) + "" + tag + ">";
}
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