[#2377] Add booster/pod split functionality

This commit is contained in:
SiboVG 2024-01-21 02:01:18 +01:00
parent d2fc662a56
commit 940b913dfa
24 changed files with 270 additions and 155 deletions

View File

@ -1153,16 +1153,20 @@ ComponentCfgDlg.MultiComponentEdit.ttip = <html>You are editing the following co
ComponentCfgDlg.Modify = Modify ComponentCfgDlg.Modify = Modify
ComponentCfgDlg.ModifyComponents = Modify components ComponentCfgDlg.ModifyComponents = Modify components
!StageConfig !ComponentAssemblyConfig
StageConfig.tab.Separation = Separation ComponentAssemblyConfig.tab.Separation = Separation
StageConfig.tab.Separation.ttip = Stage separation options ComponentAssemblyConfig.tab.Separation.ttip = Stage separation options
StageConfig.separation.lbl.title = Select when this stage separates: ComponentAssemblyConfig.separation.lbl.title = Select when this stage separates:
StageConfig.separation.lbl.plus = plus ComponentAssemblyConfig.separation.lbl.plus = plus
StageConfig.separation.lbl.seconds = seconds ComponentAssemblyConfig.separation.lbl.seconds = seconds
StageConfig.parallel.radius = Radial Distance: ComponentAssemblyConfig.parallel.radius = Radial Distance:
StageConfig.parallel.angle = Angle: ComponentAssemblyConfig.parallel.angle = Angle:
StageConfig.parallel.count = Number of Copies: ComponentAssemblyConfig.parallel.count = Number of Copies:
StageConfig.parallel.plus = plus ComponentAssemblyConfig.parallel.plus = plus
ComponentAssemblyConfig.but.splitPods = Split Pods
ComponentAssemblyConfig.but.splitPods.ttip = Split the pod set into separate pod components.
ComponentAssemblyConfig.but.splitBoosters = Split Boosters
ComponentAssemblyConfig.but.splitBoosters.ttip = Split the booster into separate booster components.
! FinSetConfig ! FinSetConfig
FinSetCfg.lbl.FinRotation = Fin rotation: FinSetCfg.lbl.FinRotation = Fin rotation:

View File

@ -989,15 +989,15 @@ ComponentCfgDlg.Modify = تعديل
ComponentCfgDlg.ModifyComponents = تعديل المكونات ComponentCfgDlg.ModifyComponents = تعديل المكونات
!StageConfig !StageConfig
StageConfig.tab.Separation = فصل ComponentAssemblyConfig.tab.Separation = فصل
StageConfig.tab.Separation.ttip = خيارات فصل المرحلة ComponentAssemblyConfig.tab.Separation.ttip = خيارات فصل المرحلة
StageConfig.separation.lbl.title = :حدد متى تنفصل هذه المرحلة ComponentAssemblyConfig.separation.lbl.title = :حدد متى تنفصل هذه المرحلة
StageConfig.separation.lbl.plus = زائد ComponentAssemblyConfig.separation.lbl.plus = زائد
StageConfig.separation.lbl.seconds = ثواني ComponentAssemblyConfig.separation.lbl.seconds = ثواني
StageConfig.parallel.radius = :المسافة الشعاعية ComponentAssemblyConfig.parallel.radius = :المسافة الشعاعية
StageConfig.parallel.angle = :الزاوية ComponentAssemblyConfig.parallel.angle = :الزاوية
StageConfig.parallel.count = :عدد النسخ ComponentAssemblyConfig.parallel.count = :عدد النسخ
StageConfig.parallel.plus = زائد ComponentAssemblyConfig.parallel.plus = زائد
!EllipticalFinSetConfig !EllipticalFinSetConfig
EllipticalFinSetCfg.Nbroffins = :عدد الزعانف EllipticalFinSetCfg.Nbroffins = :عدد الزعانف

View File

@ -676,11 +676,11 @@ ComponentCfgDlg.configuration = konfigurace
ComponentCfgDlg.Modify = Uprav ComponentCfgDlg.Modify = Uprav
!StageConfig !StageConfig
StageConfig.tab.Separation = Oddelení ComponentAssemblyConfig.tab.Separation = Oddelení
StageConfig.tab.Separation.ttip = Vlastnosti oddelení stupne ComponentAssemblyConfig.tab.Separation.ttip = Vlastnosti oddelení stupne
StageConfig.separation.lbl.title = Oznac kdy se má tento stupn oddelit: ComponentAssemblyConfig.separation.lbl.title = Oznac kdy se má tento stupn oddelit:
StageConfig.separation.lbl.plus = plus ComponentAssemblyConfig.separation.lbl.plus = plus
StageConfig.separation.lbl.seconds = sekundy ComponentAssemblyConfig.separation.lbl.seconds = sekundy
!EllipticalFinSetConfig !EllipticalFinSetConfig
EllipticalFinSetCfg.Nbroffins = Pocet stabilizátoru: EllipticalFinSetCfg.Nbroffins = Pocet stabilizátoru:

View File

@ -733,11 +733,11 @@ ComponentCfgDlg.configuration = Konfiguration
ComponentCfgDlg.Modify = Verändern ComponentCfgDlg.Modify = Verändern
!StageConfig !StageConfig
StageConfig.tab.Separation = Stufentrennung ComponentAssemblyConfig.tab.Separation = Stufentrennung
StageConfig.tab.Separation.ttip = Stufentrennungs-Optionen ComponentAssemblyConfig.tab.Separation.ttip = Stufentrennungs-Optionen
StageConfig.separation.lbl.title = Auswählen, wenn diese Stufe getrennt wird: ComponentAssemblyConfig.separation.lbl.title = Auswählen, wenn diese Stufe getrennt wird:
StageConfig.separation.lbl.plus = plus ComponentAssemblyConfig.separation.lbl.plus = plus
StageConfig.separation.lbl.seconds = Sekunden ComponentAssemblyConfig.separation.lbl.seconds = Sekunden
!EllipticalFinSetConfig !EllipticalFinSetConfig
EllipticalFinSetCfg.Nbroffins = Anzahl der Leitwerke EllipticalFinSetCfg.Nbroffins = Anzahl der Leitwerke

View File

@ -1051,12 +1051,12 @@ Stage.Stage = Etapa
! StageAction ! StageAction
StageAction.Stage = Etapa StageAction.Stage = Etapa
StageConfig.separation.lbl.plus = m\u00e1s ComponentAssemblyConfig.separation.lbl.plus = m\u00e1s
StageConfig.separation.lbl.seconds = segundos ComponentAssemblyConfig.separation.lbl.seconds = segundos
StageConfig.separation.lbl.title = Seleccione el instante de separaci\u00f3n de esta etapa: ComponentAssemblyConfig.separation.lbl.title = Seleccione el instante de separaci\u00f3n de esta etapa:
!StageConfig !StageConfig
StageConfig.tab.Separation = Separaci\u00f3n ComponentAssemblyConfig.tab.Separation = Separaci\u00f3n
StageConfig.tab.Separation.ttip = Opciones de separaci\u00f3n de etapa ComponentAssemblyConfig.tab.Separation.ttip = Opciones de separaci\u00f3n de etapa
StorageOptChooser.checkbox.Compfile = Archivo comprimido StorageOptChooser.checkbox.Compfile = Archivo comprimido
StorageOptChooser.lbl.Saveopt = Guardar opciones StorageOptChooser.lbl.Saveopt = Guardar opciones

View File

@ -1044,12 +1044,12 @@ Stage.Stage = Etage
! StageAction ! StageAction
StageAction.Stage = Etage StageAction.Stage = Etage
StageConfig.separation.lbl.plus = plus ComponentAssemblyConfig.separation.lbl.plus = plus
StageConfig.separation.lbl.seconds = secondes ComponentAssemblyConfig.separation.lbl.seconds = secondes
StageConfig.separation.lbl.title = Choisir lorsque cet \u00E9tage se s\u00E9pare: ComponentAssemblyConfig.separation.lbl.title = Choisir lorsque cet \u00E9tage se s\u00E9pare:
!StageConfig !StageConfig
StageConfig.tab.Separation = S\u00E9paration ComponentAssemblyConfig.tab.Separation = S\u00E9paration
StageConfig.tab.Separation.ttip = Options de s\u00E9paration de l'\u00E9tage ComponentAssemblyConfig.tab.Separation.ttip = Options de s\u00E9paration de l'\u00E9tage
StorageOptChooser.checkbox.Compfile = Compresse le fichier StorageOptChooser.checkbox.Compfile = Compresse le fichier
StorageOptChooser.lbl.Saveopt = Options de sauvegarde StorageOptChooser.lbl.Saveopt = Options de sauvegarde

View File

@ -734,11 +734,11 @@ ComponentCfgDlg.configuration = (configurazione)
ComponentCfgDlg.Modify = Modifica ComponentCfgDlg.Modify = Modifica
!StageConfig !StageConfig
StageConfig.tab.Separation = Separazione ComponentAssemblyConfig.tab.Separation = Separazione
StageConfig.tab.Separation.ttip = Opzioni della separazione dello stadio ComponentAssemblyConfig.tab.Separation.ttip = Opzioni della separazione dello stadio
StageConfig.separation.lbl.title = Seleziona quando questo stadio separa: ComponentAssemblyConfig.separation.lbl.title = Seleziona quando questo stadio separa:
StageConfig.separation.lbl.plus = pi\u00f9 ComponentAssemblyConfig.separation.lbl.plus = pi\u00f9
StageConfig.separation.lbl.seconds = secondi ComponentAssemblyConfig.separation.lbl.seconds = secondi
!EllipticalFinSetConfig !EllipticalFinSetConfig
EllipticalFinSetCfg.Nbroffins = Numero di pinne: EllipticalFinSetCfg.Nbroffins = Numero di pinne:

View File

@ -764,11 +764,11 @@ ComponentCfgDlg.configuration = \u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30F
ComponentCfgDlg.Modify = \u5909\u66F4 ComponentCfgDlg.Modify = \u5909\u66F4
!StageConfig !StageConfig
StageConfig.tab.Separation = \u5206\u96E2 ComponentAssemblyConfig.tab.Separation = \u5206\u96E2
StageConfig.tab.Separation.ttip = \u30B9\u30C6\u30FC\u30B8\u5206\u96E2\u30AA\u30D7\u30B7\u30E7\u30F3 ComponentAssemblyConfig.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 ComponentAssemblyConfig.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 ComponentAssemblyConfig.separation.lbl.plus = \u30D7\u30E9\u30B9
StageConfig.separation.lbl.seconds = \u79D2 ComponentAssemblyConfig.separation.lbl.seconds = \u79D2
!EllipticalFinSetConfig !EllipticalFinSetConfig
EllipticalFinSetCfg.Nbroffins = \u30D5\u30A3\u30F3\u306E\u6570\uFF1A EllipticalFinSetCfg.Nbroffins = \u30D5\u30A3\u30F3\u306E\u6570\uFF1A

View File

@ -940,14 +940,14 @@ ComponentCfgDlg.configuration = configuratie
ComponentCfgDlg.Modify = Wijzigen ComponentCfgDlg.Modify = Wijzigen
!StageConfig !StageConfig
StageConfig.tab.Separation = Afscheiding ComponentAssemblyConfig.tab.Separation = Afscheiding
StageConfig.tab.Separation.ttip = Etape afscheidingsopties ComponentAssemblyConfig.tab.Separation.ttip = Etape afscheidingsopties
StageConfig.separation.lbl.title = Selecteer wanneer deze etape afscheidt: ComponentAssemblyConfig.separation.lbl.title = Selecteer wanneer deze etape afscheidt:
StageConfig.separation.lbl.plus = plus ComponentAssemblyConfig.separation.lbl.plus = plus
StageConfig.separation.lbl.seconds = seconden ComponentAssemblyConfig.separation.lbl.seconds = seconden
StageConfig.parallel.radius = Radiale Afstand ComponentAssemblyConfig.parallel.radius = Radiale Afstand
StageConfig.parallel.angle = Hoek ComponentAssemblyConfig.parallel.angle = Hoek
StageConfig.parallel.count = Aantal kopieën ComponentAssemblyConfig.parallel.count = Aantal kopieën
StageConfig.parallel.offset = Offset-waarde StageConfig.parallel.offset = Offset-waarde
!EllipticalFinSetConfig !EllipticalFinSetConfig

View File

@ -679,11 +679,11 @@ ComponentInfo.EngineBlock = <b>Blokada silnika</b> unieruchamia silnik wewn\u01
ComponentCfgDlg.Modify = Zmodyfikuj ComponentCfgDlg.Modify = Zmodyfikuj
!StageConfig !StageConfig
StageConfig.tab.Separation = Separacja ComponentAssemblyConfig.tab.Separation = Separacja
StageConfig.tab.Separation.ttip = Opcje oddzielenia cz\u0142onu ComponentAssemblyConfig.tab.Separation.ttip = Opcje oddzielenia cz\u0142onu
StageConfig.separation.lbl.title = Ustal moment oddzielenia cz\u0142onu: ComponentAssemblyConfig.separation.lbl.title = Ustal moment oddzielenia cz\u0142onu:
StageConfig.separation.lbl.plus = plus ComponentAssemblyConfig.separation.lbl.plus = plus
StageConfig.separation.lbl.seconds = sek. ComponentAssemblyConfig.separation.lbl.seconds = sek.
!EllipticalFinSetConfig !EllipticalFinSetConfig
EllipticalFinSetCfg.Nbroffins = Liczba stateczników: EllipticalFinSetCfg.Nbroffins = Liczba stateczników:

View File

@ -1020,12 +1020,12 @@ Stage.Stage = Etapa
# StageAction # StageAction
StageAction.Stage = Est\u00e1gio StageAction.Stage = Est\u00e1gio
StageConfig.separation.lbl.plus = mais ComponentAssemblyConfig.separation.lbl.plus = mais
StageConfig.separation.lbl.seconds = segundos ComponentAssemblyConfig.separation.lbl.seconds = segundos
StageConfig.separation.lbl.title = Selecione quando este est\u00e1gio separa: ComponentAssemblyConfig.separation.lbl.title = Selecione quando este est\u00e1gio separa:
# StageConfig # StageConfig
StageConfig.tab.Separation = Separa\u00e7\u00e3o ComponentAssemblyConfig.tab.Separation = Separa\u00e7\u00e3o
StageConfig.tab.Separation.ttip = Op\u00e7\u00f5es de separa\u00e7\u00e3o de est\u00e1gio ComponentAssemblyConfig.tab.Separation.ttip = Op\u00e7\u00f5es de separa\u00e7\u00e3o de est\u00e1gio
StorageOptChooser.checkbox.Compfile = Compactar arquivos StorageOptChooser.checkbox.Compfile = Compactar arquivos
StorageOptChooser.lbl.Saveopt = Salvar as Op\u00e7\u00f5es StorageOptChooser.lbl.Saveopt = Salvar as Op\u00e7\u00f5es

View File

@ -968,15 +968,15 @@ ComponentCfgDlg.Modify = \u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C
ComponentCfgDlg.ModifyComponents = \u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u044B ComponentCfgDlg.ModifyComponents = \u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u044B
!StageConfig !StageConfig
StageConfig.tab.Separation = \u0420\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u0438\u0435 ComponentAssemblyConfig.tab.Separation = \u0420\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u0438\u0435
StageConfig.tab.Separation.ttip = \u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0440\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u0438\u044F \u0441\u0442\u0443\u043F\u0435\u043D\u0435\u0439 ComponentAssemblyConfig.tab.Separation.ttip = \u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0440\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u0438\u044F \u0441\u0442\u0443\u043F\u0435\u043D\u0435\u0439
StageConfig.separation.lbl.title = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435, \u043A\u043E\u0433\u0434\u0430 \u044D\u0442\u0430 \u0441\u0442\u0443\u043F\u0435\u043D\u044C \u043E\u0442\u0434\u0435\u043B\u044F\u0435\u0442\u0441\u044F: ComponentAssemblyConfig.separation.lbl.title = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435, \u043A\u043E\u0433\u0434\u0430 \u044D\u0442\u0430 \u0441\u0442\u0443\u043F\u0435\u043D\u044C \u043E\u0442\u0434\u0435\u043B\u044F\u0435\u0442\u0441\u044F:
StageConfig.separation.lbl.plus = \u043F\u043B\u044E\u0441 ComponentAssemblyConfig.separation.lbl.plus = \u043F\u043B\u044E\u0441
StageConfig.separation.lbl.seconds = \u0441\u0435\u043A\u0443\u043D\u0434 ComponentAssemblyConfig.separation.lbl.seconds = \u0441\u0435\u043A\u0443\u043D\u0434
StageConfig.parallel.radius = \u0420\u0430\u0434\u0438\u0430\u043B\u044C\u043D\u043E\u0435 \u0440\u0430\u0441\u0441\u0442\u043E\u044F\u043D\u0438\u0435: ComponentAssemblyConfig.parallel.radius = \u0420\u0430\u0434\u0438\u0430\u043B\u044C\u043D\u043E\u0435 \u0440\u0430\u0441\u0441\u0442\u043E\u044F\u043D\u0438\u0435:
StageConfig.parallel.angle = \u0423\u0433\u043E\u043B: ComponentAssemblyConfig.parallel.angle = \u0423\u0433\u043E\u043B:
StageConfig.parallel.count = \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043A\u043E\u043F\u0438\u0439: ComponentAssemblyConfig.parallel.count = \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043A\u043E\u043F\u0438\u0439:
StageConfig.parallel.plus = \u043F\u043B\u044E\u0441 ComponentAssemblyConfig.parallel.plus = \u043F\u043B\u044E\u0441
!EllipticalFinSetConfig !EllipticalFinSetConfig
EllipticalFinSetCfg.Nbroffins = \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0430\u0442\u043E\u0440\u043E\u0432: EllipticalFinSetCfg.Nbroffins = \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0430\u0442\u043E\u0440\u043E\u0432:

View File

@ -838,11 +838,11 @@ ComponentCfgDlg.configuration = configuration
ComponentCfgDlg.Modify = Modify ComponentCfgDlg.Modify = Modify
!StageConfig !StageConfig
StageConfig.tab.Separation = Separation ComponentAssemblyConfig.tab.Separation = Separation
StageConfig.tab.Separation.ttip = Stage separation options ComponentAssemblyConfig.tab.Separation.ttip = Stage separation options
StageConfig.separation.lbl.title = Select when this stage separates: ComponentAssemblyConfig.separation.lbl.title = Select when this stage separates:
StageConfig.separation.lbl.plus = plus ComponentAssemblyConfig.separation.lbl.plus = plus
StageConfig.separation.lbl.seconds = seconds ComponentAssemblyConfig.separation.lbl.seconds = seconds
!EllipticalFinSetConfig !EllipticalFinSetConfig
EllipticalFinSetCfg.Nbroffins = Number of fins: EllipticalFinSetCfg.Nbroffins = Number of fins:

View File

@ -1120,12 +1120,12 @@ Stage.Stage = \u706B\u7BAD\u7EA7
! StageAction ! StageAction
StageAction.Stage = \u7EA7 StageAction.Stage = \u7EA7
StageConfig.separation.lbl.plus = \u52A0 ComponentAssemblyConfig.separation.lbl.plus = \u52A0
StageConfig.separation.lbl.seconds = \u79D2 ComponentAssemblyConfig.separation.lbl.seconds = \u79D2
StageConfig.separation.lbl.title = \u8BBE\u5B9A\u5206\u79BB\u65F6\u673A: ComponentAssemblyConfig.separation.lbl.title = \u8BBE\u5B9A\u5206\u79BB\u65F6\u673A:
!StageConfig !StageConfig
StageConfig.tab.Separation = \u5206\u79BB ComponentAssemblyConfig.tab.Separation = \u5206\u79BB
StageConfig.tab.Separation.ttip = \u591A\u7EA7\u5206\u79BB\u9009\u9879 ComponentAssemblyConfig.tab.Separation.ttip = \u591A\u7EA7\u5206\u79BB\u9009\u9879
StorageOptChooser.lbl.Saveopt = \u4FDD\u5B58\u9009\u9879 StorageOptChooser.lbl.Saveopt = \u4FDD\u5B58\u9009\u9879
! StorageOptionChooser ! StorageOptionChooser

View File

@ -242,7 +242,9 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
public void clearConfigListeners() { public void clearConfigListeners() {
super.clearConfigListeners(); super.clearConfigListeners();
// StageSeparationConfiguration also has config listeners, so clear them as well // StageSeparationConfiguration also has config listeners, so clear them as well
StageSeparationConfiguration thisConfig = getSeparationConfiguration(); if (getRoot() instanceof Rocket) { // Root can be different from the rocket if this stage (or its parent) has been removed from the rocket
thisConfig.clearConfigListeners(); StageSeparationConfiguration thisConfig = getSeparationConfiguration();
thisConfig.clearConfigListeners();
}
} }
} }

View File

@ -1478,61 +1478,18 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
/** /**
* Split the fin set into individual fins. * Split the fin set into individual fins.
* @return A list of the new fin sets. *
* @return A list of the new fin sets.
*/ */
public List<FinSet> splitFins(boolean freezeRocket) { public List<RocketComponent> splitFins(boolean freezeRocket) {
final RocketComponent root = getRoot(); return splitInstances(freezeRocket);
RocketComponent parent = getParent();
int index = parent.getChildPosition(this);
int count = getFinCount();
double base = getBaseRotation();
List<FinSet> splitFins = null; // List of all the split fins
try {
// Freeze rocket
if (freezeRocket && root instanceof Rocket) {
((Rocket) root).freeze();
}
// Split the fins
if (count > 1) {
parent.removeChild(index);
splitFins = new ArrayList<>();
for (int i = 0; i < count; i++) {
FinSet copy = (FinSet) this.copy();
copy.setFinCount(1);
copy.setBaseRotation(base + i * 2 * Math.PI / count);
copy.setName(copy.getName() + " #" + (i + 1));
copy.setOverrideMass(getOverrideMass() / getFinCount());
parent.addChild(copy, index + i);
splitFins.add(copy);
}
}
// Split fins for children
for (RocketComponent listener : configListeners) {
if (listener instanceof FinSet) {
((FinSet) listener).splitFins(false);
this.removeConfigListener(listener);
}
}
} finally {
// Unfreeze rocket
if (freezeRocket && root instanceof Rocket) {
((Rocket) root).thaw();
}
}
return splitFins;
} }
/** /**
* Split the fin set into individual fins. * Split the fin set into individual fins.
* @return A list of the new fin sets. * @return A list of the new fin sets.
*/ */
public List<FinSet> splitFins() { public List<RocketComponent> splitFins() {
return splitFins(true); return splitFins(true);
} }

View File

@ -5,7 +5,7 @@ import net.sf.openrocket.util.Coordinate;
public interface Instanceable { public interface Instanceable {
@Deprecated @Deprecated
public Coordinate[] getLocations(); Coordinate[] getLocations();
/** /**
* Returns vector coordinates of each instance of this component relative to this component's parent * Returns vector coordinates of each instance of this component relative to this component's parent
@ -16,7 +16,7 @@ public interface Instanceable {
* *
* @return coordinates location of each instance relative to component's parent * @return coordinates location of each instance relative to component's parent
*/ */
public Coordinate[] getInstanceLocations(); Coordinate[] getInstanceLocations();
/** /**
* Returns vector coordinates of each instance of this component relative to this component's reference point (typically front center) * Returns vector coordinates of each instance of this component relative to this component's reference point (typically front center)
@ -27,20 +27,20 @@ public interface Instanceable {
* *
* @return coordinates location of each instance relative to <b>this</b> component's reference point. * @return coordinates location of each instance relative to <b>this</b> component's reference point.
*/ */
public Coordinate[] getInstanceOffsets(); Coordinate[] getInstanceOffsets();
/** /**
* How many instances of this component are represented. This should generally be editable. * How many instances of this component are represented. This should generally be editable.
* @param newCount number of instances to set * @param newCount number of instances to set
*/ */
public void setInstanceCount( final int newCount ); void setInstanceCount( final int newCount );
/** /**
* How many instances of this component are represented. This should generally be editable. * How many instances of this component are represented. This should generally be editable.
* *
* @return number of instances this component currently represent. * @return number of instances this component currently represent.
*/ */
public int getInstanceCount(); int getInstanceCount();
/** /**
* Get a human-readable name for this instance arrangement. * Get a human-readable name for this instance arrangement.
@ -48,6 +48,6 @@ public interface Instanceable {
* *
* @return pattern name * @return pattern name
*/ */
public String getPatternName(); String getPatternName();
} }

View File

@ -412,6 +412,8 @@ public class Rocket extends ComponentAssembly {
* changes. * changes.
*/ */
public void loadFrom(Rocket source) { public void loadFrom(Rocket source) {
checkState();
mutex.lock("loadFrom");
// Store list of components to invalidate after event has been fired // Store list of components to invalidate after event has been fired
List<RocketComponent> toInvalidate = this.copyFrom(source); List<RocketComponent> toInvalidate = this.copyFrom(source);
@ -453,6 +455,8 @@ public class Rocket extends ComponentAssembly {
for (RocketComponent c : toInvalidate) { for (RocketComponent c : toInvalidate) {
c.invalidate(); c.invalidate();
} }
mutex.unlock("loadFrom");
} }

View File

@ -15,6 +15,7 @@ import net.sf.openrocket.aerodynamics.AerodynamicForces;
import net.sf.openrocket.aerodynamics.BarrowmanCalculator; import net.sf.openrocket.aerodynamics.BarrowmanCalculator;
import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.aerodynamics.FlightConditions;
import net.sf.openrocket.logging.WarningSet; import net.sf.openrocket.logging.WarningSet;
import net.sf.openrocket.rocketcomponent.position.AnglePositionable;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.util.ORColor; import net.sf.openrocket.util.ORColor;
@ -1089,6 +1090,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
return 1; return 1;
} }
public void setInstanceCount(int count) {
// Do nothing
log.warn("setInstanceCount called on component that does not support multiple instances");
}
/** /**
* Get the user-defined name of the component. * Get the user-defined name of the component.
*/ */
@ -2382,12 +2388,16 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
*/ */
public final RocketComponent findComponent(String idToFind) { public final RocketComponent findComponent(String idToFind) {
checkState(); checkState();
mutex.lock("findComponent");
Iterator<RocketComponent> iter = this.iterator(true); Iterator<RocketComponent> iter = this.iterator(true);
while (iter.hasNext()) { while (iter.hasNext()) {
final RocketComponent c = iter.next(); final RocketComponent c = iter.next();
if (c.getID().equals(idToFind)) if (c.getID().equals(idToFind)) {
mutex.unlock("findComponent");
return c; return c;
}
} }
mutex.unlock("findComponent");
return null; return null;
} }
@ -2440,6 +2450,67 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
return c; return c;
} }
/**
* Split the current multi-instance component into multiple single-instance components.
* @param freezeRocket whether to freeze the rocket while splitting
* @return list of all the split components
*/
public List<RocketComponent> splitInstances(boolean freezeRocket) {
final Rocket rocket = getRocket();
RocketComponent parent = getParent();
int index = parent.getChildPosition(this);
int count = getInstanceCount();
double angleOffset = getAngleOffset();
List<RocketComponent> splitComponents = null; // List of all the split components
try {
// Freeze rocket
if (freezeRocket) {
rocket.freeze();
}
// Split the components
if (count > 1) {
parent.removeChild(index, true); // Remove the original component
splitComponents = new java.util.ArrayList<>();
for (int i = 0; i < count; i++) {
RocketComponent copy = this.copy();
copy.setInstanceCount(1);
if (copy instanceof AnglePositionable) {
((AnglePositionable) copy).setAngleOffset(angleOffset + i * 2 * Math.PI / count);
}
copy.setName(copy.getName() + " #" + (i + 1));
copy.setOverrideMass(getOverrideMass() / count);
parent.addChild(copy, index + i, true); // Add the new component
splitComponents.add(copy);
}
}
// Split components for listeners
for (RocketComponent listener : configListeners) {
if (listener.getClass().isAssignableFrom(this.getClass())) {
listener.splitInstances(false);
this.removeConfigListener(listener);
}
}
} finally {
// Unfreeze rocket
if (freezeRocket) {
rocket.thaw();
}
}
fireComponentChangeEvent(ComponentChangeEvent.TREE_CHANGE);
return splitComponents;
}
public List<RocketComponent> splitInstances() {
return splitInstances(true);
}
/////////// Event handling ////////// /////////// Event handling //////////
// //
// Listener lists are provided by the root Rocket component, // Listener lists are provided by the root Rocket component,

View File

@ -1000,6 +1000,9 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
log.warn("Invalidating " + this + " while still having listeners " + listeners); log.warn("Invalidating " + this + " while still having listeners " + listeners);
} }
listeners.clear(); listeners.clear();
if (source instanceof ChangeSource) {
((ChangeSource) source).removeChangeListener(this);
}
MemoryManagement.collectable(this); MemoryManagement.collectable(this);
} }

View File

@ -31,8 +31,8 @@ public class AxialStageConfig extends ComponentAssemblyConfig {
// Stage separation config (for non-first stage) // Stage separation config (for non-first stage)
if (component.getStageNumber() > 0) { if (component.getStageNumber() > 0) {
JPanel tab = separationTab((AxialStage) component); JPanel tab = separationTab((AxialStage) component);
tabbedPane.insertTab(trans.get("StageConfig.tab.Separation"), null, tab, tabbedPane.insertTab(trans.get("ComponentAssemblyConfig.tab.Separation"), null, tab,
trans.get("StageConfig.tab.Separation.ttip"), 0); trans.get("ComponentAssemblyConfig.tab.Separation.ttip"), 0);
tabbedPane.setSelectedIndex(0); tabbedPane.setSelectedIndex(0);
} }
@ -49,7 +49,7 @@ public class AxialStageConfig extends ComponentAssemblyConfig {
JPanel panel = new JPanel(new MigLayout()); JPanel panel = new JPanel(new MigLayout());
// Select separation event // Select separation event
panel.add(new StyledLabel(trans.get("StageConfig.separation.lbl.title") + " " + CommonStrings.dagger, Style.BOLD), panel.add(new StyledLabel(trans.get("ComponentAssemblyConfig.separation.lbl.title") + " " + CommonStrings.dagger, Style.BOLD),
"spanx, gaptop unrel, wrap 30lp"); "spanx, gaptop unrel, wrap 30lp");
StageSeparationConfiguration sepConfig = stage.getSeparationConfiguration(); StageSeparationConfiguration sepConfig = stage.getSeparationConfiguration();
@ -61,7 +61,7 @@ public class AxialStageConfig extends ComponentAssemblyConfig {
order.add(combo); order.add(combo);
// ... and delay // ... and delay
panel.add(new JLabel(trans.get("StageConfig.separation.lbl.plus"))); panel.add(new JLabel(trans.get("ComponentAssemblyConfig.separation.lbl.plus")));
DoubleModel dm = new DoubleModel( sepConfig, "SeparationDelay", 0); DoubleModel dm = new DoubleModel( sepConfig, "SeparationDelay", 0);
JSpinner spin = new JSpinner(dm.getSpinnerModel()); JSpinner spin = new JSpinner(dm.getSpinnerModel());
@ -70,7 +70,7 @@ public class AxialStageConfig extends ComponentAssemblyConfig {
order.add(((SpinnerEditor)spin.getEditor()).getTextField()); order.add(((SpinnerEditor)spin.getEditor()).getTextField());
//// seconds //// seconds
panel.add(new JLabel(trans.get("StageConfig.separation.lbl.seconds")), "wrap unrel"); panel.add(new JLabel(trans.get("ComponentAssemblyConfig.separation.lbl.seconds")), "wrap unrel");
panel.add(new StyledLabel(CommonStrings.override_description, -1), "spanx, pushy, wrap para"); panel.add(new StyledLabel(CommonStrings.override_description, -1), "spanx, pushy, wrap para");

View File

@ -1,11 +1,13 @@
package net.sf.openrocket.gui.configdialog; package net.sf.openrocket.gui.configdialog;
import javax.swing.ComboBoxModel; import javax.swing.ComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JSpinner; import javax.swing.JSpinner;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.OpenRocketDocument;
@ -15,28 +17,36 @@ import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.adaptors.IntegerModel; import net.sf.openrocket.gui.adaptors.IntegerModel;
import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.widgets.SelectColorButton;
import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.logging.Markers;
import net.sf.openrocket.rocketcomponent.ComponentAssembly; import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.ParallelStage; import net.sf.openrocket.rocketcomponent.ParallelStage;
import net.sf.openrocket.rocketcomponent.PodSet; import net.sf.openrocket.rocketcomponent.PodSet;
import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.rocketcomponent.position.RadiusMethod; import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.unit.UnitGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.ItemEvent; import java.awt.event.ItemEvent;
import java.awt.event.ItemListener; import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.EventObject; import java.util.EventObject;
import java.util.List;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class ComponentAssemblyConfig extends RocketComponentConfig { public class ComponentAssemblyConfig extends RocketComponentConfig {
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
private static final Logger log = LoggerFactory.getLogger(ComponentAssemblyConfig.class);
private final RocketComponent component; private final RocketComponent component;
private JButton split = null;
public ComponentAssemblyConfig(OpenRocketDocument document, RocketComponent component, JDialog parent) { public ComponentAssemblyConfig(OpenRocketDocument document, RocketComponent component, JDialog parent) {
super(document, component, parent); super(document, component, parent);
this.component = component; this.component = component;
@ -46,6 +56,8 @@ public class ComponentAssemblyConfig extends RocketComponentConfig {
tabbedPane.insertTab( trans.get("RocketCompCfg.tab.Assembly"), null, parallelTab( (ComponentAssembly)component ), tabbedPane.insertTab( trans.get("RocketCompCfg.tab.Assembly"), null, parallelTab( (ComponentAssembly)component ),
trans.get("RocketCompCfg.tab.AssemblyComment"), 0); trans.get("RocketCompCfg.tab.AssemblyComment"), 0);
tabbedPane.setSelectedIndex(0); tabbedPane.setSelectedIndex(0);
addSplitButton();
} }
} }
@ -62,7 +74,7 @@ public class ComponentAssemblyConfig extends RocketComponentConfig {
order.add(radiusMethodCombo); order.add(radiusMethodCombo);
// set radial distance // set radial distance
JLabel radiusLabel = new JLabel(trans.get("StageConfig.parallel.radius")); JLabel radiusLabel = new JLabel(trans.get("ComponentAssemblyConfig.parallel.radius"));
motherPanel.add( radiusLabel , "align left"); motherPanel.add( radiusLabel , "align left");
//radiusMethodModel.addEnableComponent(radiusLabel, false); //radiusMethodModel.addEnableComponent(radiusLabel, false);
DoubleModel radiusModel = new DoubleModel( boosters, "RadiusOffset", UnitGroup.UNITS_LENGTH, 0); DoubleModel radiusModel = new DoubleModel( boosters, "RadiusOffset", UnitGroup.UNITS_LENGTH, 0);
@ -85,7 +97,7 @@ public class ComponentAssemblyConfig extends RocketComponentConfig {
}); });
// set angle // set angle
JLabel angleLabel = new JLabel(trans.get("StageConfig.parallel.angle")); JLabel angleLabel = new JLabel(trans.get("ComponentAssemblyConfig.parallel.angle"));
motherPanel.add( angleLabel, "align left"); motherPanel.add( angleLabel, "align left");
DoubleModel angleModel = new DoubleModel( boosters, "AngleOffset", 1.0, UnitGroup.UNITS_ANGLE, -Math.PI, Math.PI); DoubleModel angleModel = new DoubleModel( boosters, "AngleOffset", 1.0, UnitGroup.UNITS_ANGLE, -Math.PI, Math.PI);
@ -98,7 +110,7 @@ public class ComponentAssemblyConfig extends RocketComponentConfig {
motherPanel.add(new BasicSlider(angleModel.getSliderModel(-Math.PI, Math.PI)), "gapleft para, growx 2, wrap"); motherPanel.add(new BasicSlider(angleModel.getSliderModel(-Math.PI, Math.PI)), "gapleft para, growx 2, wrap");
// set multiplicity // set multiplicity
JLabel countLabel = new JLabel(trans.get("StageConfig.parallel.count")); JLabel countLabel = new JLabel(trans.get("ComponentAssemblyConfig.parallel.count"));
motherPanel.add( countLabel, "align left"); motherPanel.add( countLabel, "align left");
IntegerModel countModel = new IntegerModel( boosters, "InstanceCount", 1); IntegerModel countModel = new IntegerModel( boosters, "InstanceCount", 1);
@ -112,4 +124,64 @@ public class ComponentAssemblyConfig extends RocketComponentConfig {
return motherPanel; return motherPanel;
} }
@Override
public void updateFields() {
super.updateFields();
if (split != null) {
split.setEnabled(component.getInstanceCount() > 1);
}
}
private void addSplitButton() {
//// Split fins
final String btnText;
final String btnTextTtip;
final boolean freezeRocket;
if (PodSet.class.isAssignableFrom(component.getClass())) {
btnText = trans.get("ComponentAssemblyConfig.but.splitPods");
btnTextTtip = trans.get("ComponentAssemblyConfig.but.splitPods.ttip");
freezeRocket = true;
} else if (ParallelStage.class.isAssignableFrom(component.getClass())) {
btnText = trans.get("ComponentAssemblyConfig.but.splitBoosters");
btnTextTtip = trans.get("ComponentAssemblyConfig.but.splitBoosters.ttip");
freezeRocket = false;
} else {
return;
}
split = new SelectColorButton(btnText);
split.setToolTipText(btnTextTtip);
split.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
log.info(Markers.USER_MARKER, "Splitting " + component.getComponentName() + " into separate assemblies, instance count=" +
component.getInstanceCount());
// This is a bit awkward, we need to store the listeners before closing the dialog, because closing it
// will remove them. We then add them back before the split and remove them afterwards.
List<RocketComponent> listeners = new ArrayList<>(component.getConfigListeners());
// Do change in future for overall safety
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
disposeDialog();
document.startUndo("Split assembly");
for (RocketComponent listener : listeners) {
component.addConfigListener(listener);
}
component.splitInstances(freezeRocket);
component.clearConfigListeners();
document.stopUndo();
}
});
}
});
split.setEnabled(component.getInstanceCount() > 1);
addButtons(split);
order.add(split);
}
} }

View File

@ -339,6 +339,7 @@ public class RocketComponentConfig extends JPanel {
} }
protected void disposeDialog() { protected void disposeDialog() {
invalidate();
if (parent != null) { if (parent != null) {
if (parent instanceof ComponentConfigDialog) { if (parent instanceof ComponentConfigDialog) {
ComponentConfigDialog.disposeDialog(); ComponentConfigDialog.disposeDialog();
@ -700,6 +701,7 @@ public class RocketComponentConfig extends JPanel {
panel.add(checkboxes, "growx 1, gapright 20lp"); panel.add(checkboxes, "growx 1, gapright 20lp");
m = new DoubleModel(component, "OverrideCD", UnitGroup.UNITS_COEFFICIENT, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); m = new DoubleModel(component, "OverrideCD", UnitGroup.UNITS_COEFFICIENT, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
register(m);
spin = new JSpinner(m.getSpinnerModel()); spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin)); spin.setEditor(new SpinnerEditor(spin));

View File

@ -82,7 +82,7 @@ public class SeparationSelectionDialog extends JDialog {
panel.add(event, "wrap rel"); panel.add(event, "wrap rel");
// ... and delay // ... and delay
panel.add(new JLabel(trans.get("StageConfig.separation.lbl.plus")), "alignx 100%"); panel.add(new JLabel(trans.get("ComponentAssemblyConfig.separation.lbl.plus")), "alignx 100%");
final DoubleModel delay = new DoubleModel(newConfiguration, "SeparationDelay", UnitGroup.UNITS_SHORT_TIME, 0); final DoubleModel delay = new DoubleModel(newConfiguration, "SeparationDelay", UnitGroup.UNITS_SHORT_TIME, 0);
JSpinner spin = new JSpinner(delay.getSpinnerModel()); JSpinner spin = new JSpinner(delay.getSpinnerModel());
@ -90,7 +90,7 @@ public class SeparationSelectionDialog extends JDialog {
panel.add(spin, "span, split"); panel.add(spin, "span, split");
//// seconds //// seconds
panel.add(new JLabel(trans.get("StageConfig.separation.lbl.seconds")), "wrap para"); panel.add(new JLabel(trans.get("ComponentAssemblyConfig.separation.lbl.seconds")), "wrap para");
panel.add(new JPanel(), "span, split, growx"); panel.add(new JPanel(), "span, split, growx");