Skip to content

Commit 953c2f4

Browse files
Copilotgonzalocasas
andcommitted
Add transform() method to handle scale components in Shape subclasses
Co-authored-by: gonzalocasas <933277+gonzalocasas@users.noreply.github.com>
1 parent 21a31b0 commit 953c2f4

7 files changed

Lines changed: 637 additions & 29 deletions

File tree

src/compas/geometry/shapes/box.py

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -529,35 +529,49 @@ def to_brep(self):
529529
# Transformations
530530
# ==========================================================================
531531

532-
# def transform(self, transformation):
533-
# """Transform the box.
534-
535-
# Parameters
536-
# ----------
537-
# transformation : :class:`Transformation`
538-
# The transformation used to transform the Box.
539-
540-
# Returns
541-
# -------
542-
# None
543-
544-
# Examples
545-
# --------
546-
# >>> box = Box(Frame.worldXY(), 1.0, 2.0, 3.0)
547-
# >>> frame = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15])
548-
# >>> T = Transformation.from_frame(frame)
549-
# >>> box.transform(T)
550-
551-
# """
552-
# self.frame.transform(transformation)
553-
# # Always local scaling, non-uniform scaling based on frame not yet considered.
554-
# Sc, _, _, _, _ = transformation.decomposed()
555-
# scalex = Sc[0, 0]
556-
# scaley = Sc[1, 1]
557-
# scalez = Sc[2, 2]
558-
# self.xsize *= scalex
559-
# self.ysize *= scaley
560-
# self.zsize *= scalez
532+
def transform(self, transformation):
533+
"""Transform the box.
534+
535+
Parameters
536+
----------
537+
transformation : :class:`Transformation`
538+
The transformation used to transform the Box.
539+
540+
Returns
541+
-------
542+
None
543+
544+
Examples
545+
--------
546+
>>> from compas.geometry import Frame, Transformation, Scale
547+
>>> box = Box(Frame.worldXY(), 1.0, 2.0, 3.0)
548+
>>> frame = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15])
549+
>>> T = Transformation.from_frame(frame)
550+
>>> box.transform(T)
551+
>>> S = Scale.from_factors([2.0, 3.0, 4.0])
552+
>>> box = Box(1.0, 2.0, 3.0)
553+
>>> box.transform(S)
554+
>>> box.xsize
555+
2.0
556+
>>> box.ysize
557+
6.0
558+
>>> box.zsize
559+
12.0
560+
561+
"""
562+
# Extract scale component from the transformation
563+
Sc, _, _, _, _ = transformation.decomposed()
564+
scale_x = Sc.matrix[0][0]
565+
scale_y = Sc.matrix[1][1]
566+
scale_z = Sc.matrix[2][2]
567+
568+
# Apply scaling to dimensions
569+
self.xsize *= scale_x
570+
self.ysize *= scale_y
571+
self.zsize *= scale_z
572+
573+
# Apply transformation to frame
574+
self.frame.transform(transformation)
561575

562576
def scale(self, x, y=None, z=None):
563577
"""Scale the box.

src/compas/geometry/shapes/capsule.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,46 @@ def compute_faces(self): # type: () -> list[list[int]]
332332
# Transformations
333333
# =============================================================================
334334

335+
def transform(self, transformation):
336+
"""Transform the capsule.
337+
338+
Parameters
339+
----------
340+
transformation : :class:`Transformation`
341+
The transformation used to transform the capsule.
342+
343+
Returns
344+
-------
345+
None
346+
347+
Examples
348+
--------
349+
>>> from compas.geometry import Frame, Scale
350+
>>> capsule = Capsule(radius=2.0, height=4.0)
351+
>>> S = Scale.from_factors([2.0, 2.0, 3.0])
352+
>>> capsule.transform(S)
353+
>>> capsule.radius
354+
4.0
355+
>>> capsule.height
356+
12.0
357+
358+
"""
359+
# Extract scale component from the transformation
360+
Sc, _, _, _, _ = transformation.decomposed()
361+
scale_x = Sc.matrix[0][0]
362+
scale_y = Sc.matrix[1][1]
363+
scale_z = Sc.matrix[2][2]
364+
365+
# For a capsule aligned with Z-axis:
366+
# - radius is affected by X and Y scaling (use average)
367+
# - height is affected by Z scaling
368+
average_radial_scale = (scale_x + scale_y) / 2.0
369+
self.radius *= average_radial_scale
370+
self.height *= scale_z
371+
372+
# Apply transformation to frame
373+
self.frame.transform(transformation)
374+
335375
def scale(self, factor):
336376
"""Scale the capsule.
337377

src/compas/geometry/shapes/cone.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,46 @@ def to_brep(self):
300300
# Transformations
301301
# ==========================================================================
302302

303+
def transform(self, transformation):
304+
"""Transform the cone.
305+
306+
Parameters
307+
----------
308+
transformation : :class:`Transformation`
309+
The transformation used to transform the cone.
310+
311+
Returns
312+
-------
313+
None
314+
315+
Examples
316+
--------
317+
>>> from compas.geometry import Frame, Scale
318+
>>> cone = Cone(radius=3.0, height=6.0)
319+
>>> S = Scale.from_factors([2.0, 2.0, 3.0])
320+
>>> cone.transform(S)
321+
>>> cone.radius
322+
6.0
323+
>>> cone.height
324+
18.0
325+
326+
"""
327+
# Extract scale component from the transformation
328+
Sc, _, _, _, _ = transformation.decomposed()
329+
scale_x = Sc.matrix[0][0]
330+
scale_y = Sc.matrix[1][1]
331+
scale_z = Sc.matrix[2][2]
332+
333+
# For a cone aligned with Z-axis:
334+
# - radius is affected by X and Y scaling (use average)
335+
# - height is affected by Z scaling
336+
average_radial_scale = (scale_x + scale_y) / 2.0
337+
self.radius *= average_radial_scale
338+
self.height *= scale_z
339+
340+
# Apply transformation to frame
341+
self.frame.transform(transformation)
342+
303343
# ==========================================================================
304344
# Methods
305345
# ==========================================================================

src/compas/geometry/shapes/cylinder.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,46 @@ def to_brep(self):
302302
# Transformations
303303
# =============================================================================
304304

305+
def transform(self, transformation):
306+
"""Transform the cylinder.
307+
308+
Parameters
309+
----------
310+
transformation : :class:`Transformation`
311+
The transformation used to transform the cylinder.
312+
313+
Returns
314+
-------
315+
None
316+
317+
Examples
318+
--------
319+
>>> from compas.geometry import Frame, Scale
320+
>>> cylinder = Cylinder(radius=2.0, height=5.0)
321+
>>> S = Scale.from_factors([2.0, 2.0, 3.0])
322+
>>> cylinder.transform(S)
323+
>>> cylinder.radius
324+
4.0
325+
>>> cylinder.height
326+
15.0
327+
328+
"""
329+
# Extract scale component from the transformation
330+
Sc, _, _, _, _ = transformation.decomposed()
331+
scale_x = Sc.matrix[0][0]
332+
scale_y = Sc.matrix[1][1]
333+
scale_z = Sc.matrix[2][2]
334+
335+
# For a cylinder aligned with Z-axis:
336+
# - radius is affected by X and Y scaling (use average)
337+
# - height is affected by Z scaling
338+
average_radial_scale = (scale_x + scale_y) / 2.0
339+
self.radius *= average_radial_scale
340+
self.height *= scale_z
341+
342+
# Apply transformation to frame
343+
self.frame.transform(transformation)
344+
305345
def scale(self, factor):
306346
"""Scale the cylinder by multiplying the radius and height by a factor.
307347

src/compas/geometry/shapes/sphere.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,42 @@ def to_brep(self):
265265
# Transformations
266266
# =============================================================================
267267

268+
def transform(self, transformation):
269+
"""Transform the sphere.
270+
271+
Parameters
272+
----------
273+
transformation : :class:`Transformation`
274+
The transformation used to transform the sphere.
275+
276+
Returns
277+
-------
278+
None
279+
280+
Examples
281+
--------
282+
>>> from compas.geometry import Frame, Transformation, Scale
283+
>>> sphere = Sphere(5.0)
284+
>>> S = Scale.from_factors([2.0, 2.0, 2.0])
285+
>>> sphere.transform(S)
286+
>>> sphere.radius
287+
10.0
288+
289+
"""
290+
# Extract scale component from the transformation
291+
Sc, _, _, _, _ = transformation.decomposed()
292+
scale_x = Sc.matrix[0][0]
293+
scale_y = Sc.matrix[1][1]
294+
scale_z = Sc.matrix[2][2]
295+
296+
# For a sphere, use the average of the three scale factors
297+
# to maintain the spherical shape
298+
average_scale = (scale_x + scale_y + scale_z) / 3.0
299+
self.radius *= average_scale
300+
301+
# Apply transformation to frame
302+
self.frame.transform(transformation)
303+
268304
def scale(self, factor):
269305
"""Scale the sphere.
270306

src/compas/geometry/shapes/torus.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,33 @@ def transform(self, transformation):
270270
--------
271271
>>> from compas.geometry import Frame
272272
>>> from compas.geometry import Transformation
273+
>>> from compas.geometry import Scale
273274
>>> from compas.geometry import Torus
274275
>>> torus = Torus(5, 2)
275276
>>> frame = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15])
276277
>>> T = Transformation.from_frame(frame)
277278
>>> torus.transform(T)
279+
>>> torus = Torus(5, 2)
280+
>>> S = Scale.from_factors([2.0, 2.0, 3.0])
281+
>>> torus.transform(S)
282+
>>> torus.radius_axis
283+
10.0
284+
>>> torus.radius_pipe
285+
6.0
278286
279287
"""
288+
# Extract scale component from the transformation
289+
Sc, _, _, _, _ = transformation.decomposed()
290+
scale_x = Sc.matrix[0][0]
291+
scale_y = Sc.matrix[1][1]
292+
scale_z = Sc.matrix[2][2]
293+
294+
# For a torus in the XY plane:
295+
# - axis radius is affected by X and Y scaling (use average)
296+
# - pipe radius is affected by Z scaling
297+
average_planar_scale = (scale_x + scale_y) / 2.0
298+
self.radius_axis *= average_planar_scale
299+
self.radius_pipe *= scale_z
300+
301+
# Apply transformation to frame
280302
self.frame.transform(transformation)

0 commit comments

Comments
 (0)