HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GU_Grid.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: GU_Grid.h
7  *
8  * COMMENTS:
9  *
10  */
11 
12 #pragma once
13 
14 #ifndef __GU_GRID__
15 #define __GU_GRID__
16 
17 #include "GU_API.h"
18 #include "GU_Types.h"
19 #include <GEO/GEO_SurfaceType.h>
20 #include <GA/GA_Types.h>
21 #include <UT/UT_Assert.h>
22 #include <UT/UT_UniquePtr.h>
23 #include <SYS/SYS_Types.h>
24 
26 {
27 public:
28  GU_GridParms();
29 
30  int rows, cols;
31  float xsize, ysize;
32  float xcenter, ycenter, zcenter;
35  int interpEndsU, interpEndsV;
36  int orderU, orderV;
37 };
38 
39 
40 template<typename INT_TYPE=GA_Offset>
41 struct GU_GridT
42 {
43  /// These are the number of edge rows and edge columns.
44  /// @{
47  /// @}
48 
49  /// These members specify the points of the grid
50  /// @{
51  INT_TYPE myCorners[4];
52  INT_TYPE myRow0Start;
53  union {
54  INT_TYPE myRow0Step;
55  INT_TYPE *myRow0Array;
56  };
57  INT_TYPE myRow1Start;
58  union {
59  INT_TYPE myRow1Step;
60  INT_TYPE *myRow1Array;
61  };
62  INT_TYPE myCol0Start;
63  union {
64  INT_TYPE myCol0Step;
65  INT_TYPE *myCol0Array;
66  };
67  INT_TYPE myCol1Start;
68  union {
69  INT_TYPE myCol1Step;
70  INT_TYPE *myCol1Array;
71  };
72  INT_TYPE myMiddleStart;
73  INT_TYPE myMiddleColStep;
74  INT_TYPE myMiddleRowStep;
75  /// @}
76 
78 
79  enum class PrimitiveType : unsigned char {
80  POLYGON,
81  MESH,
82  NURBS,
83  BEZIER,
84  POLYSOUP,
85  POINTS
86  };
87 
89 
90  /// NOTE: Basis orders for NURBS and Bezier surfaces must be in the range
91  /// [2,GA_MAX_ORDER], which fits in 4 bits, since GA_MAX_ORDER==11.
92  unsigned char myBasisOrderU:4;
93  unsigned char myBasisOrderV:4;
95 
96  /// If mySurfaceType is rows, cols, or rows and cols, and a particular
97  /// row or column wraps around (row0[col]==row1[col] or col0[row]==col1[row]),
98  /// This indicates to create an open curve with an extra vertex (if true),
99  /// instead of a closed polygon (if false).
101 
102  /// If mySurfaceType is rows or rows and cols, and columns aren't wrapped,
103  /// if this is true, the first row is present (the usual), else it's not
104  /// (e.g. the grid might be connected to an existing mesh).
106 
107  /// If mySurfaceType is rows or rows and cols, and columns aren't wrapped,
108  /// if this is true, the final row is present (the usual), else it's not
109  /// (e.g. the grid might be connected to an existing mesh).
111 
112  /// If mySurfaceType is cols or rows and cols, and rows aren't wrapped,
113  /// if this is true, the first col is present (the usual), else it's not
114  /// (e.g. the grid might be connected to an existing mesh).
116 
117  /// If mySurfaceType is cols or rows and cols, and rows aren't wrapped,
118  /// if this is true, the final col is present (the usual), else it's not
119  /// (e.g. the grid might be connected to an existing mesh).
121 
122  /// If adjacent points on the boundary are equal, and mySurfaceType is
123  /// one of the triangle options or quads, the appropriate polygons
124  /// will be simple triangles, instead of quads or pairs of triangles.
125  /// If a boundary quad becomes fully degenerate, no polygon will be output
126  /// for it.
128 
129  /// If myPrimitiveType is NURBS or BEZIER, and mySurfaceType is
130  /// GEO_PATCH_ROWS or GEO_PATCH_COLS or GEO_PATCH_ROWCOL, this
131  /// indicates whether to create separate curve primitives (true),
132  /// or a surface primitive with its surface type set correspondingly.
134 
135  /// NOTE: The following members are cached values computed from the members
136  /// above, not used for input.
137  /// After setting the other members, either precompute() must be
138  /// called or these members must be manually set, before any other
139  /// functions can be called.
140  /// @{
141  bool myAllWrapU:1;
142  bool myAllWrapV:1;
143  bool myNoWrapU:1;
144  bool myNoWrapV:1;
147  /// @}
148 
149  /// NOTE: The bools can't be in-class initialized, since they're bitfields,
150  /// and the C++ standard evidently missed the case of bitfields.
151  /// NOTE: The row and col starts are non-negative, so that destructing
152  /// a default constructed grid won't crash.
154  : myRow0Start(0)
155  , myRow1Start(0)
156  , myCol0Start(0)
157  , myCol1Start(0)
159  , myPrimitiveType(PrimitiveType::POLYGON)
160  , myUnrollCurves(false)
161  , myFirstRowIfNotWrapped(true)
162  , myLastRowIfNotWrapped(true)
163  , myFirstColIfNotWrapped(true)
164  , myLastColIfNotWrapped(true)
165  , myTriangularPoles(false)
166  , myCurvesIfBasisRowCol(false)
167  {}
168  ~GU_GridT() { clear(); }
169 
170  /// This computes myAllWrapU, myAllWrapV, myNoWrapU, and myNoWrapV
171  /// after everything else is set.
172  void precompute();
173 
174  /// NOTE: mySurfaceType & myPrimitiveType must be set before calling, and so should
175  /// myFirstRowIfNotWrapped, myLastRowIfNotWrapped, myFirstColIfNotWrapped,
176  /// and myLastColIfNotWrapped if rows/cols/rowcol.
177  /// NOTE: You don't need to call precompute() after this.
178  void initSingleGrid(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0));
179 
180  /// NOTE: mySurfaceType & myPrimitiveType must be set before calling, and so should
181  /// myFirstRowIfNotWrapped, myLastRowIfNotWrapped,
182  /// and myUnrollCurves if rows/cols/rowcol.
183  /// NOTE: You don't need to call precompute() after this.
184  void initColTube(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0));
185 
186  /// NOTE: mySurfaceType & myPrimitiveType must be set before calling, and so should
187  /// myFirstColIfNotWrapped, myLastColIfNotWrapped,
188  /// and myUnrollCurves if rows/cols/rowcol.
189  /// NOTE: You don't need to call precompute() after this.
190  void initRowTube(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0));
191 
192  /// This is the sphere most people expect, with north and south poles (row=0/nedgerows).
193  /// NOTE: mySurfaceType & myPrimitiveType must be set before calling, and so should
194  /// myFirstRowIfNotWrapped, myLastRowIfNotWrapped,
195  /// and myUnrollCurves if rows/cols/rowcol, or
196  /// myTriangularPoles if quads/tris.
197  /// NOTE: You'll probably want to set myFirstRowIfNotWrapped and
198  /// myLastRowIfNotWrapped to false, to match the Sphere SOP.
199  /// NOTE: You don't need to call precompute() after this.
200  void initColSphere(exint nedgerows, exint nedgecols,
201  INT_TYPE start_pt=INT_TYPE(0), INT_TYPE end_pt=INT_TYPE(1),
202  INT_TYPE start_mid_pt=INT_TYPE(2));
203 
204  /// This is the sphere most people DON'T expect, with east and west poles (col=0/nedgecols).
205  /// NOTE: mySurfaceType & myPrimitiveType must be set before calling, and so should
206  /// myFirstColIfNotWrapped, myLastColIfNotWrapped,
207  /// and myUnrollCurves if rows/cols/rowcol, or
208  /// myTriangularPoles if quads/tris.
209  /// NOTE: You'll probably want to set myFirstColIfNotWrapped and
210  /// myLastColIfNotWrapped to false, to match the Sphere SOP.
211  /// NOTE: You don't need to call precompute() after this.
212  void initRowSphere(exint nedgerows, exint nedgecols,
213  INT_TYPE start_pt=INT_TYPE(0), INT_TYPE end_pt=INT_TYPE(1),
214  INT_TYPE start_mid_pt=INT_TYPE(2));
215 
216  /// This is effectively a slice of a row sphere, with north and south poles (col=0/nedgecols).
217  /// NOTE: mySurfaceType & myPrimitiveType must be set before calling, and so should
218  /// myFirstRowIfNotWrapped, myLastRowIfNotWrapped,
219  /// and myUnrollCurves if rows/cols/rowcol, or
220  /// myTriangularPoles if quads/tris.
221  /// NOTE: You'll probably want to set myFirstRowIfNotWrapped and
222  /// myLastRowIfNotWrapped to false, to match the Sphere SOP.
223  /// NOTE: You don't need to call precompute() after this.
224  void initSplitRowSphere(exint nedgerows, exint nedgecols,
225  INT_TYPE start_pt = INT_TYPE(0), INT_TYPE end_pt = INT_TYPE(1),
226  INT_TYPE start_mid_pt = INT_TYPE(2));
227 
228  /// This is effectively a slice of a col sphere, with north and south poles (row=0/nedgerows).
229  /// NOTE: mySurfaceType & myPrimitiveType must be set before calling, and so should
230  /// myFirstRowIfNotWrapped, myLastRowIfNotWrapped,
231  /// and myUnrollCurves if rows/cols/rowcol, or
232  /// myTriangularPoles if quads/tris.
233  /// NOTE: You'll probably want to set myFirstRowIfNotWrapped and
234  /// myLastRowIfNotWrapped to false, to match the Sphere SOP.
235  /// NOTE: You don't need to call precompute() after this.
236  void initSplitColSphere(exint nedgerows, exint nedgecols,
237  INT_TYPE start_pt=INT_TYPE(0), INT_TYPE end_pt=INT_TYPE(1),
238  INT_TYPE start_mid_pt=INT_TYPE(2));
239 
240  /// NOTE: mySurfaceType & myPrimitiveType must be set before calling, and so should
241  /// myUnrollCurves if rows/cols/rowcol.
242  /// NOTE: You don't need to call precompute() after this.
243  void initTorus(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0));
244 
245  /// NOTE: mySurfaceType & myPrimitiveType must be set before calling, and so should
246  /// myFirstRowIfNotWrapped, myLastRowIfNotWrapped,
247  /// and myUnrollCurves if rows/cols/rowcol, or
248  /// myTriangularPoles if quads/tris.
249  /// NOTE: You'll probably want to set myFirstRowIfNotWrapped or
250  /// myLastRowIfNotWrapped to false, depending on row0_pole.
251  /// NOTE: You don't need to call precompute() after this.
252  void initColCone(exint nedgerows, exint nedgecols, bool row0_pole=true,
253  INT_TYPE start_pt=INT_TYPE(0), INT_TYPE start_mid_pt=INT_TYPE(1));
254 
255  /// Free memory allocations owned by this.
256  /// NOTE: This intentionally does not clear myPrimitiveType,
257  /// mySurfaceType, myBasisOrderU, myBasisOrderV, or myUnrollCurves.
258  void clear()
259  {
260  if (myRow0Start < INT_TYPE(0))
261  {
262  delete [] myRow0Array;
263  myRow0Start = INT_TYPE(0);
264  }
265  if (myRow1Start < INT_TYPE(0))
266  {
267  delete [] myRow1Array;
268  myRow1Start = INT_TYPE(0);
269  }
270  if (myCol0Start < INT_TYPE(0))
271  {
272  delete [] myCol0Array;
273  myCol0Start = INT_TYPE(0);
274  }
275  if (myCol1Start < INT_TYPE(0))
276  {
277  delete [] myCol1Array;
278  myCol1Start = INT_TYPE(0);
279  }
280  }
281 
282  INT_TYPE getPoint(exint row, exint col) const
283  {
284  UT_ASSERT_P(row >= 0 && row <= myNumEdgeRows);
285  UT_ASSERT_P(col >= 0 && col <= myNumEdgeCols);
286  if (row == 0)
287  {
288  if (col == 0)
289  return myCorners[0];
290  if (col == myNumEdgeCols)
291  return myCorners[1];
292  --col;
293  return (myRow0Start < INT_TYPE(0)) ? myRow0Array[col] : (myRow0Start + myRow0Step*col);
294  }
295  if (row == myNumEdgeRows)
296  {
297  if (col == 0)
298  return myCorners[2];
299  if (col == myNumEdgeCols)
300  return myCorners[3];
301  --col;
302  return (myRow1Start < INT_TYPE(0)) ? myRow1Array[col] : (myRow1Start + myRow1Step*col);
303  }
304  --row;
305  if (col == 0)
306  return (myCol0Start < INT_TYPE(0)) ? myCol0Array[row] : (myCol0Start + myCol0Step*row);
307  if (col == myNumEdgeCols)
308  return (myCol1Start < INT_TYPE(0)) ? myCol1Array[row] : (myCol1Start + myCol1Step*row);
309  --col;
310  return myMiddleStart + myMiddleRowStep*row + myMiddleColStep*col;
311  }
312 
313  bool doesRowWrap(exint row) const
314  {
315  if (row == 0)
316  return myCorners[0] == myCorners[1];
317  if (row == myNumEdgeRows)
318  return myCorners[2] == myCorners[3];
319  UT_ASSERT_P(row > 0 && row < myNumEdgeRows);
320  --row;
321  INT_TYPE pt0 = (myCol0Start < INT_TYPE(0)) ? myCol0Array[row] : (myCol0Start + myCol0Step*row);
322  INT_TYPE pt1 = (myCol1Start < INT_TYPE(0)) ? myCol1Array[row] : (myCol1Start + myCol1Step*row);
323  return pt0 == pt1;
324  }
325  bool doesColWrap(exint col) const
326  {
327  if (col == 0)
328  return myCorners[0] == myCorners[2];
329  if (col == myNumEdgeCols)
330  return myCorners[1] == myCorners[3];
331  UT_ASSERT_P(col > 0 && col < myNumEdgeCols);
332  --col;
333  INT_TYPE pt0 = (myRow0Start < INT_TYPE(0)) ? myRow0Array[col] : (myRow0Start + myRow0Step*col);
334  INT_TYPE pt1 = (myRow1Start < INT_TYPE(0)) ? myRow1Array[col] : (myRow1Start + myRow1Step*col);
335  return pt0 == pt1;
336  }
337 
338  // Adjusts the points of the grid such that the effective starting point is different.
339  void shiftPoints(INT_TYPE shift)
340  {
341  for (int i = 0; i < 4; ++i)
342  myCorners[i] += shift;
343 
344  if (myRow0Start >= INT_TYPE(0))
345  myRow0Start += shift;
346  else
347  {
348  for (exint col = 1; col < myNumEdgeCols; ++col)
349  myRow0Array[col-1] += shift;
350  }
351  if (myRow1Start >= INT_TYPE(0))
352  myRow1Start += shift;
353  else
354  {
355  for (exint col = 1; col < myNumEdgeCols; ++col)
356  myRow1Array[col-1] += shift;
357  }
358  if (myCol0Start >= INT_TYPE(0))
359  myCol0Start += shift;
360  else
361  {
362  for (exint row = 1; row < myNumEdgeRows; ++row)
363  myCol0Array[row-1] += shift;
364  }
365  if (myCol1Start >= INT_TYPE(0))
366  myCol1Start += shift;
367  else
368  {
369  for (exint row = 1; row < myNumEdgeRows; ++row)
370  myCol1Array[row-1] += shift;
371  }
372  myMiddleStart += shift;
373  }
374 
375  bool isInvalidTPSurf() const
376  {
377  const bool is_nurbs = (myPrimitiveType == PrimitiveType::NURBS);
378  const bool is_bezier = (myPrimitiveType == PrimitiveType::BEZIER);
379 
380  if (!is_nurbs && !is_bezier)
381  return false;
382 
383  // If number of row vertices or col vertices is smaller than
384  // the corresponding basis order, reject this grid.
385  // If we are wrapped, our basis should be wrapped, so we also
386  // need to include the final point.
387  exint nvtxrows = myNumEdgeRows + 1; //exint(myNoWrapV || myUnrollCurves);
388  exint nvtxcols = myNumEdgeCols + 1; //exint(myNoWrapU || myUnrollCurves);
389  if (nvtxrows < myBasisOrderV || nvtxcols < myBasisOrderU)
390  return true;
391  // Beziers need the edge count along an axis to be a multiple of (order-1)
392  if (is_bezier && ((myNumEdgeRows % (myBasisOrderV-1)) != 0 || (myNumEdgeCols % (myBasisOrderU-1)) != 0))
393  return true;
394  return false;
395  }
396 
397  void fixTPSurf()
398  {
399  const bool is_nurbs = (myPrimitiveType == PrimitiveType::NURBS);
400  const bool is_bezier = (myPrimitiveType == PrimitiveType::BEZIER);
401  if (!is_nurbs && !is_bezier)
402  return;
403 
404  // Order must be between 2 and GA_MAX_ORDER (inclusive).
407 
408  // If number of row vertices or col vertices is smaller than
409  // the corresponding basis order, increase it.
410  // If we are wrapped, our basis should be wrapped, so we also
411  // need to include the final point.
412  const exint nvtxrows = myNumEdgeRows + 1; //exint(myNoWrapV || myUnrollCurves);
413  const exint nvtxcols = myNumEdgeCols + 1; //exint(myNoWrapU || myUnrollCurves);
414  if (nvtxrows < myBasisOrderV)
415  myNumEdgeRows += myBasisOrderV-nvtxrows;
416  if (nvtxcols < myBasisOrderU)
417  myNumEdgeCols += myBasisOrderU-nvtxcols;
418  if (is_bezier)
419  {
420  // Beziers need the edge count along an axis to be a multiple of (order-1)
421  const exint row_excess = myNumEdgeRows % (myBasisOrderV-1);
422  const exint col_excess = myNumEdgeCols % (myBasisOrderU-1);
423  if (row_excess != 0)
424  myNumEdgeRows += (myBasisOrderV-1) - row_excess;
425  if (col_excess != 0)
426  myNumEdgeCols += (myBasisOrderU-1) - col_excess;
427  }
428  }
429 };
430 
432 
433 #endif
void initColTube(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0))
Definition: GU_GridImpl.h:1184
INT_TYPE myMiddleRowStep
Definition: GU_Grid.h:74
INT_TYPE myCol0Step
Definition: GU_Grid.h:64
GU_GridT()
Definition: GU_Grid.h:153
unsigned char myBasisOrderU
Definition: GU_Grid.h:92
#define GA_MAX_ORDER
Definition: GA_Types.h:185
INT_TYPE myRow1Start
Definition: GU_Grid.h:57
bool myNoWrapU
Definition: GU_Grid.h:143
bool myFirstRowIfNotWrapped
Definition: GU_Grid.h:105
int64 exint
Definition: SYS_Types.h:125
bool myCurvesIfBasisRowCol
Definition: GU_Grid.h:133
INT_TYPE myRow0Step
Definition: GU_Grid.h:54
INT_TYPE * myCol1Array
Definition: GU_Grid.h:70
bool myFirstColIfNotWrapped
Definition: GU_Grid.h:115
void initColCone(exint nedgerows, exint nedgecols, bool row0_pole=true, INT_TYPE start_pt=INT_TYPE(0), INT_TYPE start_mid_pt=INT_TYPE(1))
exint myNumEdgeCols
Definition: GU_Grid.h:46
GEO_SurfaceType type
Definition: GU_Grid.h:34
INT_TYPE * myCol0Array
Definition: GU_Grid.h:65
int interpEndsV
Definition: GU_Grid.h:35
INT_TYPE * myRow0Array
Definition: GU_Grid.h:55
INT_TYPE myRow0Start
Definition: GU_Grid.h:52
bool myTriangularPoles
Definition: GU_Grid.h:127
INT_TYPE myCorners[4]
Definition: GU_Grid.h:51
INT_TYPE myCol1Step
Definition: GU_Grid.h:69
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:152
bool myAllWrapU
Definition: GU_Grid.h:141
GU_OrientationType plane
Definition: GU_Grid.h:33
UT_Vector3T< T > SYSclamp(const UT_Vector3T< T > &v, const UT_Vector3T< T > &min, const UT_Vector3T< T > &max)
Definition: UT_Vector3.h:1039
void clear()
Definition: GU_Grid.h:258
bool myUnrollCurves
Definition: GU_Grid.h:100
bool isInvalidTPSurf() const
Definition: GU_Grid.h:375
INT_TYPE * myRow1Array
Definition: GU_Grid.h:60
void precompute()
Definition: GU_GridImpl.h:1006
#define GU_API
Definition: GU_API.h:14
INT_TYPE myRow1Step
Definition: GU_Grid.h:59
void initColSphere(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0), INT_TYPE end_pt=INT_TYPE(1), INT_TYPE start_mid_pt=INT_TYPE(2))
Definition: GU_GridImpl.h:1363
GEO_SurfaceType mySurfaceType
Definition: GU_Grid.h:77
void shiftPoints(INT_TYPE shift)
Definition: GU_Grid.h:339
GU_OrientationType
Definition: GU_Types.h:32
bool myLastRowIfNotWrapped
Definition: GU_Grid.h:110
exint myNumVertices
Definition: GU_Grid.h:146
INT_TYPE myCol0Start
Definition: GU_Grid.h:62
INT_TYPE getPoint(exint row, exint col) const
Definition: GU_Grid.h:282
INT_TYPE myMiddleStart
Definition: GU_Grid.h:72
bool myLastColIfNotWrapped
Definition: GU_Grid.h:120
~GU_GridT()
Definition: GU_Grid.h:168
void initSplitRowSphere(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0), INT_TYPE end_pt=INT_TYPE(1), INT_TYPE start_mid_pt=INT_TYPE(2))
Definition: GU_GridImpl.h:1545
void fixTPSurf()
Definition: GU_Grid.h:397
bool myNoWrapV
Definition: GU_Grid.h:144
exint myNumEdgeRows
Definition: GU_Grid.h:45
bool doesRowWrap(exint row) const
Definition: GU_Grid.h:313
GLenum GLenum void * row
Definition: glew.h:4995
int orderV
Definition: GU_Grid.h:36
INT_TYPE myCol1Start
Definition: GU_Grid.h:67
GEO_SurfaceType
float ysize
Definition: GU_Grid.h:31
bool myAllWrapV
Definition: GU_Grid.h:142
bool doesColWrap(exint col) const
Definition: GU_Grid.h:325
exint myNumPrimitives
Definition: GU_Grid.h:145
PrimitiveType
Definition: GU_Grid.h:79
float zcenter
Definition: GU_Grid.h:32
void initRowSphere(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0), INT_TYPE end_pt=INT_TYPE(1), INT_TYPE start_mid_pt=INT_TYPE(2))
Definition: GU_GridImpl.h:1454
INT_TYPE myMiddleColStep
Definition: GU_Grid.h:73
PrimitiveType myPrimitiveType
Definition: GU_Grid.h:88
void initRowTube(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0))
Definition: GU_GridImpl.h:1273
void initTorus(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0))
Definition: GU_GridImpl.h:1744
int rows
Definition: GU_Grid.h:30
unsigned char myBasisOrderV
Definition: GU_Grid.h:93
void initSingleGrid(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0))
Definition: GU_GridImpl.h:1094
SYS_STATIC_ASSERT(GA_MAX_ORDER< 16)
void initSplitColSphere(exint nedgerows, exint nedgecols, INT_TYPE start_pt=INT_TYPE(0), INT_TYPE end_pt=INT_TYPE(1), INT_TYPE start_mid_pt=INT_TYPE(2))
Definition: GU_GridImpl.h:1653