JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
BoxLayout.java
Go to the documentation of this file.
1/**
2 * Copyright 2023 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.graph.ui.layout;
29
30import java.util.List;
31
32import com.jogamp.graph.ui.Group;
33import com.jogamp.graph.ui.Shape;
34import com.jogamp.math.FloatUtil;
35import com.jogamp.math.Vec2f;
36import com.jogamp.math.Vec3f;
37import com.jogamp.math.geom.AABBox;
38import com.jogamp.math.util.PMVMatrix4f;
39
40/**
41 * GraphUI Stack {@link Group.Layout}.
42 * <p>
43 * A stack of {@link Shape}s
44 * <ul>
45 * <li>Optionally centered {@link Alignment.Bit#CenterHoriz horizontally}, {@link Alignment.Bit#CenterVert vertically} or {@link Alignment#Center both}.</li>
46 * <li>Optionally scaled to cell-size if given and {@link Alignment#Fill}</li>
47 * <li>Unscaled {@link Padding} is applied to each {@Shape} via {@link Shape#setPaddding(Padding)} if passed in constructor and is scaled if {@link Alignment.Bit#Fill}</li>
48 * <li>Scaled {@link Margin} is applied unscaled if used and ignored with only center {@link Alignment} w/o additional scaling</li>
49 * <li>Not implemented {@link Alignment}: {@link Alignment.Bit#Top Top}, {@link Alignment.Bit#Right Right}, {@link Alignment.Bit#Bottom Bottom}, {@link Alignment.Bit#Left Left}</li>
50 * </ul>
51 * </p>
52 */
53public class BoxLayout implements Group.Layout {
54 private final Vec2f cellSize;
55 private final Alignment alignment;
56 /** Scaled {@link Margin} value is applied w/o additional scaling. */
57 private final Margin margin;
58 /** Unscaled {@link Padding} value. */
59 private final Padding padding;
60
61 private static final boolean TRACE_LAYOUT = false;
62
63
64 /**
65 */
66 public BoxLayout() {
67 this(0f, 0f, Alignment.None, Margin.None, null);
68 }
69
70 /**
71 *
72 * @param padding unscaled {@link Padding} applied to each {@Shape} via {@link Shape#setPaddding(Padding)} and is scaled if {@link Alignment.Bit#Fill}
73 */
74 public BoxLayout(final Padding padding) {
75 this(0f, 0f, Alignment.None, Margin.None, padding);
76 }
77
78 /**
79 *
80 * @param cellWidth optional cell width, zero for none
81 * @param cellHeight optional cell height, zero for none
82 * @param alignment
83 */
84 public BoxLayout(final float cellWidth, final float cellHeight, final Alignment alignment) {
85 this(cellWidth, cellHeight, alignment, Margin.None, null);
86 }
87
88 /**
89 *
90 * @param cellWidth optional cell width, zero for none
91 * @param cellHeight optional cell height, zero for none
92 * @param margin scaled {@link Margin} is applied unscaled and ignored with only center {@link Alignment} w/o additional scaling
93 */
94 public BoxLayout(final float cellWidth, final float cellHeight, final Margin margin) {
95 this(cellWidth, cellHeight, Alignment.None, margin, null);
96 }
97
98 /**
99 *
100 * @param cellWidth optional cell width, zero for none
101 * @param cellHeight optional cell height, zero for none
102 * @param padding unscaled {@link Padding} applied to each {@Shape} via {@link Shape#setPaddding(Padding)} and is scaled if {@link Alignment.Bit#Fill}
103 */
104 public BoxLayout(final float cellWidth, final float cellHeight, final Padding padding) {
105 this(cellWidth, cellHeight, Alignment.None, Margin.None, padding);
106 }
107
108 /**
109 *
110 * @param cellWidth optional cell width, zero for none
111 * @param cellHeight optional cell height, zero for none
112 * @param margin scaled {@link Margin} is applied unscaled and ignored with only center {@link Alignment} w/o additional scaling
113 * @param padding unscaled {@link Padding} applied to each {@Shape} via {@link Shape#setPaddding(Padding)} and is scaled if {@link Alignment.Bit#Fill}
114 */
115 public BoxLayout(final float cellWidth, final float cellHeight, final Margin margin, final Padding padding) {
116 this(cellWidth, cellHeight, Alignment.None, margin, padding);
117 }
118
119 /**
120 *
121 * @param cellWidth optional cell width, zero for none
122 * @param cellHeight optional cell height, zero for none
123 * @param margin scaled {@link Margin} is applied unscaled
124 */
125 public BoxLayout(final float cellWidth, final float cellHeight, final Alignment alignment, final Margin margin) {
126 this(cellWidth, cellHeight, alignment, margin, null);
127 }
128
129 /**
130 *
131 * @param cellWidth optional cell width, zero for none
132 * @param cellHeight optional cell height, zero for none
133 * @param alignment
134 * @param padding unscaled {@link Padding} applied to each {@Shape} via {@link Shape#setPaddding(Padding)} and is scaled if {@link Alignment.Bit#Fill}
135 */
136 public BoxLayout(final float cellWidth, final float cellHeight, final Alignment alignment, final Padding padding) {
137 this(cellWidth, cellHeight, alignment, Margin.None, padding);
138 }
139
140 /**
141 *
142 * @param cellWidth optional cell width, zero for none
143 * @param cellHeight optional cell height, zero for none
144 * @param alignment
145 * @param margin scaled {@link Margin} is applied unscaled and ignored with only center {@link Alignment} w/o additional scaling
146 * @param padding unscaled {@link Padding} applied to each {@Shape} via {@link Shape#setPaddding(Padding)} and is scaled if {@link Alignment.Bit#Fill}
147 */
148 public BoxLayout(final float cellWidth, final float cellHeight, final Alignment alignment, final Margin margin, final Padding padding) {
149 this.cellSize = new Vec2f(Math.max(0f, cellWidth), Math.max(0f, cellHeight));
150 this.alignment = alignment;
151 this.margin = margin;
152 this.padding = padding;
153 }
154 // Vec2f totalSize
155
156 /** Returns the preset cell size */
157 public Vec2f getCellSize() { return cellSize; }
158 /** Returns given {@link Alignment}. */
159 public Alignment getAlignment() { return alignment; }
160 /** Returns given scaled {@link Margin}. */
161 public Margin getMargin() { return margin; }
162 /** Returns given unscaled {@link Padding}, may be {@code null} if not given via constructor. */
163 public Padding getPadding() { return padding; }
164
165 @Override
166 public void preValidate(final Shape s) {
167 if( null != padding && !padding.zeroSize() ) {
168 s.setPaddding(padding);
169 }
170 }
171
172 @Override
173 public void layout(final Group g, final AABBox box, final PMVMatrix4f pmv) {
174 final boolean hasCellWidth = !FloatUtil.isZero(cellSize.x());
175 final boolean hasCellHeight = !FloatUtil.isZero(cellSize.y());
176 final boolean isCenteredHoriz = hasCellWidth && alignment.isSet(Alignment.Bit.CenterHoriz);
177 final boolean isCenteredVert = hasCellHeight && alignment.isSet(Alignment.Bit.CenterVert);
178 final boolean isScaled = alignment.isSet(Alignment.Bit.Fill) && ( hasCellWidth || hasCellHeight );
179
180 final List<Shape> shapes = g.getShapes();
181 final AABBox sbox = new AABBox();
182 for(int i=0; i < shapes.size(); ++i) {
183 final Shape s = shapes.get(i);
184
185 // measure size
186 pmv.pushMv();
187 s.applyMatToMv(pmv);
188 s.getBounds().transform(pmv.getMv(), sbox);
189 pmv.popMv();
190
191 final float x = 0, y = 0;
192 if( TRACE_LAYOUT ) {
193 System.err.println("bl("+i+").0: sbox "+sbox+", s "+s);
194 }
195
196 // IF isScaled: Uniform scale w/ lowest axis scale and center position on lower-scale axis
197 final float shapeWidthU = sbox.getWidth();
198 final float shapeHeightU = sbox.getHeight();
199 if( FloatUtil.isZero(shapeHeightU) || FloatUtil.isZero(shapeHeightU) ) {
200 continue;
201 }
202 final float sxy;
203 float dxh = 0, dyh = 0;
204 if( isScaled ) {
205 // scaling to cell size
206 final float cellWidth = hasCellWidth ? cellSize.x() - margin.width() : shapeWidthU;
207 final float cellHeight = hasCellHeight ? cellSize.y() - margin.height() : shapeHeightU;
208 final float sx = cellWidth / shapeWidthU;
209 final float sy = cellHeight/ shapeHeightU;
210 sxy = sx < sy ? sx : sy;
211
212 if( isCenteredHoriz ) {
213 dxh += shapeWidthU * ( sx - sxy ) * 0.5f; // horiz-center (adjustment for scale-axis w/o margin)
214 }
215 if( isCenteredVert ) {
216 dyh += shapeHeightU * ( sy - sxy ) * 0.5f; // vert-center (adjustment for scale-axis w/o margin)
217 }
218 dyh += margin.bottom; // always consider unscaled margin when scaling
219 dxh += margin.left; // ditto
220 if( TRACE_LAYOUT ) {
221 System.err.println("bl("+i+").s: "+sx+" x "+sy+" -> "+sxy+": +"+dxh+" / "+dyh+", U: s "+shapeWidthU+" x "+shapeHeightU+", sz "+cellWidth+" x "+cellHeight);
222 }
223 } else {
224 sxy = 1;
225 }
226 final float shapeWidthS = sxy * shapeWidthU;
227 final float shapeHeightS = sxy * shapeHeightU;
228 final float cellWidthS = hasCellWidth ? cellSize.x() : shapeWidthS;
229 final float cellHeightS = hasCellHeight ? cellSize.y() : shapeHeightS;
230
231 if( !isScaled ) {
232 // Center w/o scale and ignoring margin (not scaled)
233 if( isCenteredHoriz ) {
234 dxh += 0.5f * ( cellWidthS - shapeWidthS ); // horiz-center
235 } else {
236 dxh += margin.left;
237 }
238 if( isCenteredVert ) {
239 dyh += 0.5f * ( cellHeightS - shapeHeightS ); // vert-center
240 } else {
241 dyh += margin.bottom;
242 }
243 }
244
245 if( TRACE_LAYOUT ) {
246 System.err.println("bl("+i+").m: "+x+" / "+y+" + "+dxh+" / "+dyh+", sxy "+sxy+", S: s "+shapeWidthS+" x "+shapeHeightS+", sz "+cellWidthS+" x "+cellHeightS);
247 }
248 // Position and scale shape
249 {
250 // New shape position
251 s.moveTo( x + dxh, y + dyh, s.getPosition().z() );
252
253 // Remove the negative or positive delta on centered axis.
254 // Only remove negative offset of non-centered axis (i.e. underline)
255 final Vec3f diffBL = new Vec3f(s.getBounds().getLow());
256 diffBL.setZ(0);
257 if( isCenteredHoriz || isCenteredVert ) {
258 if( !isCenteredVert && diffBL.y() > 0 ) {
259 diffBL.setY(0); // only adjust negative if !center-vert
260 } else if( !isCenteredHoriz && diffBL.x() > 0 ) {
261 diffBL.setX(0); // only adjust negative if !center-horiz
262 }
263 diffBL.mul(s.getScale()).scale(-1f);
264 } else {
265 diffBL.min(new Vec3f()).mul(s.getScale()).scale(-1f);
266 }
267 s.move( diffBL.scale(sxy) );
268 if( TRACE_LAYOUT ) {
269 System.err.println("bl("+i+").bl: sbox0 "+s.getBounds()+", diffBL_ "+diffBL);
270 }
271
272 // resize bounds
273 box.resize( x, y, sbox.getMinZ());
274 box.resize( x + cellWidthS, y + cellHeightS, sbox.getMaxZ());
275 }
276 s.scale( sxy, sxy, 1f);
277
278 if( TRACE_LAYOUT ) {
279 System.err.println("bl("+i+").x: "+dxh+" / "+dyh+" -> "+s.getPosition()+", p3 "+shapeWidthS+" x "+shapeHeightS+", sz3 "+cellWidthS+" x "+cellHeightS+", box "+box.getWidth()+" x "+box.getHeight());
280 System.err.println("bl("+i+").x: "+s);
281 System.err.println("bl("+i+").x: "+box);
282 }
283 }
284 if( Float.isInfinite(box.getWidth()) || Float.isInfinite(box.getHeight()) ) {
285 box.resize(0, 0, 0);
286 }
287 if( TRACE_LAYOUT ) {
288 System.err.println("bl(X).x: "+box);
289 }
290 }
291
292 @Override
293 public String toString() {
294 final String p_s = ( null == padding || padding.zeroSize() ) ? "" : ", "+padding.toString();
295 final String m_s = margin.zeroSize() ? "" : ", "+margin.toString();
296 return "Box[cell "+cellSize+", a "+alignment+m_s+p_s+"]";
297 }
298}
299
Group of Shapes, optionally utilizing a Group.Layout.
Definition: Group.java:61
List< Shape > getShapes()
Returns added Shapes.
Definition: Group.java:219
Generic Shape, potentially using a Graph via GraphShape or other means of representing content.
Definition: Shape.java:87
final Shape move(final float dtx, final float dty, final float dtz)
Move about scaled distance.
Definition: Shape.java:557
final Vec3f getScale()
Returns scale Vec3f reference.
Definition: Shape.java:682
final Shape moveTo(final float tx, final float ty, final float tz)
Move to scaled position.
Definition: Shape.java:543
final Vec3f getPosition()
Returns position Vec3f reference, i.e.
Definition: Shape.java:587
final AABBox getBounds()
Returns the unscaled bounding AABBox for this shape, borrowing internal instance.
Definition: Shape.java:732
final Shape setPaddding(final Padding padding)
Sets the unscaled padding for this shape, which is included in unscaled getBounds() and also includes...
Definition: Shape.java:376
final void applyMatToMv(final PMVMatrix4f pmv)
Applies the internal Matrix4f to the given modelview matrix, i.e.
Definition: Shape.java:908
Immutable layout alignment options, including Bit#Fill.
Definition: Alignment.java:35
boolean isSet(final Bit bit)
Definition: Alignment.java:94
static final Alignment None
No alignment constant.
Definition: Alignment.java:37
GraphUI Stack Group.Layout.
Definition: BoxLayout.java:53
Margin getMargin()
Returns given scaled Margin.
Definition: BoxLayout.java:161
BoxLayout(final float cellWidth, final float cellHeight, final Padding padding)
Definition: BoxLayout.java:104
BoxLayout(final float cellWidth, final float cellHeight, final Margin margin, final Padding padding)
Definition: BoxLayout.java:115
Padding getPadding()
Returns given unscaled Padding, may be null if not given via constructor.
Definition: BoxLayout.java:163
BoxLayout(final float cellWidth, final float cellHeight, final Margin margin)
Definition: BoxLayout.java:94
Alignment getAlignment()
Returns given Alignment.
Definition: BoxLayout.java:159
void preValidate(final Shape s)
Prepare given Shape before validation, e.g.
Definition: BoxLayout.java:166
void layout(final Group g, final AABBox box, final PMVMatrix4f pmv)
Performing the layout of Group#getShapes(), called @ Shape#validate(GL2ES2) or Shape#validate(GLProfi...
Definition: BoxLayout.java:173
BoxLayout(final float cellWidth, final float cellHeight, final Alignment alignment, final Padding padding)
Definition: BoxLayout.java:136
BoxLayout(final float cellWidth, final float cellHeight, final Alignment alignment, final Margin margin, final Padding padding)
Definition: BoxLayout.java:148
BoxLayout(final float cellWidth, final float cellHeight, final Alignment alignment)
Definition: BoxLayout.java:84
BoxLayout(final Padding padding)
Definition: BoxLayout.java:74
BoxLayout(final float cellWidth, final float cellHeight, final Alignment alignment, final Margin margin)
Definition: BoxLayout.java:125
Vec2f getCellSize()
Returns the preset cell size.
Definition: BoxLayout.java:157
GraphUI CSS property Margin, scaled space between or around elements and not included in the element'...
Definition: Margin.java:41
final float left
Scaled left value.
Definition: Margin.java:52
float height()
Return scaled height of vertical values bottom + top.
Definition: Margin.java:112
final float bottom
Scaled bottom value.
Definition: Margin.java:50
static final Margin None
Zero margin constant.
Definition: Margin.java:43
float width()
Return scaled width of horizontal values top + right.
Definition: Margin.java:109
GraphUI CSS property Padding, unscaled space belonging to the element and included in the element's s...
Definition: Padding.java:38
Basic Float math utility functions.
Definition: FloatUtil.java:83
static boolean isZero(final float a, final float epsilon)
Returns true if value is zero, i.e.
2D Vector based upon two float components.
Definition: Vec2f.java:37
3D Vector based upon three float components.
Definition: Vec3f.java:37
Vec3f mul(final float val)
Returns this * val; creates new vector.
Definition: Vec3f.java:178
void setX(final float x)
Definition: Vec3f.java:158
Vec3f scale(final float s)
this = this * s, returns this.
Definition: Vec3f.java:218
void setZ(final float z)
Definition: Vec3f.java:160
void setY(final float y)
Definition: Vec3f.java:159
Vec3f min(final Vec3f m)
this = min(this, m), returns this.
Definition: Vec3f.java:170
Axis Aligned Bounding Box.
Definition: AABBox.java:54
final float getWidth()
Definition: AABBox.java:879
final Vec3f getLow()
Returns the minimum left-bottom-far (xyz) coordinate.
Definition: AABBox.java:140
final float getHeight()
Definition: AABBox.java:883
AABBox transform(final Matrix4f mat, final AABBox out)
Transform this box using the given Matrix4f into out @endiliteral.
Definition: AABBox.java:933
final AABBox resize(final AABBox newBox)
Resize the AABBox to encapsulate another AABox.
Definition: AABBox.java:274
PMVMatrix4f implements the basic computer graphics Matrix4f pack using projection (P),...
final Matrix4f getMv()
Returns the modelview matrix (Mv).
final PMVMatrix4f popMv()
Pop the modelview matrix from its stack.
final PMVMatrix4f pushMv()
Push the modelview matrix to its stack, while preserving its values.
CenterHoriz
Horizontal center alignment.
Definition: Alignment.java:64
CenterVert
Vertical center alignment.
Definition: Alignment.java:67
Fill
Scale object to parent size, e.g.
Definition: Alignment.java:61
Layout for the GraphUI Group, called @ Shape#validate(GL2ES2) or Shape#validate(GLProfile).
Definition: Group.java:63