scaling a three.js geometry in one direction without adjusting the mesh's position in place

Our goal is to create "edit handles" that will enable users to resize a (projected) plane by dragging from one of 8 positions (4 corners + 4 edges). This resizing should only scale/stretch the surface in the direction of the drag. However, the default scale mechanism in three.js does not meet our requirements as it scales in two opposite directions simultaneously, similar to what can be observed in this example.

I have done extensive research and experimentation, particularly focusing on these two discussions:

  • Scaling a Three.js geometry only up
  • Scaling a geometry in one direction with top as reference in threejs

Following these investigations, I arrived at the following solution:

// preparing to resize into -x direction aka "left edge"
// moving the geometry center to the right edge
mesh.geometry.translate(width/2, 0, 0);
mesh.geometry.scale(scaleX, 1, 1);
// moving the geometry center back to the middle 
// as more resizing might happen, into another direction, as per comment here: 
// https://stackoverflow.com/questions/26817717#comment74469004_26818522
mesh.geometry.translate(-(width/2 * scaleX), 0, 0);

However, the outcome was not as expected: The scaling occurs in two opposite directions again, resulting in the translations canceling each other out. It appears that either the scaling does not occur while the center/point of origin is placed on the right edge, or there may be a misconception on my part.

In contrast, the following approach yielded successful results:

mesh.scale.set(scaleX, 1, 1);
mesh.position.x = (-width / 2);

Although effective, continually resetting the mesh position feels like a hacky solution. Additionally, there are concerns about performance implications, especially regarding the frequent application of transformations during every frame, as discussed in this thread: Three.js - Scale model with scale.set() or increase model size?. It has been suggested that transforming the underlying geometry could potentially offer better performance benefits.

I also explored the source code of the TransformControls, but given its limitations and my incomplete understanding of quaternions, the concept remains elusive to me.

Thus, my inquiry is as follows: Is there a method to achieve unidirectional resizing without the need for continuously adjusting the mesh's position?

Answer №1

There are two ways to approach the issue at hand, as you have discovered.

  1. Adjust the geometry scale.
  2. Modify the Mesh scale and adjust its central position.

Let's first delve into option 1, which I do not recommend for reasons that will become apparent shortly.

To linearly scale geometry in a single direction, every vertex must be moved in accordance with the scaling factor. This entails calculating the new position of each vertex based on the amount of scaling it will undergo (with vertices nearest to the scaling side moving the most, those in the middle moving less, and vertices opposite the scaling side experiencing minimal movement). When performing live scaling, this can result in a substantial number of calculations.

Now, consider option 2: Altering the matrix of the entire mesh, including its new position. Once this matrix is sent to the GPU, the GPU takes care of the rest. Given the GPU's prowess in mathematical operations, particularly matrix mathematics, compared to JavaScript, the process of manipulating matrices poses no challenge to the GPU.

In addition, having the transformation matrix in place allows for obtaining the world positions of the vertices:

vertexCopy.set(myMesh.geometry.attributes.position.getX(index),
    myMesh.geometry.attributes.position.getY(index).
    myMesh.geometry.attributes.position.getZ(index));
myMesh.localToWorld(vertexCopy);

Summary:

Manipulating geometry could be considered even more "hacky" than manipulating the transformation matrix. It's akin to dictating how each pixel of a square should be drawn, rather than simply instructing to draw a square.

  1. Updating the geometry incurs significant computational costs. Despite "baking" the vertices afterward, any subsequent manipulation will trigger further computations.

  2. Updating the mesh's matrix proves straightforward and shifts the computational complexity to the adept GPU, capable of processing such information seamlessly.

You are free to choose the method aligned with your requirements, but based on the details provided, I highly recommend opting for option 2.

An alternative suggestion:

You might explore utilizing Morph Targets to achieve your objective, although my experience in this area is limited. Here's an example of morphing altering a cube for reference. Employing multiple morph targets would be necessary to attain the desired effect, yet this approach likely surpasses direct geometry manipulation given that morph manipulations happen within the GPU environment.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The identification number is not used to update Mongo DB

When attempting to use the MongoDB driver in Node.js to update a document, I encountered an issue where the log indicated that the update was successful, but the data did not reflect the changes. Specifically, despite trying to update the document using it ...

React-file-viewer shrinks any document to a compact size

I have been searching extensively for information on how to adjust file sizing in react-file-viewer without any success. My objective is to utilize the react-file-viewer to allow users to click on a filename hyperlink and open the file (be it an image, do ...

Tips for enabling or disabling elements within an array using React JS

I am looking to develop a feature where I can toggle individual boxes on and off by clicking on them. Currently, only one box at a time can be activated (displayed in green), but I want the ability to control each box independently without affecting the ot ...

When using `this.array.find` within methods or computed properties in Vue, it sometimes returns

Within a component, I am receiving an array called approvalOptions from getters that contains a certain value I need to find. I have attempted the following methods: Using it within methods (read from ...mapGetters) as shown below methods: { approva ...

What is the best way to extract text following a particular symbol in javascript or Jquery?

If I have a string that reads and the cow went @moo, and my goal is to exclusively choose "moo"... what steps should I take? ...

How to Properly Implement app.use() in Express for Middleware

What is the reason behind some middleware functions being passed in with invocation parentheses, while an anonymous function is passed in without being invoked? app.use(logger()); app.use(bodyParser()); If logger() is evaluated immediately and the return ...

Retrieve the Span class value contained within a Select Option

I realize it may appear repetitive, but it is not. This is the appearance of my select: <select id="avpMy4Y7E4cH_-iIRmmAK6-2GsEOu6Sjr-0-0"> <option value="1Ltr [ <span class=money>Rs.1,495.00</span> ]">...< ...

Easy-peasy AJAX bookmarking

One of my tasks involves displaying a list of items on a webpage with the use of PHP. I am looking to incorporate a straightforward AJAX toggle feature that will allow users to bookmark an item from the list while they are exploring. If the item's bo ...

Exploring the Universe: Modify the hue of a celestial body and showcase information upon hoveringokableCall

I have been working on developing a simulation that shows the positions of 4673 of the nearest galaxies. In this simulation, each galaxy is represented as a point. My goal is to change the color of a point when the mouse hovers over it and display the na ...

Angular sorting - Strings with undefined values are placed at the end

I am looking to understand how to effectively utilize the Angular orderBy filter with a custom sorting function that places undefined values at the end. For numeric sorting, my code looks like this: <tr ng-repeat="item in items | handleEmptyValues(sor ...

When the add button is clicked, a new radio button will be added along with the value entered in

Hello everyone, I'm working on a form that needs to display multiple radio buttons. Initially, I have set up one radio button with text and an option to add more buttons. Users can add multiple radio buttons by clicking on the "Add" button according t ...

Guide to correctly passing custom parameters along with the event object to an asynchronous form submission handler

Asking for guidance on defining and typing custom parameters alongside the native event object in an async onSubmitHandler for a form. The current implementation only receives the native event as a single parameter: const onSubmitHandler: FormEventHa ...

Determine the byte size of the ImageData Object

Snippet: // Generate a blank canvas let canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 100; document.body.appendChild(canvas); // Access the drawing context let ctx = canvas.getContext('2d'); // Extrac ...

Are there any event triggers for History.pushstate?

My Google-fu is failing me. Consider the following code snippet: var stateObj = { state: "some state" }; history.pushState(stateObj, "page 2", "other.htm"); Does this trigger a window callback? I am aware of the following code: window.onpopstate = fun ...

Sending Node.js FileStream Image to HTML5 FileReader API

Is there a way to utilize Node FileSystem for opening a file and then having it read by the FileReader API? const myFile = "C:\\Users\\Me\\Image.png"; fs.readFile(myFile, (error, data) => { const blob = new B ...

Tinymce editor does not display icons as expected

I am attempting to create a custom styled list that is editable with tinymce. The list-items are using Material-Check-Icons as bullet-points, which are added as css-pseudo-elements ::before. This setup works well, but when I integrate tinymce (v5) into the ...

Guide to successfully integrate MathJax into your React application

I developed an application using HTML that successfully rendered MathJax equations through a script tag. However, after transitioning to React, the MathJax equations are no longer appearing. I have tried adding the MathJax script (shown below) in the comp ...

Obtain the jQuery dialog's closure event within the $.Ajax completion function

I have developed a custom jQuery plugin that utilizes jQuery Dialog to showcase messages. Specifically, I am using it within my jQuery $.ajax -->done function. My goal is to capture the close event of the Dialog in the .ajax function so that I can redir ...

How can modifications be made to HTML strings passed through props in React?

I'm working on implementing a pre-loader icon before an image is rendered in a React component using the code snippet below: export default class MediaPostItem extends BaseComponent { public constructor(props: IMediaPostItemProperties) { ...

Using React Router v6, you can easily set up routing for paths such as https://adomain.com/blogs/sample

After implementing nested routes with createBrowserRouter, I was surprised to find that the router was only routing to the top-level part of the URL. Despite having React components for both BlogsPage and SampleBlog, the routing behavior was unexpected. I ...