From 47d4f7c0b12bcecb13cfe44e417db5dc60d7a054 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Mon, 7 Nov 2022 00:06:58 +0100 Subject: [PATCH 01/16] [#1791] Return component mass as section mass if subcomponents mass is overridden --- .../src/net/sf/openrocket/rocketcomponent/RocketComponent.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 11f2fbb1a..5235a92cd 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -1534,6 +1534,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab */ public final double getSectionMass() { Double massSubtotal = getMass(); + if (massOverridden && overrideSubcomponentsMass) { + return massSubtotal; + } mutex.verify(); for (RocketComponent rc : children) { massSubtotal += rc.getSectionMass(); From 8cbe1e791c5adc7b23c621230dfdb59320c2596c Mon Sep 17 00:00:00 2001 From: SiboVG Date: Mon, 7 Nov 2022 10:34:09 +0100 Subject: [PATCH 02/16] Add unit test for getMass vs getSectionMass --- .../masscalc/MassCalculatorTest.java | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java index 6c89ed6eb..977feb3c6 100644 --- a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java +++ b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java @@ -4,10 +4,12 @@ import static org.junit.Assert.assertEquals; import java.util.List; +import net.sf.openrocket.document.OpenRocketDocumentFactory; import net.sf.openrocket.rocketcomponent.*; import net.sf.openrocket.rocketcomponent.position.AngleMethod; import net.sf.openrocket.rocketcomponent.position.AxialMethod; import net.sf.openrocket.rocketcomponent.position.RadiusMethod; +import net.sf.openrocket.util.MathUtil; import org.junit.Test; import net.sf.openrocket.motor.Motor; @@ -377,10 +379,12 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals(0.0, sustainerBody.getPosition().x, EPSILON); assertEquals(0.1, sustainerBody.getLength(), EPSILON); assertEquals(expSingleBodyMass, sustainerBody.getMass(), EPSILON); + assertEquals(expSingleBodyMass, sustainerBody.getSectionMass(), EPSILON); assertEquals(0.10, boosterBody.getComponentLocations()[0].x, EPSILON); assertEquals(0.10, boosterBody.getLength(), EPSILON); assertEquals(expSingleBodyMass, boosterBody.getMass(), EPSILON); + assertEquals(expSingleBodyMass, boosterBody.getSectionMass(), EPSILON); } { // [1] test Rocket CM, before: @@ -397,13 +401,16 @@ public class MassCalculatorTest extends BaseTestCase { boosterBody.setSubcomponentsOverriddenMass(false); boosterBody.setMassOverridden(true); - boosterBody.setOverrideMass(0.001); + double newMass = 0.001; + boosterBody.setOverrideMass(newMass); { // [1] test Rocket CM, after: final RigidBody actualStructure = MassCalculator.calculateStructure(config); final double actualRocketDryMass = actualStructure.cm.weight; - assertEquals(expSingleBodyMass+0.001, actualRocketDryMass, EPSILON); + assertEquals(expSingleBodyMass+newMass, actualRocketDryMass, EPSILON); + assertEquals(newMass, boosterBody.getMass(), EPSILON); + assertEquals(newMass, boosterBody.getSectionMass(), EPSILON); final Coordinate actualRocketDryCM = actualStructure.cm; assertEquals(0.06976699, actualRocketDryCM.x, EPSILON); @@ -411,13 +418,15 @@ public class MassCalculatorTest extends BaseTestCase { boosterBody.setSubcomponentsOverriddenMass(true); // change. Also, this body lacks subcomponents. boosterBody.setMassOverridden(true); // repeat - boosterBody.setOverrideMass(0.001); // repeat + boosterBody.setOverrideMass(newMass); // repeat { // [1] test Rocket CM, after: final RigidBody actualStructure = MassCalculator.calculateStructure(config); final double actualRocketDryMass = actualStructure.cm.weight; - assertEquals(expSingleBodyMass+0.001, actualRocketDryMass, EPSILON); + assertEquals(expSingleBodyMass+newMass, actualRocketDryMass, EPSILON); + assertEquals(newMass, boosterBody.getMass(), EPSILON); + assertEquals(newMass, boosterBody.getSectionMass(), EPSILON); final Coordinate actualRocketDryCM = actualStructure.cm; assertEquals(0.06976699, actualRocketDryCM.x, EPSILON); @@ -1232,5 +1241,31 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals("Empty Stages Rocket Rotational MOI calculated incorrectly: ", overrideMOIrotRef, overrideMOIrot, EPSILON); assertEquals("Empty Stages Rocket Longitudinal MOI calculated incorrectly: ", overrideMOIlongRef, overrideMOIlong, EPSILON); } + + @Test + public void testStructureMass() { + Rocket rocket = OpenRocketDocumentFactory.createNewRocket().getRocket(); + AxialStage stage = rocket.getStage(0); + stage.addChild(new NoseCone()); + BodyTube bodyTube = new BodyTube(); + stage.addChild(bodyTube); + MassComponent massComponent = new MassComponent(); + massComponent.setComponentMass(0.01); + bodyTube.addChild(massComponent); + + assertEquals(0.041016634, bodyTube.getMass(), EPSILON); + assertEquals(0.051016634, bodyTube.getSectionMass(), EPSILON); + + bodyTube.setMassOverridden(true); + bodyTube.setOverrideMass(0.02); + + assertEquals(0.02, bodyTube.getMass(), EPSILON); + assertEquals(0.03, bodyTube.getSectionMass(), EPSILON); + + bodyTube.setSubcomponentsOverriddenMass(true); + + assertEquals(0.02, bodyTube.getMass(), EPSILON); + assertEquals(0.02, bodyTube.getSectionMass(), EPSILON); + } } From ae47195f66fef43e9a6155286315d3fdca4549b4 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 8 Nov 2022 11:50:37 +0100 Subject: [PATCH 03/16] Save state of which component overrides mass/CG/CD of the current component --- .../rocketcomponent/RocketComponent.java | 88 +++++++++++++++++-- 1 file changed, 81 insertions(+), 7 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 5235a92cd..0c3e2364b 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -96,18 +96,23 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab private LineStyle lineStyle = null; - // Override mass/CG/CD + // Override mass protected double overrideMass = 0; protected boolean massOverridden = false; private boolean overrideSubcomponentsMass = false; - + private RocketComponent massOverriddenBy = null; // The (super-)parent component that overrides the mass of this component + + // Override CG private double overrideCGX = 0; private boolean cgOverridden = false; private boolean overrideSubcomponentsCG = false; - + private RocketComponent CGOverriddenBy = null; // The (super-)parent component that overrides the CG of this component + + // Override CD private double overrideCD = 0; private boolean cdOverridden = false; private boolean overrideSubcomponentsCD = false; + private RocketComponent CDOverriddenBy = null; // The (super-)parent component that overrides the CD of this component private boolean cdOverriddenByAncestor = false; @@ -606,6 +611,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } checkState(); massOverridden = o; + updateChildrenMassOverriddenBy(); fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } @@ -680,6 +686,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } checkState(); cgOverridden = o; + updateChildrenCGOverriddenBy(); fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } @@ -746,6 +753,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } checkState(); cdOverridden = o; + updateChildrenCDOverriddenBy(); // if overrideSubcompoents is set, we need to descend the component // tree. If we are overriding our own CD, we need to override all @@ -784,7 +792,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return overrideSubcomponentsMass; } - // TODO: delete no compatibility is needed anymore with OR 15.03 + // TODO: delete when compatibility with OR 15.03 is not needed anymore public void setSubcomponentsOverridden(boolean override) { setSubcomponentsOverriddenMass(override); setSubcomponentsOverriddenCG(override); @@ -809,6 +817,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); overrideSubcomponentsMass = override; + updateChildrenMassOverriddenBy(); + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } @@ -845,6 +855,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); overrideSubcomponentsCG = override; + updateChildrenCGOverriddenBy(); + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } @@ -881,6 +893,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); overrideSubcomponentsCD = override; + updateChildrenCDOverriddenBy(); + overrideSubcomponentsCD(override); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); @@ -931,8 +945,50 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab mutex.verify(); return isCGOverridden() || isMassOverridden() || isCDOverridden(); } - - /** + + /** + * Returns which (super-)parent overrides the mass of this component, or null if no parent does so. + */ + public RocketComponent getMassOverriddenBy() { + return massOverriddenBy; + } + + /** + * Returns which (super-)parent overrides the CG of this component, or null if no parent does so. + */ + public RocketComponent getCGOverriddenBy() { + return CGOverriddenBy; + } + + /** + * Returns which (super-)parent overrides the CD of this component, or null if no parent does so. + */ + public RocketComponent getCDOverriddenBy() { + return CDOverriddenBy; + } + + private void updateChildrenMassOverriddenBy() { + RocketComponent overriddenBy = massOverridden && overrideSubcomponentsMass ? this : null; + for (RocketComponent c : getAllChildren()) { + c.massOverriddenBy = overriddenBy; + } + } + + private void updateChildrenCGOverriddenBy() { + RocketComponent overriddenBy = cgOverridden && overrideSubcomponentsCG ? this : null; + for (RocketComponent c : getAllChildren()) { + c.CGOverriddenBy = overriddenBy; + } + } + + private void updateChildrenCDOverriddenBy() { + RocketComponent overriddenBy = cdOverridden && overrideSubcomponentsCD ? this : null; + for (RocketComponent c : getAllChildren()) { + c.CDOverriddenBy = overriddenBy; + } + } + + /** * placeholder. This allows code to generally test if this component represents multiple instances with just one function call. * * @return number of instances @@ -1642,6 +1698,21 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab children.add(index, component); component.parent = this; + if (this.massOverridden && this.overrideSubcomponentsMass) { + component.massOverriddenBy = this; + } else { + component.massOverriddenBy = this.massOverriddenBy; + } + if (this.cgOverridden && this.overrideSubcomponentsCG) { + component.CGOverriddenBy = this; + } else { + component.CGOverriddenBy = this.CGOverriddenBy; + } + if (this.cdOverridden && this.overrideSubcomponentsCD) { + component.CDOverriddenBy = this; + } else { + component.CDOverriddenBy = this.CDOverriddenBy; + } if (component instanceof AxialStage) { AxialStage nStage = (AxialStage) component; @@ -1663,7 +1734,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab */ public final void removeChild(int n) { checkState(); - RocketComponent component = this.getChild(n); + RocketComponent component = this.getChild(n); this.removeChild(component); } @@ -1682,6 +1753,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab if (children.remove(component)) { component.parent = null; + component.massOverriddenBy = null; + component.CGOverriddenBy = null; + component.CDOverriddenBy = null; if (component instanceof AxialStage) { AxialStage stage = (AxialStage) component; From 8004743b6403e229cbd22a770be45df5fabf5c2c Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 8 Nov 2022 11:51:05 +0100 Subject: [PATCH 04/16] Add icons for mass/CG/CD subcomponent override --- .../pix/icons/cd-override-subcomponent.png | Bin 0 -> 2315 bytes .../pix/icons/cg-override-subcomponent.png | Bin 0 -> 1837 bytes .../pix/icons/mass-override-subcomponent.png | Bin 0 -> 2137 bytes swing/src/net/sf/openrocket/gui/util/Icons.java | 3 +++ 4 files changed, 3 insertions(+) create mode 100644 core/resources/pix/icons/cd-override-subcomponent.png create mode 100644 core/resources/pix/icons/cg-override-subcomponent.png create mode 100755 core/resources/pix/icons/mass-override-subcomponent.png diff --git a/core/resources/pix/icons/cd-override-subcomponent.png b/core/resources/pix/icons/cd-override-subcomponent.png new file mode 100644 index 0000000000000000000000000000000000000000..228cb95f3b795ae933a4232004f9be8dbd8e236f GIT binary patch literal 2315 zcmbVN4Nw$i7(OSFB7w6pDUw{*CQ01eAMS5APC|~KOOGEpO$;6OcK42jyWMMd;SS7d zP!lnKBg+zN8YeQ-u{8ftRQi?FjPVB>&`8UPIvED3Q0zN6Zm7^4cV>6L{l4dW-{*ba z_j`AFPF6-_L~MjgrHZs`zgYo}IK8;i$Q=S*yjuqaqLfBKo$lCPpQ zabagG7bmZn9UkMHA6F2TaxJ{^yRR+Zr9C_Tp-grAhNz!6$@_ZLzwd6}yvP59rNuw) zxvtzT1)wm)p<*JI>JmGDtyi^f+tt3=aG@uAYO|pNPg&CSLfwVQdzbIr z^H=S(Ba7JfZ;NX$l3B`(%MnL1*pf+ok>?#-3MMXbdFy>^u@xVZpO@q%wRSwY#JOhc zWWIKX_Q!7Ol3ubbRIm1r#tyGtaG-kp`fd9=sw*?UeP+hoY@cne`R0vZ{KCSO-`AzTbET`}7BRlh_{9(Nua~`gtZntOWA&5SXlvTXb6ipNfxZZ`a- zFA;s$*0fv4iA{g>HYnn)?$>&I@9WK!yiX@wP6DZUz>}FTgIw(h+#{~Ow6R{L3hUr) zbCtQ)nG_?sH8d;QIgQWl0ce#fDcR?tnG#Nc?3|MqOtAmJVHo0B6a1Xcidj9WToIpH zA#r&XSvIDkgdtftc^Z`DqksW7r_hkk?Gj|lXM+3fQeYgIMq#KQqLi56lz>BME+~^! zQQ{zj21ghS!yp5xv11OSog=ZS5P=amiW4ZVLkOHAbQG?Gf)@<@NvwmKWlj%z179Y% zNKrf#ih8|XjW7fTfr z47d&i;r4{g3UW{=AQ|eTJt(fh0zvfyStf+@luE9C=PZMAF3!yfiVU!L2iBkh%nnpfGFz zY86@DQ9cBU;|ON7;W&k9sYGJ}1_pyrP&O>B(Ekf&8Ok9_ZW@HlyJ;tfdITp7g@mM1 zMVBZ6$ADep(0L2!R+8x8U0~tAU8|q3)k;|eS)m1nvzSdV&_~1bEJbP)2}VzH2*Z#B zqGebDA@!sIvD*oQfzVk%T6;}ESL=ta{kh8T5h zBB^CC(m~^c`?Dn;oI~1m&vpW*Gf+NMCNG1;mIqHy9`{0U?JilJyGa1u-~6DR?W zg@e=lFbdpV1OsNhMI11Chr$?u$)ZE?(h`^A1Zv$y>7loyFVkgrCVzM7`w#iyOZDE! z2c>0-Xu-*W=8D3BaL_;#8b}QK@0|6o4QW_|aL@$vBj@ldTL@-P43vwJKI(Rk2l zt+69Nc}u`zmm>Rrc{TFDeN@%BF*D~)&3gGK z-6?U8u1tr&(H(4fbYnxTe!uYBudS`uPbB%&xvPGR`oQ>Ze#YMB8@(&mg&WVF-sCuw zy(YiylH{2G=Y$tO?fyQ0(#}KkM;=wTJ%2c<#9Y(9p?ijT->T9Vk7ai_&n$a+UGnap zn5acJro2CMUPFGz?yEvt&6hixd+MvCXhY}6*3*~ZO@-mBPjP1^jJZ7O@xszm*AJ|$ zPB>S5v8$<~XTsJ)LQAAGO8bTJ{f$SpJNw?2>lkZtboA33N{R4!tKt*RHTE`DT#GsQ gUaR)zEBK93H+L0eeRM?qZs6BwNy{>CpI%t`H&EtM%m4rY literal 0 HcmV?d00001 diff --git a/core/resources/pix/icons/cg-override-subcomponent.png b/core/resources/pix/icons/cg-override-subcomponent.png new file mode 100644 index 0000000000000000000000000000000000000000..1107ec954d81afd9352ef15ebea3219856d7e411 GIT binary patch literal 1837 zcmbVNeQ4Zd7|)PNTdlP?#uRZe6}sti$(#F3*sZorYp?aXdOft8SbQ&e?=HK`$C9*n zS6Y=a=VVZI;K_+!J+koVJm3soBN{EZk7d-yh(n~ z@A*9+?~^Bcy6>s2xuu36h}uwx7;*0SyuRvMe0}FF|CjhyZFKCj2;#b%oO2T~F}aN( zHqR;1KD#e`uOR8F3(9&7xw5K((FC!(DQiF}g={j05{ee2emU_zMJjTTdcYfI!$vDg zDjh>6>K*EiN<%4$m#L;bx#4id$GTcnwc^6E+XitMrG%`DwxT%$F%e|x1Ez%92C>WrCmKU&H4(IRGpg(9 za;bXCl1a|(;>aCgC@Go~#LhJ)NQ5>DQn+p$%>vpDqJS5?9>K%i#R7q4i_owxEAiYK zC;&7YhyskjP|qb$+%___;s1hVNr>yF3USDa3KNJiv;;*~5-GImY2Cz*u{w7_MK~;k zG|PsXghFDF!cn^vMHYB(JnmyTiDo5UqCH-SXdZffbifzm;xYoj7xR?uMO_+jYFoCK zv3)?6{49`pn&nuQ_ITVZ4c&g32A()Vz8H%81F^FGUQ@xl3#PA{$JujEQwSZ3h0B#I z?TKD=u(V1mWT6iPC^`Kdq$H=XNTy1!m31<(u?UK^nIyubS15N8X6bP|15MPNz}ebB zLot_=Z^6MUi{DuJ{v&?<2E01)rPNAEs3i~{nGEHG!#Ja*SQzHtH7k5uGlt4=JlmXQ zWvt?d%3wwsrZ(}|ouU5_31U-yNNkSo7#}$Uy3XCV<i4+%#e^w)2xnLgfi z_oqqa#n-ogt5LPHsix*zSGqo2+|+N;4&Bxdcfz{VQPKNqWCEW4bJxQB=*n-iZZTYV!;rT;HhYr-|7GJ*kk;cDT5?`%0Tm&84UfMqX&FSSA&`&EDr=p$i z59(eM^Ly)a@A=MmSI?Z8|12``@kzJ->*f=Iv5%fRJJ&S1kQ(9MzGmwA{l0Une?R2> O&_Zq9;!MlKqyGRJNo=wzs=Y zF_{=)iwg{bF$N?VAY?KH5=h)KSVqK%$R7}gM&(yDhD@0V8W1PG-tI0#2NGhFwy&?B z?~l*-`+4s*EvlYBu3%b$!{Hd`_jwni_XKp0&Cf&6x{Kp@^cti4mYHa7l6{YIyt{R( z!;#mj1VdIRus{&CxD!ZP7&=pN9ibhL^0_G;h^wK6g<(WdJ@`P^5gbz_55ANQPyxLX zMipO!0hcsX2gQcfA}`@{=V0Y20Xc|63t*{uOf`j+2T%JI(Ab_PaV!n7R(tRYJ0KPc zEW#=^17e($CPa#&FphVIDY+~RdFok=p%|K^8IooRh87rBpjj+);V7OV$--jqbD3!9 z%Y#QPOBYBonM^v9E~jQhNSfz)l43}PArOKv>s1S+2-TdK_27l3Xehd+XewrV1Ys>< zd2p2L5Q(^+bE}$}LZOn8DWH?Ild?&rfs&ZR=?Np24lao#jKMfmEfZns99EBNmS#q^ zJGjpMzAXSMZXl40F=8(9crJu#Rn;LHnSzXnHiPv#Bo{+dOBf=oszYhcwAtuFr2&DZ z89_~pWm~lMySBOF&J{2j6&q}F6|zInVM`RfdMNb zs9N_RDDqC}o1pIQ$5%qLIixf4pAt;}8@8*WW~QgmR&5V+ zrv&=Qoik8Hl`_yNGV6tF7aWdJs^41?EN)G-R;ZIICVN+MC(DjCdRs5Pb;42mQ(*F# zDHGOzRdu}gnSE{FKGZZxfAfjPv3lO2eQVeIW@-n1zI6H0m1~j04O^rI{)5-sYX;lT zJ@0*|diUO=7cN{W((_*JI{epfYrdO47_I5qTvXWf)Hc^j@5Oby4xgH4J^A>Fr#Ewt zCA#ymiDJ2L)8+B!Ww#*ob?!KRYR(r|=WcJ@PON?H*4ID8>T1Is^GjNMv7NOqf4aY~ zxvlu)&OaK?PA%WF@%@JfDnsXA>RGHtTcY2)zy8vrRCAS zhIxCIe=|MQ|K-`U6VA`r_|e3jmp*SgxZ?PM8H01$+uP3cb9o;W>=+zeI-73poIbks z`pUk9l;qaDa-_Ha#-G~2?{zC0_a6GBIJ)M?lHOlm9NjTHxaC~ld$%6kwZHpj{}d)< PAO8NTYVU{hR=xf=TWsIe literal 0 HcmV?d00001 diff --git a/swing/src/net/sf/openrocket/gui/util/Icons.java b/swing/src/net/sf/openrocket/gui/util/Icons.java index f3bdcd413..76190fbc1 100644 --- a/swing/src/net/sf/openrocket/gui/util/Icons.java +++ b/swing/src/net/sf/openrocket/gui/util/Icons.java @@ -94,8 +94,11 @@ public class Icons { public static final Icon FAVORITE = loadImageIcon("pix/icons/star_gold.png", "Favorite"); public static final Icon CG_OVERRIDE = loadImageIcon("pix/icons/cg-override.png", "CG Override"); + public static final Icon CG_OVERRIDE_SUBCOMPONENT = loadImageIcon("pix/icons/cg-override-subcomponent.png", "CG Override Subcomponent"); public static final Icon CD_OVERRIDE = loadImageIcon("pix/icons/cd-override.png", "CD Override"); + public static final Icon CD_OVERRIDE_SUBCOMPONENT = loadImageIcon("pix/icons/cd-override-subcomponent.png", "CD Override Subcomponent"); public static final Icon MASS_OVERRIDE = loadImageIcon("pix/icons/mass-override.png", "Mass Override"); + public static final Icon MASS_OVERRIDE_SUBCOMPONENT = loadImageIcon("pix/icons/mass-override-subcomponent.png", "Mass Override Subcomponent"); // MANUFACTURERS ICONS public static final Icon RASAERO_ICON = loadImageIcon("pix/icons/RASAero_16.png", "RASAero Icon"); From 451dec9caca5ac5f989a8cc4e8c9c1f17b2bd5b1 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 8 Nov 2022 14:24:41 +0100 Subject: [PATCH 05/16] Add new, dark red color preset --- core/src/net/sf/openrocket/util/Color.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/net/sf/openrocket/util/Color.java b/core/src/net/sf/openrocket/util/Color.java index d74032a3f..59582160c 100644 --- a/core/src/net/sf/openrocket/util/Color.java +++ b/core/src/net/sf/openrocket/util/Color.java @@ -4,6 +4,7 @@ public class Color { public static Color BLACK = new Color(255,255,255); public static Color INVISIBLE = new Color(1, 1, 1, 0); + public static Color DARK_RED = new Color(200, 0, 0); private int red; private int green; @@ -60,5 +61,9 @@ public class Color { public String toString() { return "Color [r=" + red + ", g=" + green + ", b=" + blue + ", a=" + alpha + "]"; } + + public java.awt.Color toAWTColor() { + return new java.awt.Color(red, green, blue, alpha); + } } From 378808fae4b600a0cbba7be42b2b37f4bfe8b5b5 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 8 Nov 2022 14:25:05 +0100 Subject: [PATCH 06/16] Change warning to dark red color instead of flashy red --- .../gui/dialogs/preferences/LaunchPreferencesPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java index 3179a2c3b..f2bc4c8b5 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java @@ -38,7 +38,7 @@ public class LaunchPreferencesPanel extends PreferencesPanel { StyledLabel warning = new StyledLabel(String.format( "%s", trans.get("pref.dlg.lbl.launchWarning")), 0.5f, StyledLabel.Style.BOLD); - warning.setFontColor(Color.RED); + warning.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); warning.setToolTipText(trans.get("pref.dlg.lbl.launchWarning.ttip")); add(warning, "spanx, growx 0, gapbottom para, wrap"); From de15bdd79b9317f99b97c01cc21ea016239393b4 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 8 Nov 2022 15:48:59 +0100 Subject: [PATCH 07/16] Add warning in prefs sim options --- .../dialogs/preferences/SimulationPreferencesPanel.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java index 10af0a981..8e6e9eb8a 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java @@ -15,6 +15,7 @@ import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.adaptors.DoubleModel; import net.sf.openrocket.gui.adaptors.EnumModel; import net.sf.openrocket.gui.components.BasicSlider; +import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.simulation.RK4SimulationStepper; import net.sf.openrocket.unit.UnitGroup; @@ -79,6 +80,14 @@ public class SimulationPreferencesPanel extends PreferencesPanel { // layout subsub = new JPanel(new MigLayout("insets 0, fill", "[grow][min!][min!][]")); + // // Warning + StyledLabel warning = new StyledLabel(String.format( + "%s", trans.get("pref.dlg.lbl.launchWarning")), + 0, StyledLabel.Style.BOLD); + warning.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); + warning.setToolTipText(trans.get("pref.dlg.lbl.launchWarning.ttip")); + subsub.add(warning, "spanx, wrap para"); + // // Calculation method: tip = trans.get("simedtdlg.lbl.ttip.Calcmethod"); label = new JLabel(trans.get("simedtdlg.lbl.Calcmethod")); From 4e1c0f15fbad89dc4d203838c0d5df1a34182114 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 8 Nov 2022 16:51:15 +0100 Subject: [PATCH 08/16] [#1631] Show subcomponent overridden icon When a component's parent has the subcomponent overridden icon enabled, that component will now also have a dedicated icon, indicating that it's mass has been overridden --- core/resources/l10n/messages.properties | 6 ++++++ .../rocketcomponent/ComponentChangeEvent.java | 12 ++++++++++-- .../rocketcomponent/RocketComponent.java | 6 +++--- .../main/componenttree/ComponentTreeModel.java | 13 +++++++++---- .../componenttree/ComponentTreeRenderer.java | 18 +++++++++++++----- 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index c5929d0bb..b8969fd8b 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -930,6 +930,12 @@ RocketCompCfg.checkbox.OverrideSubcomponents = Override for all subcomponents RocketCompCfg.checkbox.OverrideSubcomponents.Mass.ttip = Overrides the aggregate mass of this component
and its subcomponents with the mass of this component. RocketCompCfg.checkbox.OverrideSubcomponents.CG.ttip = Overrides the center of gravity (CG) of this component
and its subcomponents with the CG of this component. RocketCompCfg.checkbox.OverrideSubcomponents.CD.ttip = Overrides the coefficient of drag (CD) of this component
and its subcomponents with the CD of this component. +RocketCompCfg.lbl.MassOverriddenBy = Mass overridden by %s +RocketCompCfg.lbl.CGOverriddenBy = CG overridden by %s +RocketCompCfg.lbl.CDOverriddenBy = CD overridden by %s +RocketCompCfg.lbl.MassOverriddenBy.ttip = The mass of this component is determined by the mass override value of %s +RocketCompCfg.lbl.CGOverriddenBy.ttip = The CG of this component is determined by the CG override value of %s +RocketCompCfg.lbl.CDOverriddenBy.ttip = The CD of this component is determined by the CD override value of %s RocketCompCfg.lbl.longB1 = The overridden mass and center of gravity does not include motors.
RocketCompCfg.lbl.longB2 = The center of gravity is measured from the front end of the RocketCompCfg.lbl.Commentsonthe = Comments on the diff --git a/core/src/net/sf/openrocket/rocketcomponent/ComponentChangeEvent.java b/core/src/net/sf/openrocket/rocketcomponent/ComponentChangeEvent.java index f74798f6a..b1aaf1777 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/ComponentChangeEvent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/ComponentChangeEvent.java @@ -14,8 +14,9 @@ public class ComponentChangeEvent extends EventObject { UNDO( 16, "UNDO"), MOTOR( 32, "Motor"), EVENT( 64, "Event"), - TEXTURE ( 128, "Texture") - , GRAPHIC( 256, "Configuration") + TEXTURE ( 128, "Texture"), + GRAPHIC( 256, "Configuration"), + TREE_CHILDREN( 512, "TREE_CHILDREN"), ; protected int value; @@ -45,6 +46,8 @@ public class ComponentChangeEvent extends EventObject { /** A change that affects the rocket tree structure */ public static final int TREE_CHANGE = TYPE.TREE.value; + /** A change that affects the children's tree structure */ + public static final int TREE_CHANGE_CHILDREN = TYPE.TREE_CHILDREN.value; /** A change caused by undo/redo. */ public static final int UNDO_CHANGE = TYPE.UNDO.value; /** A change in the motor configurations or names */ @@ -124,6 +127,9 @@ public class ComponentChangeEvent extends EventObject { public boolean isTreeChange() { return TYPE.TREE.matches(this.type); } + public boolean isTreeChildrenChange() { + return TYPE.TREE_CHILDREN.matches(this.type); + } public boolean isUndoChange() { return TYPE.UNDO.matches(this.type); @@ -150,6 +156,8 @@ public class ComponentChangeEvent extends EventObject { s += ",aero"; if (isTreeChange()) s += ",tree"; + if (isTreeChildrenChange()) + s += ",treechild"; if (isUndoChange()) s += ",undo"; if (isMotorChange()) diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 0c3e2364b..5f362cf61 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -819,7 +819,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab updateChildrenMassOverriddenBy(); - fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE | ComponentChangeEvent.TREE_CHANGE_CHILDREN); } /** @@ -857,7 +857,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab updateChildrenCGOverriddenBy(); - fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE | ComponentChangeEvent.TREE_CHANGE_CHILDREN); } /** @@ -897,7 +897,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab overrideSubcomponentsCD(override); - fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE | ComponentChangeEvent.TREE_CHANGE_CHILDREN); } /** diff --git a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java index b33089118..5506cc3ce 100644 --- a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java +++ b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java @@ -90,9 +90,9 @@ public class ComponentTreeModel implements TreeModel, ComponentChangeListener { private void fireTreeNodeChanged(RocketComponent node) { TreeModelEvent e = new TreeModelEvent(this, makeTreePath(node), null, null); - Object[] l = listeners.toArray(); - for (int i = 0; i < l.length; i++) - ((TreeModelListener) l[i]).treeNodesChanged(e); + for (TreeModelListener listener : listeners) { + listener.treeNodesChanged(e); + } } @@ -147,7 +147,12 @@ public class ComponentTreeModel implements TreeModel, ComponentChangeListener { // TODO: LOW: Could this be performed better? expandAll(); } - } else { + } else if (e.isTreeChildrenChange()) { + for (RocketComponent c : e.getSource().getAllChildren()) { + fireTreeNodeChanged(c); + } + } + else { fireTreeNodeChanged(e.getSource()); } } diff --git a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java index ccd0e6ee4..0f66f24e2 100644 --- a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java +++ b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java @@ -49,23 +49,31 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer { } else { setIcon(ComponentIcons.getSmallIcon(value.getClass())); } - if (c.isMassOverridden() || c.isCGOverridden() || c.isCDOverridden()) { + if (c.isMassOverridden() || c.getMassOverriddenBy() != null || + c.isCGOverridden() || c.getCGOverriddenBy() != null || + c.isCDOverridden() || c.getCDOverriddenBy() != null) { JPanel p = new JPanel(); p.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1)); p.setBackground(UIManager.getColor("Tree.textBackground")); p.setForeground(UIManager.getColor("Tree.textForeground")); p.add(comp/* , BorderLayout.WEST */); - if (c.isMassOverridden()) { + if (c.getMassOverriddenBy() != null) { + p.add(new JLabel(Icons.MASS_OVERRIDE_SUBCOMPONENT)); + } else if (c.isMassOverridden()) { p.add(new JLabel(Icons.MASS_OVERRIDE)); } - if (c.isCGOverridden()) { + if (c.getCGOverriddenBy() != null) { + p.add(new JLabel(Icons.CG_OVERRIDE_SUBCOMPONENT)); + } else if (c.isCGOverridden()) { p.add(new JLabel(Icons.CG_OVERRIDE)); } - if (c.isCDOverridden()) { + if (c.getCDOverriddenBy() != null) { + p.add(new JLabel(Icons.CD_OVERRIDE_SUBCOMPONENT)); + } else if (c.isCDOverridden()) { p.add(new JLabel(Icons.CD_OVERRIDE)); } - + // Make sure the tooltip also works on the override icons if (components != null && components.size() > 1 && components.contains(c)) { p.setToolTipText(getToolTipMultipleComponents(components)); } else { From a3776ddec0b68be699b86d7d91d3b375ab708425 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 8 Nov 2022 16:51:39 +0100 Subject: [PATCH 09/16] Add warning for subcomponents that mass/CG/CD is overridden by parent --- .../configdialog/RocketComponentConfig.java | 81 ++++++++++++++----- 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index de4777116..039fcd661 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -355,6 +355,7 @@ public class RocketComponentConfig extends JPanel { //// Override the mass, center of gravity, or drag coeficient of the component JCheckBox check; + JCheckBox checkSub; BooleanModel bm; UnitSelector us; BasicSlider bs; @@ -371,15 +372,27 @@ public class RocketComponentConfig extends JPanel { ////// Override subcomponents BooleanModel bmSubcomp = new BooleanModel(component, "SubcomponentsOverriddenMass"); - check = new JCheckBox(bmSubcomp); - check.setText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents")); - Font smallFont = check.getFont(); + checkSub = new JCheckBox(bmSubcomp); + checkSub.setText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents")); + Font smallFont = checkSub.getFont(); smallFont = smallFont.deriveFont(smallFont.getSize2D() - 1); - check.setFont(smallFont); - check.setToolTipText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents.Mass.ttip")); - bm.addEnableComponent(check, true); - checkboxes.add(check, "gapleft 25lp, wrap"); - order.add(check); + checkSub.setFont(smallFont); + checkSub.setToolTipText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents.Mass.ttip")); + bm.addEnableComponent(checkSub, true); + checkboxes.add(checkSub, "gapleft 25lp, wrap"); + order.add(checkSub); + + ////// Mass overridden by + if (component.getMassOverriddenBy() != null) { + StyledLabel labelMassOverriddenBy = new StyledLabel( + String.format(trans.get("RocketCompCfg.lbl.MassOverriddenBy"), component.getMassOverriddenBy().getName()), + 0, StyledLabel.Style.BOLD); + labelMassOverriddenBy.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); + labelMassOverriddenBy.setToolTipText( + String.format(trans.get("RocketCompCfg.lbl.MassOverriddenBy.ttip"), component.getMassOverriddenBy().getName())); + checkboxes.add(labelMassOverriddenBy, "gapleft 25lp, wrap"); + check.setEnabled(false); + } panel.add(checkboxes, "growx 1, gapright 20lp"); @@ -413,13 +426,25 @@ public class RocketComponentConfig extends JPanel { ////// Override subcomponents bmSubcomp = new BooleanModel(component, "SubcomponentsOverriddenCG"); - check = new JCheckBox(bmSubcomp); - check.setText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents")); - check.setFont(smallFont); - check.setToolTipText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents.CG.ttip")); - bm.addEnableComponent(check, true); - checkboxes.add(check, "gapleft 25lp, wrap"); - order.add(check); + checkSub = new JCheckBox(bmSubcomp); + checkSub.setText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents")); + checkSub.setFont(smallFont); + checkSub.setToolTipText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents.CG.ttip")); + bm.addEnableComponent(checkSub, true); + checkboxes.add(checkSub, "gapleft 25lp, wrap"); + order.add(checkSub); + + ////// CG overridden by + if (component.getCGOverriddenBy() != null) { + StyledLabel labelCGOverriddenBy = new StyledLabel( + String.format(trans.get("RocketCompCfg.lbl.CGOverriddenBy"), component.getCGOverriddenBy().getName()), + 0, StyledLabel.Style.BOLD); + labelCGOverriddenBy.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); + labelCGOverriddenBy.setToolTipText( + String.format(trans.get("RocketCompCfg.lbl.CGOverriddenBy.ttip"), component.getCGOverriddenBy().getName())); + checkboxes.add(labelCGOverriddenBy, "gapleft 25lp, wrap"); + check.setEnabled(false); + } panel.add(checkboxes, "growx 1, gapright 20lp"); @@ -484,13 +509,25 @@ public class RocketComponentConfig extends JPanel { ////// Override subcomponents bmSubcomp = new BooleanModel(component, "SubcomponentsOverriddenCD"); - check = new JCheckBox(bmSubcomp); - check.setText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents")); - check.setFont(smallFont); - check.setToolTipText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents.CD.ttip")); - bm.addEnableComponent(check, true); - checkboxes.add(check, "gapleft 25lp, wrap"); - order.add(check); + checkSub = new JCheckBox(bmSubcomp); + checkSub.setText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents")); + checkSub.setFont(smallFont); + checkSub.setToolTipText(trans.get("RocketCompCfg.checkbox.OverrideSubcomponents.CD.ttip")); + bm.addEnableComponent(checkSub, true); + checkboxes.add(checkSub, "gapleft 25lp, wrap"); + order.add(checkSub); + + ////// CG overridden by + if (component.getCDOverriddenBy() != null) { + StyledLabel labelCDOverriddenBy = new StyledLabel( + String.format(trans.get("RocketCompCfg.lbl.CDOverriddenBy"), component.getCDOverriddenBy().getName()), + 0, StyledLabel.Style.BOLD); + labelCDOverriddenBy.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor()); + labelCDOverriddenBy.setToolTipText( + String.format(trans.get("RocketCompCfg.lbl.CDOverriddenBy"), component.getCDOverriddenBy().getName())); + checkboxes.add(labelCDOverriddenBy, "gapleft 25lp, wrap"); + check.setEnabled(false); + } panel.add(checkboxes, "growx 1, gapright 20lp"); From 072616468ea4fdd3dfb47a9cbd40745e8402a4e0 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 9 Nov 2022 01:55:49 +0100 Subject: [PATCH 10/16] Fix component tooltip when subcomponents overridden --- core/resources/l10n/messages.properties | 9 +- core/resources/l10n/messages_de.properties | 1 + core/resources/l10n/messages_es.properties | 2 + core/resources/l10n/messages_fr.properties | 2 + core/resources/l10n/messages_it.properties | 1 + core/resources/l10n/messages_nl.properties | 4 +- .../componenttree/ComponentTreeRenderer.java | 89 ++++++++++++++----- 7 files changed, 81 insertions(+), 27 deletions(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index b8969fd8b..135ad0ed8 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -932,7 +932,7 @@ RocketCompCfg.checkbox.OverrideSubcomponents.CG.ttip = Overrides the cente RocketCompCfg.checkbox.OverrideSubcomponents.CD.ttip = Overrides the coefficient of drag (CD) of this component
and its subcomponents with the CD of this component. RocketCompCfg.lbl.MassOverriddenBy = Mass overridden by %s RocketCompCfg.lbl.CGOverriddenBy = CG overridden by %s -RocketCompCfg.lbl.CDOverriddenBy = CD overridden by %s +RocketCompCfg.lbl.CDOverriddenBy = CD overridden by %s RocketCompCfg.lbl.MassOverriddenBy.ttip = The mass of this component is determined by the mass override value of %s RocketCompCfg.lbl.CGOverriddenBy.ttip = The CG of this component is determined by the CG override value of %s RocketCompCfg.lbl.CDOverriddenBy.ttip = The CD of this component is determined by the CD override value of %s @@ -1360,10 +1360,13 @@ PlotDialog.CheckBox.Showdatapoints = Show data points 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. PlotDialog.btn.exportImage = Export Image -ComponentTree.ttip.massoverride = mass override -ComponentTree.ttip.cgoverride = cg override +ComponentTree.ttip.massoverride = mass overriden +ComponentTree.ttip.cgoverride = CG overriden +ComponentTree.ttip.cdoverride = CD overriden ! "main" prefix is used for the main application dialog +ComponentTreeRenderer.total = total + # FIXME: Rename the description keys main.menu.file = File diff --git a/core/resources/l10n/messages_de.properties b/core/resources/l10n/messages_de.properties index e2cda7b33..11418eb82 100644 --- a/core/resources/l10n/messages_de.properties +++ b/core/resources/l10n/messages_de.properties @@ -1024,6 +1024,7 @@ PlotDialog.Chart.Simulatedflight = Simulierter Flug PlotDialog.CheckBox.Showdatapoints = Datenpunkte anzeigen PlotDialog.lbl.Chart = Klicken+ziehen: runter+rechts um hinein zu zoomen, hoch+links um heraus zu zoomen +ComponentTreeRenderer.total = gesamt ! "main" prefix is used for the main application dialog diff --git a/core/resources/l10n/messages_es.properties b/core/resources/l10n/messages_es.properties index aec98d9c5..48a1df482 100644 --- a/core/resources/l10n/messages_es.properties +++ b/core/resources/l10n/messages_es.properties @@ -732,6 +732,8 @@ PlotDialog.CheckBox.Showdatapoints = Mostrar los datos de los puntos PlotDialog.lbl.Chart = Arastrar con bot\u00f3n-izq rat\u00f3n para zoom del \u00e1rea. Rueda rat\u00f3n para zoom. Ctrl+rueda rat\u00f3n para zoom eje x. Ctrl+arrastrar con bot\u00f3n-izq rat\u00f3n para desplazar. Arrastrar con bot\u00f3n-der para zoom din\u00e1mico PlotDialog.title.Flightdataplot = Representaci\u00f3n de los datos de vuelo +ComponentTreeRenderer.total = total + PreferencesDialog.languages.default = Idioma por defecto PreferencesDialog.lbl.language = Idioma de la interfaz: PreferencesDialog.lbl.languageEffect = El idioma cambiar\u00e1 la pr\u00f3xima vez que abra OpenRocket. diff --git a/core/resources/l10n/messages_fr.properties b/core/resources/l10n/messages_fr.properties index 0e9ea5cdb..8eed74a8f 100644 --- a/core/resources/l10n/messages_fr.properties +++ b/core/resources/l10n/messages_fr.properties @@ -724,6 +724,8 @@ PlotDialog.lbl.Chart = Cliquer+d\u00E9placer en bas +droite pour a ! PlotDialog PlotDialog.title.Flightdataplot = Trac\u00E9 du vol +ComponentTreeRenderer.total = total + PreferencesDialog.languages.default = Valeur syst\u00E8me par d\u00E9faut PreferencesDialog.lbl.language = Langue du programme: PreferencesDialog.lbl.languageEffect = La langue sera chang\u00E9e apr\u00E8s avoir red\u00E9marr\u00E9 OpenRocket. diff --git a/core/resources/l10n/messages_it.properties b/core/resources/l10n/messages_it.properties index f8b4cb3f0..897a2f29b 100644 --- a/core/resources/l10n/messages_it.properties +++ b/core/resources/l10n/messages_it.properties @@ -1025,6 +1025,7 @@ PlotDialog.Chart.Simulatedflight = Volo simulato PlotDialog.CheckBox.Showdatapoints = Mostra i punti PlotDialog.lbl.Chart = Clicca e trascina giu'-dx per ingrandire, su-sx per rimpicciolire +ComponentTreeRenderer.total = totali ! "main" prefix is used for the main application dialog diff --git a/core/resources/l10n/messages_nl.properties b/core/resources/l10n/messages_nl.properties index 08dccf2ca..f30d5c91e 100644 --- a/core/resources/l10n/messages_nl.properties +++ b/core/resources/l10n/messages_nl.properties @@ -1224,10 +1224,12 @@ TCMotorSelPan.btn.close = Sluit PlotDialog.CheckBox.Showdatapoints = Toon datapunten PlotDialog.lbl.Chart = Linksklik+sleep om in te zoomen op omgeving. Muiswiel om te zoomen. Ctrl-muiswiel om enkel x-as te zoomen. Ctrl-linksklik om te bewegen. Rechtsklik+sleep om dynamisch te zoomen. -ComponentTree.ttip.massoverride = Massa overschrijven +ComponentTree.ttip.massoverride = Massa overschreven ComponentTree.ttip.cgoverride = ZP overschrijven ! "main" prefix is used for the main application dialog +ComponentTreeRenderer.total = totaal + # FIXME: Rename the description keys main.menu.file = Bestand diff --git a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java index 0f66f24e2..2dad7795d 100644 --- a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java +++ b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java @@ -96,32 +96,60 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer { StringBuilder sb = new StringBuilder(); sb.append(""); + // Component name title sb.append("").append(c.getName()).append(""); - if (c.isMassive() || c.isMassOverridden()) { + + // Only add mass information if mass is not overridden by a parent component + RocketComponent overriddenBy = c.getMassOverriddenBy(); + if (overriddenBy == null) { + if (c.isMassive() || c.isMassOverridden()) { + sb.append(" (").append( + UnitGroup.UNITS_MASS.toStringUnit(c.getMass())); + if (c.getChildCount() > 0) { + sb.append(" of ") + .append(UnitGroup.UNITS_MASS.toStringUnit(c + .getSectionMass())).append(" ").append(trans.get("ComponentTreeRenderer.total")); + } + sb.append(")"); + } else { + if ((c.getChildCount() > 0) && (c.getSectionMass() > 0)) { + sb.append(" (") + .append(UnitGroup.UNITS_MASS.toStringUnit(c + .getSectionMass())).append(" ").append(trans.get("ComponentTreeRenderer.total")).append(")"); + } + } + } + + // Add component override information in title + if (c.isMassOverridden() && c.getMassOverriddenBy() == null) { + sb.append(", ").append(trans.get("ComponentTree.ttip.massoverride")); + } + if (c.isCGOverridden() && c.getCGOverriddenBy() == null) { + sb.append(", ").append(trans.get("ComponentTree.ttip.cgoverride")); + } + if (c.isCDOverridden() && c.getCDOverriddenBy() == null) { + sb.append(", ").append(trans.get("ComponentTree.ttip.cdoverride")); + } + + // Add parent component override information on new lines + if (overriddenBy != null) { + sb.append("
").append(String.format(trans.get("RocketCompCfg.lbl.MassOverriddenBy"), overriddenBy.getName())); sb.append(" (").append( - UnitGroup.UNITS_MASS.toStringUnit(c.getMass())); - if (c.getChildCount() > 0) { - sb.append(" of ") - .append(UnitGroup.UNITS_MASS.toStringUnit(c - .getSectionMass())).append(" total"); - } - sb.append(")"); - } else { - if ((c.getChildCount() > 0) && (c.getSectionMass() > 0)) { - sb.append(" (") - .append(UnitGroup.UNITS_MASS.toStringUnit(c - .getSectionMass())).append(" total)"); - } - } - - if (c.isMassOverridden()) { - sb.append(" ").append(trans.get("ComponentTree.ttip.massoverride")); - } - - if (c.isCGOverridden()) { - sb.append(" ").append(trans.get("ComponentTree.ttip.cgoverride")); + UnitGroup.UNITS_MASS.toStringUnit(overriddenBy.getMass())).append(")"); + } + overriddenBy = c.getCGOverriddenBy(); + if (overriddenBy != null) { + sb.append("
").append(String.format(trans.get("RocketCompCfg.lbl.CGOverriddenBy"), overriddenBy.getName())); + sb.append(" (").append( + UnitGroup.UNITS_LENGTH.toStringUnit(overriddenBy.getOverrideCGX())).append(")"); + } + overriddenBy = c.getCDOverriddenBy(); + if (overriddenBy != null) { + sb.append("
").append(String.format(trans.get("RocketCompCfg.lbl.CDOverriddenBy"), overriddenBy.getName())); + sb.append(" (").append(overriddenBy.getOverrideCD()).append(")"); } + // Add component comment on new line String comment = c.getComment().trim(); if (comment.length() > 0) { comment = TextUtil.escapeXML(comment); @@ -140,11 +168,24 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer { StringBuilder sb = new StringBuilder(); sb.append(""); + // Components title sb.append("Components"); + + // Calculate the total mass of the selected components double totalMass = 0; double totalSectionMass = 0; boolean containsSectionMass = false; + List overriddenByComponents = new java.util.ArrayList<>(); // Components that override the mass of the selected components for (RocketComponent c : components) { + RocketComponent overriddenBy = c.getMassOverriddenBy(); + if (overriddenBy != null) { + if (!components.contains(overriddenBy) && !overriddenByComponents.contains(overriddenBy)) { + totalMass += overriddenBy.getMass(); + overriddenByComponents.add(overriddenBy); + } + continue; + } + if (c.isMassive() || c.isMassOverridden()) { totalMass += c.getMass(); // Don't add this component's mass to the section mass if its parent is in the list, otherwise you add up duplicate mass @@ -163,9 +204,11 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer { } } + // Add total mass of the selected components in the title sb.append(" (").append(UnitGroup.UNITS_MASS.toStringUnit(totalMass)); if (containsSectionMass) { - sb.append(" of ").append(UnitGroup.UNITS_MASS.toStringUnit(totalSectionMass)).append(" total)"); + sb.append(" of ").append(UnitGroup.UNITS_MASS.toStringUnit(totalSectionMass)) + .append(" ").append(trans.get("ComponentTreeRenderer.total")).append(")"); } else { sb.append(")"); } From 03431b9c416f4e1c1c101b4481c5b5732eea72ad Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 9 Nov 2022 04:31:09 +0100 Subject: [PATCH 11/16] Fix issue in override parent updating for sub-children --- .../rocketcomponent/RocketComponent.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 5f362cf61..f5efe4503 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -1713,6 +1713,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } else { component.CDOverriddenBy = this.CDOverriddenBy; } + for (Iterator it = component.iterator(false); it.hasNext(); ) { + RocketComponent child = it.next(); + child.massOverriddenBy = component.massOverriddenBy; + child.CGOverriddenBy = component.CGOverriddenBy; + child.CDOverriddenBy = component.CDOverriddenBy; + } if (component instanceof AxialStage) { AxialStage nStage = (AxialStage) component; @@ -1753,9 +1759,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab if (children.remove(component)) { component.parent = null; - component.massOverriddenBy = null; - component.CGOverriddenBy = null; - component.CDOverriddenBy = null; + for (RocketComponent c : component) { + c.massOverriddenBy = null; + c.CGOverriddenBy = null; + c.CDOverriddenBy = null; + } if (component instanceof AxialStage) { AxialStage stage = (AxialStage) component; From 6d80378a3f598f454b737738d915205a4a49cb3b Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 9 Nov 2022 04:35:47 +0100 Subject: [PATCH 12/16] Add unit tests for overriddenBy --- .../rocketcomponent/OverrideTest.java | 295 ++++++++++++++++-- 1 file changed, 274 insertions(+), 21 deletions(-) diff --git a/core/test/net/sf/openrocket/rocketcomponent/OverrideTest.java b/core/test/net/sf/openrocket/rocketcomponent/OverrideTest.java index aaad470bb..1ad797970 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/OverrideTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/OverrideTest.java @@ -1,32 +1,21 @@ package net.sf.openrocket.rocketcomponent; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import org.junit.Test; - import net.sf.openrocket.aerodynamics.AerodynamicForces; import net.sf.openrocket.aerodynamics.BarrowmanCalculator; import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.aerodynamics.WarningSet; - -import net.sf.openrocket.rocketcomponent.AxialStage; -import net.sf.openrocket.rocketcomponent.BodyTube; -import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.FlightConfiguration; -import net.sf.openrocket.rocketcomponent.NoseCone; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; - -import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.BaseTestCase.BaseTestCase; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.TestRockets; -import net.sf.openrocket.util.BaseTestCase.BaseTestCase; +import org.junit.Test; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; public class OverrideTest extends BaseTestCase { @@ -109,4 +98,268 @@ public class OverrideTest extends BaseTestCase { Map forceMap = calc.getForceAnalysis(configuration, conditions, warnings); assertEquals(sustainer.getOverrideCD() + bodytube.getOverrideCD() + forceMap.get(nosecone).getCD(), forceMap.get(rocket).getCD(), MathUtil.EPSILON); } + + /** + * Test whether children components of a parent that has subcomponents overridden for mass, CG, or CD have the correct + * overriddenBy object. + */ + @Test + public void testOverriddenBy() { + // Create test rocket + Rocket rocket = TestRockets.makeEstesAlphaIII(); + + // Obtain the necessary components + AxialStage sustainer = rocket.getStage(0); + NoseCone noseCone = (NoseCone) sustainer.getChild(0); + BodyTube bodyTube = (BodyTube) sustainer.getChild(1); + FinSet finSet = (FinSet) bodyTube.getChild(0); + LaunchLug launchLug = (LaunchLug) bodyTube.getChild(1); + InnerTube innerTube = (InnerTube) bodyTube.getChild(2); + EngineBlock engineBlock = (EngineBlock) innerTube.getChild(0); + Parachute parachute = (Parachute) bodyTube.getChild(3); + CenteringRing bulkhead = (CenteringRing) bodyTube.getChild(4); + + // Check initial override by components + assertNull(rocket.getMassOverriddenBy()); + assertNull(rocket.getCGOverriddenBy()); + assertNull(rocket.getCDOverriddenBy()); + assertNull(sustainer.getMassOverriddenBy()); + assertNull(sustainer.getCGOverriddenBy()); + assertNull(sustainer.getCDOverriddenBy()); + assertNull(noseCone.getMassOverriddenBy()); + assertNull(noseCone.getCGOverriddenBy()); + assertNull(noseCone.getCDOverriddenBy()); + assertNull(bodyTube.getMassOverriddenBy()); + assertNull(bodyTube.getCGOverriddenBy()); + assertNull(bodyTube.getCDOverriddenBy()); + assertNull(finSet.getMassOverriddenBy()); + assertNull(finSet.getCGOverriddenBy()); + assertNull(finSet.getCDOverriddenBy()); + assertNull(launchLug.getMassOverriddenBy()); + assertNull(launchLug.getCGOverriddenBy()); + assertNull(launchLug.getCDOverriddenBy()); + assertNull(innerTube.getMassOverriddenBy()); + assertNull(innerTube.getCGOverriddenBy()); + assertNull(innerTube.getCDOverriddenBy()); + assertNull(engineBlock.getMassOverriddenBy()); + assertNull(engineBlock.getCGOverriddenBy()); + assertNull(engineBlock.getCDOverriddenBy()); + assertNull(parachute.getMassOverriddenBy()); + assertNull(parachute.getCGOverriddenBy()); + assertNull(parachute.getCDOverriddenBy()); + assertNull(bulkhead.getMassOverriddenBy()); + assertNull(bulkhead.getCGOverriddenBy()); + assertNull(bulkhead.getCDOverriddenBy()); + + // Override body tube mass, CG, and CD without for subcomponents + bodyTube.setMassOverridden(true); + bodyTube.setCGOverridden(true); + bodyTube.setCDOverridden(true); + + assertNull(sustainer.getMassOverriddenBy()); + assertNull(sustainer.getCGOverriddenBy()); + assertNull(sustainer.getCDOverriddenBy()); + assertNull(noseCone.getMassOverriddenBy()); + assertNull(noseCone.getCGOverriddenBy()); + assertNull(noseCone.getCDOverriddenBy()); + assertNull(bodyTube.getMassOverriddenBy()); + assertNull(bodyTube.getCGOverriddenBy()); + assertNull(bodyTube.getCDOverriddenBy()); + assertNull(finSet.getMassOverriddenBy()); + assertNull(finSet.getCGOverriddenBy()); + assertNull(finSet.getCDOverriddenBy()); + assertNull(launchLug.getMassOverriddenBy()); + assertNull(launchLug.getCGOverriddenBy()); + assertNull(launchLug.getCDOverriddenBy()); + assertNull(innerTube.getMassOverriddenBy()); + assertNull(innerTube.getCGOverriddenBy()); + assertNull(innerTube.getCDOverriddenBy()); + assertNull(engineBlock.getMassOverriddenBy()); + assertNull(engineBlock.getCGOverriddenBy()); + assertNull(engineBlock.getCDOverriddenBy()); + assertNull(parachute.getMassOverriddenBy()); + assertNull(parachute.getCGOverriddenBy()); + assertNull(parachute.getCDOverriddenBy()); + assertNull(bulkhead.getMassOverriddenBy()); + assertNull(bulkhead.getCGOverriddenBy()); + assertNull(bulkhead.getCDOverriddenBy()); + + // Override body tube mass for subcomponents + bodyTube.setSubcomponentsOverriddenMass(true); + + assertNull(sustainer.getMassOverriddenBy()); + assertNull(sustainer.getCGOverriddenBy()); + assertNull(sustainer.getCDOverriddenBy()); + assertNull(noseCone.getMassOverriddenBy()); + assertNull(noseCone.getCGOverriddenBy()); + assertNull(noseCone.getCDOverriddenBy()); + assertNull(bodyTube.getMassOverriddenBy()); + assertNull(bodyTube.getCGOverriddenBy()); + assertNull(bodyTube.getCDOverriddenBy()); + assertEquals(bodyTube, finSet.getMassOverriddenBy()); + assertNull(finSet.getCGOverriddenBy()); + assertNull(finSet.getCDOverriddenBy()); + assertEquals(bodyTube, launchLug.getMassOverriddenBy()); + assertNull(launchLug.getCGOverriddenBy()); + assertNull(launchLug.getCDOverriddenBy()); + assertEquals(bodyTube, innerTube.getMassOverriddenBy()); + assertNull(innerTube.getCGOverriddenBy()); + assertNull(innerTube.getCDOverriddenBy()); + assertEquals(bodyTube, engineBlock.getMassOverriddenBy()); + assertNull(engineBlock.getCGOverriddenBy()); + assertNull(engineBlock.getCDOverriddenBy()); + assertEquals(bodyTube, parachute.getMassOverriddenBy()); + assertNull(parachute.getCGOverriddenBy()); + assertNull(parachute.getCDOverriddenBy()); + assertEquals(bodyTube, bulkhead.getMassOverriddenBy()); + assertNull(bulkhead.getCGOverriddenBy()); + assertNull(bulkhead.getCDOverriddenBy()); + + // Undo override body tube mass for subcomponents, do override of CG and CD for subcomponents + bodyTube.setSubcomponentsOverriddenMass(false); + bodyTube.setSubcomponentsOverriddenCG(true); + bodyTube.setSubcomponentsOverriddenCD(true); + + assertNull(noseCone.getMassOverriddenBy()); + assertNull(noseCone.getCGOverriddenBy()); + assertNull(noseCone.getCDOverriddenBy()); + assertNull(bodyTube.getMassOverriddenBy()); + assertNull(bodyTube.getCGOverriddenBy()); + assertNull(bodyTube.getCDOverriddenBy()); + assertNull(finSet.getMassOverriddenBy()); + assertEquals(bodyTube, finSet.getCGOverriddenBy()); + assertEquals(bodyTube, finSet.getCDOverriddenBy()); + assertNull(launchLug.getMassOverriddenBy()); + assertEquals(bodyTube, launchLug.getCGOverriddenBy()); + assertEquals(bodyTube, launchLug.getCDOverriddenBy()); + assertNull(innerTube.getMassOverriddenBy()); + assertEquals(bodyTube, innerTube.getCGOverriddenBy()); + assertEquals(bodyTube, innerTube.getCDOverriddenBy()); + assertNull(engineBlock.getMassOverriddenBy()); + assertEquals(bodyTube, engineBlock.getCGOverriddenBy()); + assertEquals(bodyTube, engineBlock.getCDOverriddenBy()); + assertNull(parachute.getMassOverriddenBy()); + assertEquals(bodyTube, parachute.getCGOverriddenBy()); + assertEquals(bodyTube, parachute.getCDOverriddenBy()); + assertNull(bulkhead.getMassOverriddenBy()); + assertEquals(bodyTube, bulkhead.getCGOverriddenBy()); + assertEquals(bodyTube, bulkhead.getCDOverriddenBy()); + + // Move the inner tube from the body tube to the nose cone + bodyTube.removeChild(innerTube); + noseCone.addChild(innerTube); + + assertNull(noseCone.getMassOverriddenBy()); + assertNull(noseCone.getCGOverriddenBy()); + assertNull(noseCone.getCDOverriddenBy()); + assertNull(innerTube.getMassOverriddenBy()); + assertNull(innerTube.getCGOverriddenBy()); + assertNull(innerTube.getCDOverriddenBy()); + assertNull(engineBlock.getMassOverriddenBy()); + assertNull(engineBlock.getCGOverriddenBy()); + assertNull(engineBlock.getCDOverriddenBy()); + assertNull(bodyTube.getMassOverriddenBy()); + assertNull(bodyTube.getCGOverriddenBy()); + assertNull(bodyTube.getCDOverriddenBy()); + assertNull(finSet.getMassOverriddenBy()); + assertEquals(bodyTube, finSet.getCGOverriddenBy()); + assertEquals(bodyTube, finSet.getCDOverriddenBy()); + assertNull(launchLug.getMassOverriddenBy()); + assertEquals(bodyTube, launchLug.getCGOverriddenBy()); + assertEquals(bodyTube, launchLug.getCDOverriddenBy()); + assertNull(parachute.getMassOverriddenBy()); + assertEquals(bodyTube, parachute.getCGOverriddenBy()); + assertEquals(bodyTube, parachute.getCDOverriddenBy()); + assertNull(bulkhead.getMassOverriddenBy()); + assertEquals(bodyTube, bulkhead.getCGOverriddenBy()); + assertEquals(bodyTube, bulkhead.getCDOverriddenBy()); + + // Override mass of nose cone + noseCone.setMassOverridden(true); + + assertNull(noseCone.getMassOverriddenBy()); + assertNull(noseCone.getCGOverriddenBy()); + assertNull(noseCone.getCDOverriddenBy()); + assertNull(innerTube.getMassOverriddenBy()); + assertNull(innerTube.getCGOverriddenBy()); + assertNull(innerTube.getCDOverriddenBy()); + assertNull(engineBlock.getMassOverriddenBy()); + assertNull(engineBlock.getCGOverriddenBy()); + assertNull(engineBlock.getCDOverriddenBy()); + assertNull(bodyTube.getMassOverriddenBy()); + assertNull(bodyTube.getCGOverriddenBy()); + assertNull(bodyTube.getCDOverriddenBy()); + assertNull(finSet.getMassOverriddenBy()); + assertEquals(bodyTube, finSet.getCGOverriddenBy()); + assertEquals(bodyTube, finSet.getCDOverriddenBy()); + assertNull(launchLug.getMassOverriddenBy()); + assertEquals(bodyTube, launchLug.getCGOverriddenBy()); + assertEquals(bodyTube, launchLug.getCDOverriddenBy()); + assertNull(parachute.getMassOverriddenBy()); + assertEquals(bodyTube, parachute.getCGOverriddenBy()); + assertEquals(bodyTube, parachute.getCDOverriddenBy()); + assertNull(bulkhead.getMassOverriddenBy()); + assertEquals(bodyTube, bulkhead.getCGOverriddenBy()); + assertEquals(bodyTube, bulkhead.getCDOverriddenBy()); + + // Override nose cone mass for all subcomponents + noseCone.setSubcomponentsOverriddenMass(true); + + assertNull(sustainer.getMassOverriddenBy()); + assertNull(sustainer.getCGOverriddenBy()); + assertNull(sustainer.getCDOverriddenBy()); + assertNull(noseCone.getMassOverriddenBy()); + assertNull(noseCone.getCGOverriddenBy()); + assertNull(noseCone.getCDOverriddenBy()); + assertEquals(noseCone, innerTube.getMassOverriddenBy()); + assertNull(innerTube.getCGOverriddenBy()); + assertNull(innerTube.getCDOverriddenBy()); + assertEquals(noseCone, engineBlock.getMassOverriddenBy()); + assertNull(engineBlock.getCGOverriddenBy()); + assertNull(engineBlock.getCDOverriddenBy()); + assertNull(bodyTube.getMassOverriddenBy()); + assertNull(bodyTube.getCGOverriddenBy()); + assertNull(bodyTube.getCDOverriddenBy()); + assertNull(finSet.getMassOverriddenBy()); + assertEquals(bodyTube, finSet.getCGOverriddenBy()); + assertEquals(bodyTube, finSet.getCDOverriddenBy()); + assertNull(launchLug.getMassOverriddenBy()); + assertEquals(bodyTube, launchLug.getCGOverriddenBy()); + assertEquals(bodyTube, launchLug.getCDOverriddenBy()); + assertNull(parachute.getMassOverriddenBy()); + assertEquals(bodyTube, parachute.getCGOverriddenBy()); + assertEquals(bodyTube, parachute.getCDOverriddenBy()); + assertNull(bulkhead.getMassOverriddenBy()); + assertEquals(bodyTube, bulkhead.getCGOverriddenBy()); + assertEquals(bodyTube, bulkhead.getCDOverriddenBy()); + + // Override inner tube CG for all subcomponents + innerTube.setCGOverridden(true); + innerTube.setSubcomponentsOverriddenCG(true); + + assertNull(noseCone.getMassOverriddenBy()); + assertNull(noseCone.getCGOverriddenBy()); + assertNull(noseCone.getCDOverriddenBy()); + assertEquals(noseCone, innerTube.getMassOverriddenBy()); + assertNull(innerTube.getCGOverriddenBy()); + assertNull(innerTube.getCDOverriddenBy()); + assertEquals(noseCone, engineBlock.getMassOverriddenBy()); + assertEquals(innerTube, engineBlock.getCGOverriddenBy()); + assertNull(engineBlock.getCDOverriddenBy()); + assertNull(bodyTube.getMassOverriddenBy()); + assertNull(bodyTube.getCGOverriddenBy()); + assertNull(bodyTube.getCDOverriddenBy()); + assertNull(finSet.getMassOverriddenBy()); + assertEquals(bodyTube, finSet.getCGOverriddenBy()); + assertEquals(bodyTube, finSet.getCDOverriddenBy()); + assertNull(launchLug.getMassOverriddenBy()); + assertEquals(bodyTube, launchLug.getCGOverriddenBy()); + assertEquals(bodyTube, launchLug.getCDOverriddenBy()); + assertNull(parachute.getMassOverriddenBy()); + assertEquals(bodyTube, parachute.getCGOverriddenBy()); + assertEquals(bodyTube, parachute.getCDOverriddenBy()); + assertNull(bulkhead.getMassOverriddenBy()); + assertEquals(bodyTube, bulkhead.getCGOverriddenBy()); + assertEquals(bodyTube, bulkhead.getCDOverriddenBy()); + } } From f9383b6c7df07533286a4cf050dbabe064213928 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 9 Nov 2022 05:31:45 +0100 Subject: [PATCH 13/16] Fix overriddenBy information getting lost between sub-children --- .../rocketcomponent/RocketComponent.java | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index f5efe4503..3309e354e 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -971,6 +971,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab RocketComponent overriddenBy = massOverridden && overrideSubcomponentsMass ? this : null; for (RocketComponent c : getAllChildren()) { c.massOverriddenBy = overriddenBy; + // We need to update overriddenBy in case one of the children components has its subcomponents overridden + if (overriddenBy == null) { + overriddenBy = c.massOverridden && c.overrideSubcomponentsMass ? c : null; + } } } @@ -978,6 +982,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab RocketComponent overriddenBy = cgOverridden && overrideSubcomponentsCG ? this : null; for (RocketComponent c : getAllChildren()) { c.CGOverriddenBy = overriddenBy; + // We need to update overriddenBy in case one of the children components has its subcomponents overridden + if (overriddenBy == null) { + overriddenBy = c.cgOverridden && c.overrideSubcomponentsCG ? c : null; + } } } @@ -985,6 +993,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab RocketComponent overriddenBy = cdOverridden && overrideSubcomponentsCD ? this : null; for (RocketComponent c : getAllChildren()) { c.CDOverriddenBy = overriddenBy; + // We need to update overriddenBy in case one of the children components has its subcomponents overridden + if (overriddenBy == null) { + overriddenBy = c.cdOverridden && c.overrideSubcomponentsCD ? c : null; + } } } @@ -1715,9 +1727,18 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } for (Iterator it = component.iterator(false); it.hasNext(); ) { RocketComponent child = it.next(); - child.massOverriddenBy = component.massOverriddenBy; - child.CGOverriddenBy = component.CGOverriddenBy; - child.CDOverriddenBy = component.CDOverriddenBy; + // You only want to change the overriddenBy if the overriddenBy of component changed (i.e. is not null), + // otherwise you could lose overriddenBy information of the sub-children that have one of this component's + // children as its overrideBy component. + if (component.massOverriddenBy != null) { + child.massOverriddenBy = component.massOverriddenBy; + } + if (component.CGOverriddenBy != null) { + child.CGOverriddenBy = component.CGOverriddenBy; + } + if (component.CDOverriddenBy != null) { + child.CDOverriddenBy = component.CDOverriddenBy; + } } if (component instanceof AxialStage) { @@ -1760,9 +1781,18 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab if (children.remove(component)) { component.parent = null; for (RocketComponent c : component) { - c.massOverriddenBy = null; - c.CGOverriddenBy = null; - c.CDOverriddenBy = null; + // You only want to set the override components to null if the child's override component is either + // this component, or a (super-)parent of this component. Otherwise, you could lose the overrideBy + // information of sub-children that have one of this component's children as its overrideBy component. + if (c.massOverriddenBy == this || c.massOverriddenBy == this.massOverriddenBy) { + c.massOverriddenBy = null; + } + if (c.CGOverriddenBy == this || c.CGOverriddenBy == this.CGOverriddenBy) { + c.CGOverriddenBy = null; + } + if (c.CDOverriddenBy == this || c.CDOverriddenBy == this.CDOverriddenBy) { + c.CDOverriddenBy = null; + } } if (component instanceof AxialStage) { From 16354971a457eb30e74ae6f95aa071fa093ec62c Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 9 Nov 2022 05:32:05 +0100 Subject: [PATCH 14/16] Update unit tests --- .../rocketcomponent/OverrideTest.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/core/test/net/sf/openrocket/rocketcomponent/OverrideTest.java b/core/test/net/sf/openrocket/rocketcomponent/OverrideTest.java index 1ad797970..71bfa9ab5 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/OverrideTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/OverrideTest.java @@ -361,5 +361,82 @@ public class OverrideTest extends BaseTestCase { assertNull(bulkhead.getMassOverriddenBy()); assertEquals(bodyTube, bulkhead.getCGOverriddenBy()); assertEquals(bodyTube, bulkhead.getCDOverriddenBy()); + + // Set body tube mass override, reset CG & CD, and move inner tube back to body tube + bodyTube.setMassOverridden(true); + bodyTube.setSubcomponentsOverriddenMass(true); + bodyTube.setCGOverridden(false); + bodyTube.setCDOverridden(false); + noseCone.removeChild(innerTube); + bodyTube.addChild(innerTube); + + assertNull(noseCone.getMassOverriddenBy()); + assertNull(noseCone.getCGOverriddenBy()); + assertNull(noseCone.getCDOverriddenBy()); + assertNull(bodyTube.getMassOverriddenBy()); + assertNull(bodyTube.getCGOverriddenBy()); + assertNull(bodyTube.getCDOverriddenBy()); + assertEquals(bodyTube, finSet.getMassOverriddenBy()); + assertNull(finSet.getCGOverriddenBy()); + assertNull(finSet.getCDOverriddenBy()); + assertEquals(bodyTube, launchLug.getMassOverriddenBy()); + assertNull(launchLug.getCGOverriddenBy()); + assertNull(launchLug.getCDOverriddenBy()); + assertEquals(bodyTube, parachute.getMassOverriddenBy()); + assertNull(parachute.getCGOverriddenBy()); + assertNull(parachute.getCDOverriddenBy()); + assertEquals(bodyTube, bulkhead.getMassOverriddenBy()); + assertNull(bulkhead.getCGOverriddenBy()); + assertNull(bulkhead.getCDOverriddenBy()); + assertEquals(bodyTube, innerTube.getMassOverriddenBy()); + assertNull(innerTube.getCGOverriddenBy()); + assertNull(innerTube.getCDOverriddenBy()); + assertEquals(bodyTube, engineBlock.getMassOverriddenBy()); + assertEquals(innerTube, engineBlock.getCGOverriddenBy()); + assertNull(engineBlock.getCDOverriddenBy()); + + // Toggle the body tube CG override for all subcomponents + bodyTube.setCGOverridden(true); + bodyTube.setSubcomponentsOverriddenCG(true); + + assertEquals(bodyTube, finSet.getMassOverriddenBy()); + assertEquals(bodyTube, finSet.getCGOverriddenBy()); + assertNull(finSet.getCDOverriddenBy()); + assertEquals(bodyTube, launchLug.getMassOverriddenBy()); + assertEquals(bodyTube, launchLug.getCGOverriddenBy()); + assertNull(launchLug.getCDOverriddenBy()); + assertEquals(bodyTube, parachute.getMassOverriddenBy()); + assertEquals(bodyTube, parachute.getCGOverriddenBy()); + assertNull(parachute.getCDOverriddenBy()); + assertEquals(bodyTube, bulkhead.getMassOverriddenBy()); + assertEquals(bodyTube, bulkhead.getCGOverriddenBy()); + assertNull(bulkhead.getCDOverriddenBy()); + assertEquals(bodyTube, innerTube.getMassOverriddenBy()); + assertEquals(bodyTube, innerTube.getCGOverriddenBy()); + assertNull(innerTube.getCDOverriddenBy()); + assertEquals(bodyTube, engineBlock.getMassOverriddenBy()); + assertEquals(bodyTube, engineBlock.getCGOverriddenBy()); + assertNull(engineBlock.getCDOverriddenBy()); + + // Toggle back + bodyTube.setSubcomponentsOverriddenCG(false); + assertEquals(bodyTube, finSet.getMassOverriddenBy()); + assertNull(finSet.getCGOverriddenBy()); + assertNull(finSet.getCDOverriddenBy()); + assertEquals(bodyTube, launchLug.getMassOverriddenBy()); + assertNull(launchLug.getCGOverriddenBy()); + assertNull(launchLug.getCDOverriddenBy()); + assertEquals(bodyTube, parachute.getMassOverriddenBy()); + assertNull(parachute.getCGOverriddenBy()); + assertNull(parachute.getCDOverriddenBy()); + assertEquals(bodyTube, bulkhead.getMassOverriddenBy()); + assertNull(bulkhead.getCGOverriddenBy()); + assertNull(bulkhead.getCDOverriddenBy()); + assertEquals(bodyTube, innerTube.getMassOverriddenBy()); + assertNull(innerTube.getCGOverriddenBy()); + assertNull(innerTube.getCDOverriddenBy()); + assertEquals(bodyTube, engineBlock.getMassOverriddenBy()); + assertEquals(innerTube, engineBlock.getCGOverriddenBy()); + assertNull(engineBlock.getCDOverriddenBy()); } } From 4c8aaee8a8b61242c9368502b5f48bc2c70d922c Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 9 Nov 2022 13:52:42 +0100 Subject: [PATCH 15/16] Replace override parent search with overriddenBy object --- .../gui/configdialog/RocketComponentConfig.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index 039fcd661..c9f17ee38 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -235,11 +235,9 @@ public class RocketComponentConfig extends JPanel { overridetext = trans.get("RocketCompCfg.lbl.overriddento") + " " + UnitGroup.UNITS_MASS.getDefaultUnit(). toStringUnit(component.getOverrideMass()) + ")"; } - - for (RocketComponent c = component.getParent(); c != null; c = c.getParent()) { - if (c.isMassOverridden() && c.isSubcomponentsOverriddenMass()) { - overridetext = trans.get("RocketCompCfg.lbl.overriddenby") + " " + c.getName() + ")"; - } + + if (component.getMassOverriddenBy() != null) { + overridetext = trans.get("RocketCompCfg.lbl.overriddenby") + " " + component.getMassOverriddenBy().getName() + ")"; } if (overridetext != null) { From a75d3402c3bedbabaade69ca9fe0d3524e0ac6fb Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 9 Nov 2022 15:14:16 +0100 Subject: [PATCH 16/16] Fix subcomponents override widgets not disabling when enabled prior to parent overriding subcomponents --- .../openrocket/gui/adaptors/BooleanModel.java | 9 +++++ .../configdialog/RocketComponentConfig.java | 39 +++++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/adaptors/BooleanModel.java b/swing/src/net/sf/openrocket/gui/adaptors/BooleanModel.java index 2f631058a..915313994 100644 --- a/swing/src/net/sf/openrocket/gui/adaptors/BooleanModel.java +++ b/swing/src/net/sf/openrocket/gui/adaptors/BooleanModel.java @@ -201,6 +201,15 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, componentEnableState.add(enableState); updateEnableStatus(); } + + /** + * Remove a component from the list of enable components controlled by this model. + * @param component component to remove from the list + */ + public void removeEnableComponent(Component component) { + checkState(true); + components.remove(component); + } /** * Add a component which will be enabled when this boolean is true. diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index c9f17ee38..2d0729992 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -389,7 +389,6 @@ public class RocketComponentConfig extends JPanel { labelMassOverriddenBy.setToolTipText( String.format(trans.get("RocketCompCfg.lbl.MassOverriddenBy.ttip"), component.getMassOverriddenBy().getName())); checkboxes.add(labelMassOverriddenBy, "gapleft 25lp, wrap"); - check.setEnabled(false); } panel.add(checkboxes, "growx 1, gapright 20lp"); @@ -410,6 +409,18 @@ public class RocketComponentConfig extends JPanel { bm.addEnableComponent(bs); panel.add(bs, "growx 5, w 100lp, wrap"); + if (component.getMassOverriddenBy() != null) { + check.setEnabled(false); + bm.removeEnableComponent(checkSub); + bm.removeEnableComponent(spin); + bm.removeEnableComponent(us); + bm.removeEnableComponent(bs); + checkSub.setEnabled(false); + spin.setEnabled(false); + us.setEnabled(false); + bs.setEnabled(false); + } + // END OVERRIDE MASS ---------------------------------- // OVERRIDE CG ---------------------------------- @@ -441,7 +452,6 @@ public class RocketComponentConfig extends JPanel { labelCGOverriddenBy.setToolTipText( String.format(trans.get("RocketCompCfg.lbl.CGOverriddenBy.ttip"), component.getCGOverriddenBy().getName())); checkboxes.add(labelCGOverriddenBy, "gapleft 25lp, wrap"); - check.setEnabled(false); } panel.add(checkboxes, "growx 1, gapright 20lp"); @@ -492,6 +502,18 @@ public class RocketComponentConfig extends JPanel { bm.addEnableComponent(bs); panel.add(bs, "growx 5, w 100lp, wrap"); + if (component.getCGOverriddenBy() != null) { + check.setEnabled(false); + bm.removeEnableComponent(checkSub); + bm.removeEnableComponent(spin); + bm.removeEnableComponent(us); + bm.removeEnableComponent(bs); + checkSub.setEnabled(false); + spin.setEnabled(false); + us.setEnabled(false); + bs.setEnabled(false); + } + // END OVERRIDE CG --------------------------------------------------- @@ -515,7 +537,7 @@ public class RocketComponentConfig extends JPanel { checkboxes.add(checkSub, "gapleft 25lp, wrap"); order.add(checkSub); - ////// CG overridden by + ////// CD overridden by if (component.getCDOverriddenBy() != null) { StyledLabel labelCDOverriddenBy = new StyledLabel( String.format(trans.get("RocketCompCfg.lbl.CDOverriddenBy"), component.getCDOverriddenBy().getName()), @@ -524,7 +546,6 @@ public class RocketComponentConfig extends JPanel { labelCDOverriddenBy.setToolTipText( String.format(trans.get("RocketCompCfg.lbl.CDOverriddenBy"), component.getCDOverriddenBy().getName())); checkboxes.add(labelCDOverriddenBy, "gapleft 25lp, wrap"); - check.setEnabled(false); } panel.add(checkboxes, "growx 1, gapright 20lp"); @@ -542,6 +563,16 @@ public class RocketComponentConfig extends JPanel { bm.addEnableComponent(bs); panel.add(bs, "top, skip, growx 5, w 100lp, wrap"); + if (component.getCDOverriddenBy() != null) { + check.setEnabled(false); + bm.removeEnableComponent(checkSub); + bm.removeEnableComponent(spin); + bm.removeEnableComponent(bs); + checkSub.setEnabled(false); + spin.setEnabled(false); + bs.setEnabled(false); + } + // END OVERRIDE CD -------------------------------------------------- // OVERRIDE MASS, CG DOESN'T INCLUDE MOTORS --------------------------------------------------