Creating a freehand drawing feature with mouse movement using bufferGeometry in three.js r144

I have been working with code from a repository called scribble, which uses three.js r87. In trying to update the code to three.js r144, I followed the steps outlined in the Converting THREE.Geometry to THREE.BufferGeometry tutorial. While one function updated smoothly, the other is causing me some trouble.

It was relatively easy to upgrade the mousePressed function:

function mousePressed() {
  const point = new THREE.Vector3(mouseX, mouseY, 0);
  let points = [];
  points.push(point);
  let geometry = new THREE.BufferGeometry().setFromPoints(points);
  const line = new THREE.Line(geometry, material);
  scene.add(line);
  selected = line;
}

However, I am facing issues with updating mouseDragged(), and I can't seem to figure out why it's not functioning as expected:

function mouseDragged() {
  const line = selected;
  const point = new THREE.Vector3(mouseX, mouseY, 0);
  const oldgeometry = line.geometry;
  let newgeometry = new THREE.BufferGeometry();
  let positions = oldgeometry.attributes.position.array;
  for (let i = 0; i < positions.length; i += 3) {
    const v = new THREE.Vector3(positions[i], positions[i + 1], positions[i + 2]);
    positions[i] = v.x;
    positions[i + 1] = v.y;
    positions[i + 2] = v.z;
  }
  positions.push(point); // Shouldn't adding the new point suffice?
  newgeometry.attributes.position.needsUpdate = true;
  line.geometry = newgeometry;
  scene.add(line);
  selected = line;
}

Any help or insights would be greatly appreciated! Thank you!

Answer №1

The approach taken in the provided code snippet is not optimal as it continuously allocates geometries without proper disposal management, leading to potential memory leaks. A more efficient method would be:

let camera, scene, renderer, line;

const frustumSize = 4;

let index = 0;
const coords = new THREE.Vector3();

init();
render();

function init() {

  const aspect = window.innerWidth / window.innerHeight;

  camera = new THREE.OrthographicCamera(frustumSize * aspect / -2, frustumSize * aspect / 2, frustumSize / 2, frustumSize / -2, 0.1, 20);
  camera.position.z = 5;

  scene = new THREE.Scene();

  const geometry = new THREE.BufferGeometry();

  const positionAttribute = new THREE.BufferAttribute(new Float32Array(1000 * 3), 3); // allocate large enough buffer
  positionAttribute.setUsage(THREE.DynamicDrawUsage);
  geometry.setAttribute('position', positionAttribute);

  const material = new THREE.LineBasicMaterial()

  line = new THREE.Line(geometry, material);
  scene.add(line);

  // initial points

  addPoint(0, 0, 0); // start point
  addPoint(1, 0, 0); // current pointer coordinate

  //

  renderer = new THREE.WebGLRenderer({
    antialias: true
  });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  renderer.domElement.addEventListener('pointerdown', onPointerDown);
  renderer.domElement.addEventListener('pointermove', onPointerMove);

  window.addEventListener('resize', onWindowResize);

}

function addPoint(x, y, z) {

  const positionAttribute = line.geometry.getAttribute('position');
  positionAttribute.setXYZ(index, x, y, z);
  positionAttribute.needsUpdate = true;

  index++;

  line.geometry.setDrawRange(0, index);

}

function updatePoint(x, y, z) {

  const positionAttribute = line.geometry.getAttribute('position');
  positionAttribute.setXYZ(index - 1, coords.x, coords.y, 0);
  positionAttribute.needsUpdate = true;

}

function onPointerDown(event) {

  coords.x = (event.clientX / window.innerWidth) * 2 - 1;
  coords.y = -(event.clientY / window.innerHeight) * 2 + 1;
  coords.z = (camera.near + camera.far) / (camera.near - camera.far);

  coords.unproject(camera);

  addPoint(coords.x, coords.y, 0);

  render();

}

function onPointerMove(event) {

  coords.x = (event.clientX / window.innerWidth) * 2 - 1;
  coords.y = -(event.clientY / window.innerHeight) * 2 + 1;
  coords.z = (camera.near + camera.far) / (camera.near - camera.far);

  coords.unproject(camera);

  updatePoint(coords.x, coords.y, 0)

  render();

}

function onWindowResize() {

  const aspect = window.innerWidth / window.innerHeight;

  camera.left = -frustumSize * aspect / 2;
  camera.right = frustumSize * aspect / 2;
  camera.top = frustumSize / 2;
  camera.bottom = -frustumSize / 2;

  camera.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);

  render();

}

function render() {

  renderer.render(scene, camera);

}
body {
    margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="57233f253232176779666363">[email protected]</a>/build/three.min.js"></script>

The recommended strategy here is to allocate a single extensive buffer to store both current and future points of a line and utilize setDrawRange() to specify which parts of the buffer should be rendered.

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

What is the process for accepting user input if their username remains the same?

I've been grappling with what logic to implement in this scenario. I want the user to be able to update their information, but an issue has arisen. What if a user wishes to change their email but keep the same username? As it stands, the username is ...

NodeJS closes the previous server port before establishing a new server connection

During my development and testing process, whenever I make changes, I find myself having to exit the server, implement the updates, and then start a new server. The first time I run the command node server.js, everything works perfectly. However, when I m ...

AngularJS - Calculate multiple currencies

I need to calculate the product of a value and months. For the input value, I am utilizing a Jquery Plugin to apply a currency mask. Unfortunately, the calculations are not functioning properly with this plugin. My goal is to multiply the value, includin ...

Utilize the data storage API within Next.js or directly in the user's

Struggling to store this ini file on either the server or client, any help would be greatly appreciated. Additionally, I would like to display the ini info in the browser so that clients can easily copy and paste the information. However, I seem to be fac ...

Building nested objects within an isolated scope can be achieved by following these steps

I am trying to implement a nested structure on my nested scope within a directive, as shown below: angular.module('myAddress').directive('myAddress', [function () { return { restrict: 'AE', controller: &ap ...

"Exploring the dynamic manipulation of CSS display property through the use of an active class

When attempting to implement a small pop-up window for user confirmation before navigating away from the page, I encountered an issue. Upon clicking the RUN button with the id "openBtn," the pop-up window briefly appears and then disappears. I'm unsur ...

Scrolling is endless with jCarousel - just press a button to keep moving even when you reach the first or

I seem to be experiencing a problem with jCarousel where, upon starting at the beginning, pressing the left button fails to scroll the carousel. The expected behavior is for the carousel to cycle to the end item when the left button is pressed while the f ...

Real-time changes may not be instantly reflected in the model update

I need to add two numbers together and display the sum in a third input field: HTML <div ng-app="myApp"> <div ng-controller="MainCtrl as mainCtrl"> Main {{mainCtrl.foo}} <br/> <input type="text" ng-model="mainCtrl.foo"/> ...

Combining React, Typescript, and asynchronous Promises

Currently I am in the process of developing a component that interacts with a webservice to fetch data asynchronously using promises. Once the promise is fulfilled, I intend to integrate the results into my component's rendering method. My ultimate go ...

When trying to inject HTML into AngularJS elements, the data binding may not function

I attempted to reference the documentation but it appears that I may be overlooking something. My goal is to inject HTML that is connected to a JSON object. It functions correctly when the HTML is explicitly declared, however, upon injection and callin ...

In jQuery, utilizing dynamic class names with variables

I have a series of images with unique classes such as: .1a .2a .3a .4a ..... I am looking to toggle some other classes named .1b .2b .3b .. and so on so that: '.1a' toggles to '1b' '.2a' toggles to &ap ...

Tips for concealing text adjustments during window resizing in an HTML document

I'm currently working with a website code that involves two sidenavs, one on the right and another on the left. When one sidebar opens, the other collapses. However, I've noticed that the text in between readjusts when the sidebars are toggled op ...

Encasing the app component with a context and encountering the issue: TypeError - (destructured parameter) does not have a defined value

My goal is to wrap all components under the app in a context to provide specific functionalities (as evidenced by my UserContext component). import React, { useState, createContext, useContext } from 'react' const Context = createContext(); exp ...

Incorporating Google Analytics into a personalized Order Confirmation page on Woocommerce 3

I have a personalized gratitude page for post-checkout in WooCommerce where I want to include order details in a Google ecommerce tracking tag to log the sale in analytics. Part of this involves adding the following information for each item in the order.. ...

Ways to reset the selected option when the user chooses a different option using either Jquery or Vanilla JavaScript

I am currently working on a functionality to clear the select option when a different brand is selected. The issue I am facing is that when I choose another brand, the models from the previous selection are not cleared out. For example, if I select BMW, I ...

Tips on sending image information from node.js to an HTML5 canvas?

Currently in the process of developing a demo featuring a node.js C++ plugin, I encounter an issue with the conversion of an ARGB bitmap to RGBA format for HTML5 canvas integration. The performance of this conversion process is exceedingly slow, prompting ...

Transforming the button behavior with jQuery

I have encountered a situation where I am working with code in Twig. {% if followsId == null %} <div id="followUser" class="follow" data-userId="{{ profileUserData.id }}" data-currentUserId="{{ loggedUserData.id }}" data-action="follow"> ...

Challenges encountered with the "load" event handler when creating a Firefox Extension

I am currently troubleshooting a user interaction issue with my Firefox extension. The tasks that my extension needs to complete include: Checking certain structures on the currently viewed browser tab Making backend server calls Opening dialogs Redirect ...

Which specific events must I manage within the express response (stream) object to ensure proper handling when the response is completed?

I'm looking to automate the logging process for when a request completes. Here's an example of what I have so far: function (req, res, next) { var startTime = clock.now(); res.on('end'. function() { logger.trace("E ...

Troubleshooting Material-UI Menus

I need the menu to adjust its height dynamically as the content of the page increases vertically. Even though I have applied "height:100%" in the styles, it doesn't seem to work. Can anyone assist with this issue? Here is the code snippet: import R ...