Skip to content

Commit 68732b8

Browse files
authored
Add support for rounding rectangles (#3010)
1 parent d889f02 commit 68732b8

13 files changed

Lines changed: 411 additions & 27 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.willwinder.universalgcodesender.uielements.components;
2+
3+
import net.miginfocom.swing.MigLayout;
4+
5+
import javax.swing.JLabel;
6+
import javax.swing.JPanel;
7+
import javax.swing.JSeparator;
8+
9+
public class SeparatorLabel extends JPanel {
10+
11+
private final JLabel label;
12+
13+
/**
14+
* Creates a new instance of SeparatorLabel
15+
*
16+
* @param text the text to show
17+
* @param horizontalAlignment One of the following constants
18+
* defined in <code>SwingConstants</code>:
19+
* <code>LEFT</code>,
20+
* <code>CENTER</code>,
21+
* <code>RIGHT</code>,
22+
* <code>LEADING</code> or
23+
* <code>TRAILING</code>.
24+
*/
25+
public SeparatorLabel(String text, int horizontalAlignment) {
26+
setLayout(new MigLayout("insets 0, gap 10, fillx"));
27+
label = new JLabel(text, horizontalAlignment);
28+
add(label, "spanx, growx, gaptop 5, gapbottom 0, wrap");
29+
add(new JSeparator(), "spanx, growx, gaptop 0, gapbottom 5, wrap");
30+
}
31+
32+
public void setText(String text) {
33+
label.setText(text);
34+
}
35+
}

ugs-core/src/resources/MessagesBundle_en_US.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,4 +812,5 @@ settings = Settings
812812
designer.panel.shape-settings.title=Shape Settings
813813
designer.panel.shape-settings.transform.title=Transform options
814814
designer.panel.shape-settings.cutting.title=Cutting options
815-
designer.panel.shape-settings.text.title=Text options
815+
designer.panel.shape-settings.text.title=Text options
816+
designer.panel.shape-settings.rectangle.title=Rectangle options

ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/entities/EntitySetting.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ public enum EntitySetting {
4646
LEAD_IN_PERCENT("Lead in percent", "leadInPercent"),
4747
INCLUDE_IN_EXPORT("Include in export", "includeInExport"),
4848
TOOL_PATH_ANGLE("Tool path angle", "toolPathAngle"),
49-
DIRECTION("Tool path direction", "direction");
50-
49+
DIRECTION("Tool path direction", "direction"),
50+
CORNER_RADIUS("Corner radius", "cornerRadius");
5151

5252
public static final List<EntitySetting> TRANSFORMATION_SETTINGS = List.of(
5353
EntitySetting.POSITION_X,
@@ -94,11 +94,6 @@ public enum EntitySetting {
9494
EntitySetting.FEED_RATE,
9595
EntitySetting.INCLUDE_IN_EXPORT);
9696

97-
public static final List<EntitySetting> DEFAULT_TEXT_SETTINGS = List.of(
98-
EntitySetting.TEXT,
99-
EntitySetting.FONT_FAMILY);
100-
101-
10297
private final String label;
10398
private final String propertyName;
10499

ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/entities/cuttable/Rectangle.java

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,24 @@ This file is part of Universal Gcode Sender (UGS).
1919
package com.willwinder.ugs.nbp.designer.entities.cuttable;
2020

2121
import com.willwinder.ugs.nbp.designer.entities.Entity;
22+
import com.willwinder.ugs.nbp.designer.entities.EntityEvent;
23+
import com.willwinder.ugs.nbp.designer.entities.EntitySetting;
24+
import com.willwinder.ugs.nbp.designer.entities.EventType;
2225
import com.willwinder.ugs.nbp.designer.model.Size;
26+
import static com.willwinder.universalgcodesender.utils.MathUtils.isEqual;
2327

2428
import java.awt.Shape;
25-
import java.awt.geom.Rectangle2D;
29+
import java.awt.geom.RoundRectangle2D;
30+
import java.util.ArrayList;
31+
import java.util.List;
2632

2733
/**
2834
* @author Joacim Breiler
2935
*/
3036
public class Rectangle extends AbstractCuttable {
3137

32-
private final Rectangle2D.Double shape;
38+
private final RoundRectangle2D.Double shape;
39+
private double cornerRadiusPercent = 0.0;
3340

3441
public Rectangle() {
3542
this(0, 0);
@@ -43,7 +50,7 @@ public Rectangle() {
4350
*/
4451
public Rectangle(double x, double y) {
4552
super(x, y);
46-
this.shape = new Rectangle2D.Double(0, 0, 1, 1);
53+
this.shape = new RoundRectangle2D.Double(0, 0, 1, 1, 0, 0);
4754
setName("Rectangle");
4855
}
4956

@@ -63,4 +70,57 @@ public Entity copy() {
6370
copyPropertiesTo(rectangle);
6471
return rectangle;
6572
}
73+
74+
@Override
75+
public List<EntitySetting> getSettings() {
76+
List<EntitySetting> settings = new ArrayList<>(super.getSettings());
77+
settings.add(EntitySetting.CORNER_RADIUS);
78+
return settings;
79+
}
80+
81+
@Override
82+
public void scale(double sx, double sy) {
83+
super.scale(sx, sy);
84+
setCornerRadiusPercent(cornerRadiusPercent);
85+
}
86+
87+
public void setCornerRadius(Double cornerRadius) {
88+
if (cornerRadius == null) return;
89+
90+
double w = getSize().getWidth();
91+
double h = getSize().getHeight();
92+
double minSide = Math.min(w, h);
93+
double percent = Math.max(0d, Math.min(1d, cornerRadius / minSide));
94+
95+
if (!isEqual(percent, this.cornerRadiusPercent, 0.01)) {
96+
setCornerRadiusPercent(percent);
97+
this.notifyEvent(new EntityEvent(this, EventType.SETTINGS_CHANGED));
98+
}
99+
}
100+
101+
private void setCornerRadiusPercent(double percent) {
102+
double w = getSize().getWidth();
103+
double h = getSize().getHeight();
104+
this.cornerRadiusPercent = percent;
105+
106+
// Avoid divide-by-zero; also no meaningful rounding for degenerate sizes
107+
if (w <= 0 || h <= 0) {
108+
shape.setRoundRect(0, 0, 1, 1, 0, 0);
109+
} else {
110+
// Desired arc diameter in "world space" (after transform)
111+
double worldArc = (Math.min(w, h) * 2) * percent;
112+
113+
// Convert to local arc diameters so that after scaling they become worldArc
114+
double localArcW = worldArc / w;
115+
double localArcH = worldArc / h;
116+
117+
shape.setRoundRect(0, 0, 1, 1, localArcW, localArcH);
118+
}
119+
}
120+
121+
public double getCornerRadius() {
122+
double w = getSize().getWidth();
123+
double h = getSize().getHeight();
124+
return Math.min(w, h) * cornerRadiusPercent;
125+
}
66126
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
Copyright 2026 Joacim Breiler
3+
4+
This file is part of Universal Gcode Sender (UGS).
5+
6+
UGS is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
UGS is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with UGS. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
package com.willwinder.ugs.nbp.designer.entities.settings;
20+
21+
import com.willwinder.ugs.nbp.designer.entities.Entity;
22+
import com.willwinder.ugs.nbp.designer.entities.EntitySetting;
23+
import com.willwinder.ugs.nbp.designer.entities.cuttable.Rectangle;
24+
import org.openide.util.lookup.ServiceProvider;
25+
26+
import java.util.List;
27+
28+
29+
@ServiceProvider(service = EntitySettingsManager.class, position = 10)
30+
public class RectangleSettingsManager implements EntitySettingsManager {
31+
32+
@Override
33+
public boolean canHandle(Entity entity) {
34+
return entity instanceof Rectangle;
35+
}
36+
37+
@Override
38+
public List<EntitySetting> getSupportedSettings(Entity entity) {
39+
return List.of(EntitySetting.CORNER_RADIUS);
40+
}
41+
42+
@Override
43+
public boolean supportsSetting(EntitySetting setting) {
44+
return setting == EntitySetting.CORNER_RADIUS;
45+
}
46+
47+
@Override
48+
public Object getSettingValue(EntitySetting setting, Entity entity) {
49+
return entity instanceof Rectangle rectangle ? rectangle.getCornerRadius() : null;
50+
}
51+
52+
@Override
53+
public void applySetting(EntitySetting setting, Object value, Entity entity) {
54+
if (setting == EntitySetting.CORNER_RADIUS && value instanceof Double cornerRounding && entity instanceof Rectangle rectangle) {
55+
rectangle.setCornerRadius(cornerRounding);
56+
}
57+
}
58+
59+
@Override
60+
public String getName() {
61+
return "RectangleSettingsManager";
62+
}
63+
}

ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/entities/settings/TransformationEntitySettingsManager.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This file is part of Universal Gcode Sender (UGS).
2121
import com.willwinder.ugs.nbp.designer.entities.Anchor;
2222
import com.willwinder.ugs.nbp.designer.entities.Entity;
2323
import com.willwinder.ugs.nbp.designer.entities.EntitySetting;
24+
import com.willwinder.ugs.nbp.designer.entities.cuttable.Rectangle;
2425
import org.openide.util.lookup.ServiceProvider;
2526

2627
import java.awt.geom.Point2D;
@@ -58,6 +59,7 @@ public Object getSettingValue(EntitySetting setting, Entity entity) {
5859
case WIDTH -> entity.getSize().getWidth();
5960
case HEIGHT -> entity.getSize().getHeight();
6061
case ROTATION -> entity.getRotation();
62+
case CORNER_RADIUS -> entity instanceof Rectangle rectangle ? rectangle.getCornerRadius() : null;
6163
case LOCK_RATIO -> false;
6264
default -> null;
6365
};
@@ -101,6 +103,11 @@ public void applySetting(EntitySetting setting, Object value, Entity entity) {
101103
entity.setLockRatio(lockRatio);
102104
}
103105
}
106+
case CORNER_RADIUS -> {
107+
if (value instanceof Double cornerRounding && entity instanceof Rectangle rectangle) {
108+
rectangle.setCornerRadius(cornerRounding);
109+
}
110+
}
104111
}
105112
}
106113

ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/gui/selectionsettings/settingspanels/CuttableSettingsPanel.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2024 Albert Giro Quer
2+
Copyright 2024-2026 Albert Giro Quer
33
44
This file is part of Universal Gcode Sender (UGS).
55
@@ -33,6 +33,7 @@ This file is part of Universal Gcode Sender (UGS).
3333
import com.willwinder.universalgcodesender.i18n.Localization;
3434
import com.willwinder.universalgcodesender.uielements.TextFieldUnit;
3535
import com.willwinder.universalgcodesender.uielements.components.PercentSpinner;
36+
import com.willwinder.universalgcodesender.uielements.components.SeparatorLabel;
3637
import com.willwinder.universalgcodesender.uielements.components.UnitSpinner;
3738
import net.miginfocom.swing.MigLayout;
3839
import org.openide.util.lookup.ServiceProvider;
@@ -46,7 +47,7 @@ This file is part of Universal Gcode Sender (UGS).
4647
import java.util.List;
4748
import java.util.Map;
4849

49-
@ServiceProvider(service = EntitySettingsPanel.class, position = 10)
50+
@ServiceProvider(service = EntitySettingsPanel.class, position = 15)
5051
public class CuttableSettingsPanel extends JPanel implements EntitySettingsPanel {
5152

5253
// Use EntitySetting property names instead of string constants
@@ -118,8 +119,7 @@ private JSlider createSlider(int min, int max, int value, int minorTick, int maj
118119
}
119120

120121
private void buildLayout() {
121-
add(new JLabel(Localization.getString("designer.panel.shape-settings.cutting.title"), SwingConstants.LEFT), "spanx, gaptop 5, gapbottom 0, wrap");
122-
add(new JSeparator(), "spanx, growx, gaptop 0, gapbottom 5, wrap");
122+
add(new SeparatorLabel(Localization.getString("designer.panel.shape-settings.cutting.title"), SwingConstants.RIGHT), "spanx, growx");
123123

124124
addLabeledComponent(EntitySetting.CUT_TYPE, "Cut Type", cutTypeComboBox);
125125
addLabeledComponent(EntitySetting.DIRECTION, "Cut direction", directionCombo);

0 commit comments

Comments
 (0)