Utilizing edge geometry in ArrowHelper with ThreeJS

I'm attempting to generate an arrow using ArrowHelper within ThreeJS:

let arrow = new THREE.ArrowHelper(direction.normalize(), new THREE.Vector3(), length, color, headLength, headWidth);

In addition, I would like to have a distinct color for the edges. It seems that I should utilize THREE.EdgesGeometry, but I am unsure how to implement it. Can someone provide assistance?

Update I apologize for the confusion, I mistakenly thought the arrow was based on a pyramid rather than a cone shape. Is there a way to substitute the cone with a pyramid and assign different colors to the edges?

Update

Thank you everyone for your responses, they were incredibly helpful. I decided to create a custom arrow class (derived from most of the code in ArrowHelper):

class CustomArrow extends THREE.Object3D {

    constructor( dir, origin, length, color, edgeColor, headLength, headWidth ) {

        super();
        // dir is assumed to be normalized

        this.type = 'CustomArrow';

        if ( dir === undefined ) dir = new THREE.Vector3( 0, 0, 1 );
        if ( origin === undefined ) origin = new THREE.Vector3( 0, 0, 0 );
        if ( length === undefined ) length = 1;
        if ( color === undefined ) color = 0xffff00;
        if ( headLength === undefined ) headLength = 0.2 * length;
        if ( headWidth === undefined ) headWidth = 0.2 * headLength;

        if ( this._lineGeometry === undefined ) {
            this._lineGeometry = new THREE.BufferGeometry();
            this._lineGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) );
            this._coneGeometry = new THREE.ConeBufferGeometry( 0.5, 1, 6);
            this._coneGeometry.translate( 0, - 0.5, 0 );
            this._axis = new THREE.Vector3();
        }

        this.position.copy( origin );

        this.line = new THREE.Line( this._lineGeometry, new THREE.LineBasicMaterial( { color: color, toneMapped: false, linewidth: 4 } ) );
        this.line.matrixAutoUpdate = false;
        this.add( this.line )

        // base material        
        this.cone = new THREE.Mesh( this._coneGeometry, new THREE.MeshBasicMaterial( { color: color, toneMapped: false } ) );
        this.add(this.cone);

        // wire frame
        this.wireframe = new THREE.Mesh( this._coneGeometry, new THREE.MeshBasicMaterial( { 
            color: edgeColor, 
            toneMapped: false, 
            wireframe: true,
            wireframeLinewidth: 2 } ) );
        this.add(this.wireframe);

        this.setDirection( dir );
        this.setLength( length, headLength, headWidth );
    }

    setDirection( dir ) {

        // dir is assumed to be normalized

        if ( dir.y > 0.99999 ) {

            this.quaternion.set( 0, 0, 0, 1 );

        } else if ( dir.y < - 0.99999 ) {

            this.quaternion.set( 1, 0, 0, 0 );

        } else {

            this._axis.set( dir.z, 0, - dir.x ).normalize();

            const radians = Math.acos( dir.y );

            this.quaternion.setFromAxisAngle( this._axis, radians );

        }

    }

    setLength( length, headLength, headWidth ) {

        if ( headLength === undefined ) headLength = 0.2 * length;
        if ( headWidth === undefined ) headWidth = 0.2 * headLength;

        this.line.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458
        this.line.updateMatrix();
        
        this.cone.scale.set( headWidth, headLength, headWidth );
        this.cone.position.y = length;
        this.cone.updateMatrix();
        
        this.wireframe.scale.set( headWidth, headLength, headWidth );
        this.wireframe.position.y = length;
        this.wireframe.updateMatrix();
    }

    setColor( color ) {
        this.line.material.color.set( color );
        // this.cone.material.color.set( color );
        // this.wireframe.material.color.set( color );
    }

    copy( source ) {
        super.copy( source, false );
        this.line.copy( source.line );
        this.cone.copy( source.cone );
        this.wireframe.copy( source.wireframe );
        return this;
    }
}

For some reason, linewidth and wireframeLinewidth do not seem to impact line widths. Any insights into why this might be?

Answer №1

update: To create a pyramid resembling a cone with 4 radial segments, take inspiration from how the arrowhelper generates its cone using a tapered CylinderGeometry and line based on specific parameters. Replace the existing cone geometry setup with the following:

previous version:

_coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 );

new version:

_coneGeometry = new ConeBufferGeometry( 0.5, 1, 4);

This way, you can skip using EdgesGeometry and opt for the wireframe material option (as suggested by @prisoner849):

let wireframeMaterial = new THREE.MeshBasicMaterial({color: "aqua", wireframe: true});

let coneEdgeMesh = new THREE.Mesh(_coneGeometry, wireframeMaterial);

Earlier response:

The THREE.ArrowHelper comprises of 2 Object3Ds: a THREE.Line for the line element and a THREE.Mesh for the arrow's cone shape. While the Line geometry has only 2 points without edges due to its linear nature, for the cone component you can utilize:

let coneEdgeGeometry = new THREE.EdgesGeometry(arrow.cone.geometry);

Create a LineSegments object using the edge geometry and customize it with your preferred color:

let line = new THREE.LineSegments( coneEdgeGeometry, new THREE.LineBasicMaterial( { color: 0xffffff } ) );
arrow.add(line);

If the cone edge does not appear, adjusting the renderOrder of the THREE.LineSegments to -1 may solve the issue (though it could potentially lead to other complications)

Answer №2

To customize the appearance of the arrow's cone, you can adjust the color using the following code snippet:

body {
  overflow: hidden;
  margin: 0;
}
<script type="module">
  import * as THREE from "https://threejs.org/build/three.module.js";     
  import {OrbitControls} from "https://threejs.org/examples/jsm/controls/OrbitControls.js"; 
  
  let scene = new THREE.Scene(); 
  let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight,
  1, 100); 
  camera.position.set(0, 5, 10); 
  let renderer = new THREE.WebGLRenderer(); 
  renderer.setSize(innerWidth, innerHeight); 
  document.body.appendChild(renderer.domElement); 
  
  new OrbitControls(camera, renderer.domElement); 
  
  scene.add(new THREE.GridHelper());
  
  // Change the arrow cone color
  let ah = new THREE.ArrowHelper( 
    new THREE.Vector3(0, 1, 0), 
    new THREE.Vector3(-4, 0, 0), 
    5, 
    "magenta" /* default colour */); 
  ah.cone.material.color.set("red"); // modify cone color here
  scene.add(ah); 
  
  // Create a colorful pyramid
  let cg = new THREE.SphereBufferGeometry(0.5, 4, 2).toNonIndexed();
  let pos = cg.attributes.position;
  for (let i = 0; i < pos.count; i++){
    if (pos.getY(i) < 0) pos.setY(i, 0);
  }
  console.log(cg);
  let cls = [
    new THREE.Color("red"),
    new THREE.Color("green"),
    new THREE.Color("blue"),
    new THREE.Color("yellow")
  ]
  let colors = [];
  for(let i = 0; i < 2; i++){
    cls.forEach( (c) => {
      colors.push(c.r, c.g, c.b);
      colors.push(c.r, c.g, c.b);
      colors.push(c.r, c.g, c.b);
    });
  }
  cg.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
  
  let cm = new THREE.MeshBasicMaterial({vertexColors: true});
  let co = new THREE.Mesh(cg, cm);
  co.scale.set(1, 5, 1);
  scene.add(co);
  
  renderer.setAnimationLoop(()=>{ 
    renderer.render(scene, camera); 
  });
</script>

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

Javascript - Could anyone provide a detailed explanation of the functionality of this code snippet?

Ever since joining a new company 9 months ago, I've been encountering this line of code in JavaScript. It seems to work fine and I've been incorporating it into my coding style to align with the previous developers. However, I'm not entirely ...

Hiding or revealing the coupon field on the Magento cart page

I am currently using Magento 1.7 and I'm in need of a solution or some guidance to address the following problem: I am looking to hide the coupon field on the cart page and instead have a link that reads "Have a coupon? Click here to use it". When ...

The method .depth() does not exist within this context

When I attempted to execute this code using npm start in the terminal //index.js const api = require('./api'); console.log('Starting monitoring!'); setInterval(async () => { //console.log(await api.time()); console.log(await ...

Create a nested array of subcategories within an array object

Currently, I am working on integrating Django Rest and Angular. The JSON array received from the server includes category and subcategory values. My goal is to organize the data such that each category has its related subcategories stored as an array withi ...

Having trouble installing Angular 4 with npm?

After installing Angular, I encountered an error when trying to use the command line "ng -v". Please refer to the attached jpeg file. My node version is 6.10.3. Does anyone have a solution? https://i.sstatic.net/ZQ69k.png ...

How to extract text from a dropdown menu using JQuery in HTML

Having trouble retrieving the value of a selected element and displaying it in the console. Despite all my efforts, I can't seem to make it work - value1 is retrieved fine, but value2 and 3 are not. If you have any suggestions (apart from using JQuer ...

Sending information upwards within an onClick event in a React component

Just starting out with React/ES6 and diving into creating my first components. Currently, I have a PuzzleContainer component that houses a Puzzle component responsible for displaying images. The container component triggers an AJAX call to fetch data on wh ...

Utilizing Curves in conjunction with tubeGeometry

I am struggling to create a curve using predefined points and then using them to generate a tubeGeometry. The examples I have come across are confusing, and the documentation is not very helpful. While I can get the tubegeometry to work with a spline curv ...

Disabling $routeprovider while implementing bootstrap

I am encountering an issue with my normal routeprovider code. In a specific section of my HTML, I have some Twitter Bootstrap expand/collapse sections which inadvertently trigger the routeprovider when clicked. Is there a way to prevent this from happening ...

To trigger a pop-up window displaying a link upon clicking the submit button

I am attempting to create a popup box that displays an application accepted message and a link back to the home page upon clicking the submit button. However, the .popup box is not appearing after validation. This is the content that should be displayed w ...

Switching the background image when hovering over a list element

Looking at the screenshot, it's clear that there is an unordered list with a background image set on the div. What I am trying to achieve is to have the background image change whenever I hover over a list item. Each list item should trigger a differe ...

Avoid changing the orientation

Despite having a similar title, my question is not a duplicate of Prevent orientation change in iOS Safari. I have modified the code provided below. I have written the following code to automatically rotate the content when a user rotates their iPad/iPhon ...

Basic inquiry about Ajax

Would you like to know a simple solution for refreshing a specific area of your website using ajax? Instead of having JavaScript constantly checking for changes on the server, is there a way for the server to send data when a certain event occurs? Imagine ...

I am encountering an error stating "Cannot locate module 'nestjs/common' or its related type declarations."

I am currently working on a controller in NestJS located in the file auth.controller.ts. import { Controller } from 'nestjs/common'; @Controller() export class AppController {} However, I encountered an error that says: Error TS2307: Cannot fin ...

Converting Python code into JavaScript for use on an iPhone device

Having a web application with backend developed in Python (using Django) and front end in HTML5 & JavaScript, where Python generated data is transferred to JavaScript/HTML through JSON. Now, I want to transform it into a mobile application, starting with ...

The types 'X' and 'string' do not intersect

I have a situation where I am using the following type: export type AutocompleteChangeReason = | 'createOption' | 'selectOption' | 'removeOption' | 'clear' | 'blur'; But when I try to compress the cod ...

Adjusting the background color of <tr> depending on the number of <tr> elements in the HTML using CSS

When working on my HTML page, I have utilized multiple <tr> tags with different bgcolor values such as: <tr bgcolor="#000000"> <tr bgcolor="#E5F1CC"> <tr bgcolor="#D30A0A"> <tr bgcolor="#656766"> I am aiming to assign unique ...

Is there a built-in method in Next.js 13 to secure routes from unauthorized access?

In my project, I have the following pages: /about, /user, and /user/[id]. Unfortunately, I am unable to access req.page, which provides the slug (i.e. /user/[id]), making it difficult for me to implement logic to redirect requests from unauthenticated user ...

Redux: utilizing yield within an unrecognized function

Hey there! I am brand new to Reudx-Saga and have been experimenting with it for the past few days. I feel pretty comfortable with generators, actions, redux-stores, sagas, and other concepts. Overall, I have a good amount of experience with JavaScript. C ...

The functionality of .attachClickHandler() is not maintained when the page is refreshed or redirected

I've encountered an issue while trying to integrate a custom Google login button in my index.html file. Initially, everything functioned flawlessly upon page load, with console outputs 1, 2, and 3 appearing sequentially, followed by output 4 upon sign ...