How to scale using API?

I am developing a plugin where user input a scale . The scale amount is used to scale a ComponentInstance object. The object can be moved around. How can I scale the object uni-formally at the center ?
I used following code but it places the object at center after scaling transformation even if the object is not placed at origin

    scale_transform = Geom::Transformation.scaling(value)
    entity.transformation = scale_transform
1 Like

If you merely set the component transformation to the newly created transformation you lose the old transformation (the component position, scaling, rotation, shearing etc).

To also keep the old transformation you need to multiply it with the new transformation. This will scale the component around its axes.

scale_transform = Geom::Transformation.scaling(value)
entity.transformation *= scale_transform

Some trivia:

*= is a shorthand for assigning a variable to the current value multiples by another value. Writing a *= 5 is the same as writing a = a * 5.

Another thing worth remembering is that transformation (matrix) multiplication is non communitive, meaning transformation_a * transformation_b isn’t the same as transformation_b * transformation_a except for some trivial cases. If you for instance move something in a certain direction and then rotate around the origin you get a different result compared to first rotating around the origin, and then move in that direction.

1 Like

And finally, to scale about the center you’d need something like this.

Note that scale_transformation is relative the components internal coordinate system, not the model coordinates. This is why the center of the bounding box of the definition is used rather than the center point of the bounding box of the component instance.

center = entity.definition.bounds.center
scale_transform = Geom::Transformation.scaling(center, value)
entity.transformation *= scale_transform
1 Like

Thanks @eneroth3 . Really helped me to understand the concept.
I need to scale it to particular value but multiplying will create different effect.
To scale it to particular value i should change following to

scale_transform = Geom::Transformation.scaling(center, new_scale/previous_scale)

How could i get the previous scale from the componentInstance or should i store it in some variable ?

Simply dividing by a number representing the previous scale wont do since the component could be scaled differently in different axes.

What is needed is to extract and remove the scaling from a transformation matrix, which is a bit more complicated than the previous code example as the scaling, rotation and shearing are all baked into one object.

However I’ve been working for some time on a library that among other things help developers with transformations. The library isn’t finished yet but this can be seen as a sneak preview of what is to come.

module TransformationHelper

  # Create transformation from origin point and axes vectors.
  #
  # Unlike native +Geom::Transformation.axes+ this method does not make the axes
  # orthogonal or normalize them but uses them as they are, allowing for scaled
  # and sheared transformations.
  #
  # @param origin [Geom::Point3d]
  # @param xaxis [Geom::Vector3d]
  # @param yaxis [Geom::Vector3d]
  # @param zaxis [Geom::Vector3d]
  #
  # @example
  #   # Skew Selected Group/Component
  #   # Select a group or component and run:
  #   e = Sketchup.active_model.selection.first
  #   e.transformation = SUCommunityLib::LGeom::LTransformation.create_from_axes(
  #     ORIGIN,
  #     Geom::Vector3d.new(2, 0.3, 0.3),
  #     Geom::Vector3d.new(0.3, 2, 0.3),
  #     Geom::Vector3d.new(0.3, 0.3, 2)
  #   )
  #
  # @raise [ArgumentError] if any of the provided axes are parallel.
  # @raise [ArgumentError] if any of the vectors are zero length.
  #
  # @return [Geom::Transformation]
  def self.create_from_axes(origin = ORIGIN, xaxis = ZAXIS, yaxis = YAXIS, zaxis = XAXIS)
    unless [xaxis, yaxis, zaxis].all?(&:valid?)
      raise ArgumentError, "Axes must not be zero length."
    end
    if xaxis.parallel?(yaxis) || yaxis.parallel?(zaxis) || zaxis.parallel?(xaxis)
      raise ArgumentError, "Axes must not be parallel."
    end

    Geom::Transformation.new([
      xaxis.x,  xaxis.y,  xaxis.z,  0,
      yaxis.x,  yaxis.y,  yaxis.z,  0,
      zaxis.x,  zaxis.y,  zaxis.z,  0,
      origin.x, origin.y, origin.z, 1
    ])
  end

  # Calculate determinant of 3X3 matrix.
  #
  # @param transformation [Geom::Transformation]
  #
  # @return [Float]
  def self.determinant(transformation)
    xaxis(transformation) % (yaxis(transformation) * zaxis(transformation))
  end

  # Test if transformation is flipped (mirrored).
  #
  # @param transformation [Geom::Transformation]
  #
  # @return [Boolean]
  def self.flipped?(transformation)
    determinant(transformation) < 0
  end

  # Return new transformation with scaling removed.
  #
  # All axes of the new transformation have the length 1, meaning that if the
  # transformation was sheared it will still scale volumes, areas and length not
  # parallel to coordinate axes.
  #
  # If transformation is flipped, the X axis is reversed. Otherwise axes keeps
  # their direction.
  #
  # @param transformation [Geom::Transformation]
  #
  # @example
  #   # Mimic Context Menu > Reset Scale
  #   # Note that native Reset Scale also resets skew, not just scale.
  #   # Select a skewed group or component and run:
  #   e = Sketchup.active_model.selection.first
  #   e.transformation = SUCommunityLib::LGeom::LTransformation.reset_scaling(
  #     SUCommunityLib::LGeom::LTransformation.reset_shearing(e.transformation, false)
  #   )
  #
  # @return [Geom::Transformation]
  def self.reset_scaling(transformation)
    x_axis = xaxis(transformation).normalize
    x_axis.reverse! if flipped?(transformation)
    create_from_axes(
      transformation.origin,
      x_axis,
      yaxis(transformation).normalize,
      zaxis(transformation).normalize
    )
  end

  # Get the X axis vector of a transformation.
  #
  #
  # Unlike native +Transformation#xaxis+ the length of this axis isn't normalized
  # but resamples the scaling along the axis.
  #
  # @param transformation [Geom::Transformation]
  #
  # @return [Geom::Vector3d]
  def self.xaxis(transformation)
    Geom::Vector3d.new(transformation.to_a.values_at(0..2))
  end

  # Get the Y axis vector of a transformation.
  #
  #
  # Unlike native +Transformation#yaxis+ the length of this axis isn't normalized
  # but resamples the scaling along the axis.
  #
  # @param transformation [Geom::Transformation]
  #
  # @return [Geom::Vector3d]
  def self.yaxis(transformation)
    Geom::Vector3d.new(transformation.to_a.values_at(4..6))
  end

  # Get the Z axis vector of a transformation.
  #
  # Unlike native +Transformation#zaxis+ the length of this axis isn't normalized
  # but resamples the scaling along the axis.
  # @param transformation [Geom::Transformation]
  #
  # @return [Geom::Vector3d]
  def self.zaxis(transformation)
    Geom::Vector3d.new(transformation.to_a.values_at(8..10))
  end
  
end

With this module available you could use the following code to set the scale to a specific value:

scale_transform = Geom::Transformation.scaling(value)
entity.transformation = TransformationHelper.reset_scaling(e.transformation) * scale_transform

However scaling will be around the component axes, not bounding box center. Even if combined with my previous code that scales around another point the reset_scaling method scales around the origin.

Anyhow, what is the reason you want to scale around the bounding box center anyway. I’ve found that scaling around axes, along with positioning the axes wisely in the first place, often gives better results. If you for instance scale a chair you don’t want it to intersect the floor or float in mid air. If you place the axes on the bottom center of the chair you can scale it around its axes and it will stay in a reasonable place in space.

Some more trivia:

If you want to get a number representing the overall scale factor of the transformation, you can use the determinant method provided above. This is the scale factor for the component volume. If the transformation is uniform the length scale factor for each axis is the cubic root of this number. However it is safest not to assume the scaling is uniform.

I am developing a plugin for architectures where they can upload 3d model on my server and then can check out the experience in Gear Virtual Reality(VR). They can walk around in the house in VR.
For this I want to take input from user on the relative scale of person that can walk in the house. Like this

http://recordit.co/hm42781ikb

That person can be moved around . Final position of the person is the starting point for VR.
So i started with plugin development by adding a 3d model of person and landed into scaling issue.
If you can have some other solution in mind it will be really helpful.

I’m not entirely sure what you are doing but to me it seems more logical to scale the world into the right size than scaling the person (unless what you want to achieve is for people of different heights to use the VR thingy).

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.