Implementing a Beveled Edge on a Shape using ThreeJS

I have put in a lot of effort to find the solution, but unfortunately I have not been successful so far. Currently, I am working on creating a shape using THREE.Shape, and I have the vertices data stored in a file. The shape appears to be straight without any cuts at the edges; however, my goal is to add a 45-degree cut at the edges.

Here are some sample screenshots:

https://i.sstatic.net/wXdki.png


https://i.sstatic.net/I2afO.png

Answer №1

Let's explore an alternative solution based on my previous comment, adapted to the latest framework updates:

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(5, 5, 10);
var renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

var light = new THREE.DirectionalLight(0xffffff, 0.75);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.25));

var helper = new THREE.GridHelper(20, 20);
//helper.geometry.rotateX(Math.PI * -.5);
scene.add(helper);

var profileShape1 = new THREE.Shape();
profileShape1.moveTo(0, 0);
profileShape1.lineTo(0, 1);
profileShape1.absarc(1, 1, 0.5, Math.PI, Math.PI * 1.5);
profileShape1.lineTo(1, 0);

var contour1 = [
  new THREE.Vector2(-1, 2),
  new THREE.Vector2(1, 2),
  new THREE.Vector2(1, -2),
  new THREE.Vector2(-1, -2)
];

var geometry1 = ProfiledContourGeometry(profileShape1, contour1, true);
var fullProfile1 = new THREE.Mesh(geometry1, new THREE.MeshStandardMaterial({
  color: 0xFACE8D,
  wireframe: false,
  metalness: 0,
  roughness: 0.75,
  flatShading: true
}));
scene.add(fullProfile1);

render();

function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script>
  function ProfiledContourGeometry(profileShape, contour, contourClosed, openEnded) {

    contourClosed = contourClosed !== undefined ? contourClosed : true;
    openEnded = openEnded !== undefined ? openEnded : false;
    openEnded = contourClosed === true ? false : openEnded;

    let profileGeometry = new THREE.ShapeBufferGeometry(profileShape);
    let flipProfileGeometry = flipShapeGeometry(profileGeometry);
    profileGeometry.rotateX(Math.PI * 0.5);
    let profile = profileGeometry.attributes.position;

    let addEnds = openEnded === false ? 2 : 0;
    let profilePoints = new Float32Array(profile.count * (contour.length + addEnds) * 3);

    let endProfiles = [];

    for (let i = 0; i < contour.length; i++) {
      let v1 = new THREE.Vector2().subVectors(contour[i - 1 < 0 ? contour.length - 1 : i - 1], contour[i]);
      let v2 = new THREE.Vector2().subVectors(contour[i + 1 == contour.length ? 0 : i + 1], contour[i]);
      let angle = v2.angle() - v1.angle();
      let halfAngle = angle * 0.5;

      let hA = halfAngle;
      let tA = v2.angle() + Math.PI * 0.5;
      if (!contourClosed) {
        if (i == 0 || i == contour.length - 1) {
          hA = Math.PI * 0.5;
        }
        if (i == contour.length - 1) {
          tA = v1.angle() - Math.PI * 0.5;
        }
      }

      let shift = Math.tan(hA - Math.PI * 0.5);
      let shiftMatrix = new THREE.Matrix4().set(
        1, 0, 0, 0, -shift, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
      );


      let tempAngle = tA;
      let rotationMatrix = new THREE.Matrix4().set(
        Math.cos(tempAngle), -Math.sin(tempAngle), 0, 0,
        Math.sin(tempAngle), Math.cos(tempAngle), 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
      );

      let translationMatrix = new THREE.Matrix4().set(
        1, 0, 0, contour[i].x,
        0, 1, 0, contour[i].y,
        0, 0, 1, 0,
        0, 0, 0, 1,
      );

      let cloneProfile = profile.clone();
      cloneProfile.applyMatrix4(shiftMatrix);
      cloneProfile.applyMatrix4(rotationMatrix);
      cloneProfile.applyMatrix4(translationMatrix);

      /*shiftMatrix.applyToBufferAttribute(cloneProfile);
      rotationMatrix.applyToBufferAttribute(cloneProfile);
      translationMatrix.applyToBufferAttribute(cloneProfile);*/

      profilePoints.set(cloneProfile.array, cloneProfile.count * i * 3);
      if (openEnded === false && (i === 0 || i === contour.length - 1)) {
        endProfiles.push(cloneProfile);
      }
    }

    endProfiles.forEach((ep, idx) => {
      profilePoints.set(ep.array, ep.count * (contour.length + idx) * 3)
    });

    let fullProfileGeometry = new THREE.BufferGeometry();
    fullProfileGeometry.setAttribute("position", new THREE.BufferAttribute(profilePoints, 3));
    let index = [];

    let lastCorner = contourClosed == false ? contour.length - 1 : contour.length;
    for (let i = 0; i < lastCorner; i++) {
      for (let j = 0; j < profile.count; j++) {
        let currCorner = i;
        let nextCorner = i + 1 == contour.length ? 0 : i + 1;
        let currPoint = j;
        let nextPoint = j + 1 == profile.count ? 0 : j + 1;

        let a = nextPoint + profile.count * currCorner;
        let b = currPoint + profile.count * currCorner;
        let c = currPoint + profile.count * nextCorner;
        let d = nextPoint + profile.count * nextCorner;


        index.push(a, b, d);
        index.push(b, c, d);
      }
    }

    if (openEnded === false) {
      // add indices from profile geometries
      flipProfileGeometry.index.array.forEach(i => {
        index.push(i + profile.count * (contour.length))
      });
      profileGeometry.index.array.forEach(i => {
        index.push(i + profile.count * (contour.length + 1))
      });

    }

    fullProfileGeometry.setIndex(index);
    fullProfileGeometry.computeVertexNormals();

    return fullProfileGeometry;
  }

  function flipShapeGeometry(shapeGeometry) {
    let flipGeom = shapeGeometry.clone();
    for (let i = 0; i < flipGeom.attributes.position.count; i++) {
      flipGeom.attributes.position.array[i * 3] *= -1;
    }
    flipGeom.attributes.position.needsUpdate = true;

    var index = flipGeom.index.array;
    for (let i = 0; i < index.length; i += 3) {
      let v2 = index[i + 1];
      let tmp = v2;
      let v3 = index[i + 2];
      index[i + 1] = index[i + 2];
      index[i + 2] = tmp;
    }
    flipGeom.computeVertexNormals();
    return flipGeom;
  }
</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

Whenever I try to open my jQuery UI Dialog without first displaying an alert, it does not work

Beginning of my HTML page, initializing a dialog : <script type="text/javascript"> $(function() { $( "#dialog-confirm" ).dialog({ autoOpen: false, resizable: true, height:180, width:300, modal: true, buttons: { "Yes": ...

Step-by-step guide on setting up a click counter that securely stores data in a text file, even after the

Can anyone help me make this link actually function as intended? Right now it only runs the JavaScript code, but I would like it to run the code and redirect to a webpage. Additionally, I need the data to be saved to a text file. Please provide assistanc ...

Splitting React Code - Loading additional component following initial page load

I've recently integrated Router-based code splitting (lazy loading) in my app. As far as I understand, with lazy loading, a user will only load a specific chunk of the complete bundle when visiting a page. Is there a way to instruct React to start lo ...

How can I create the effect of text changing color automatically after a specified time period has elapsed

I am currently dealing with a timer that is functioning properly, but I have a specific CSS inquiry. Essentially, what I would like to achieve is when the timer hits 30 seconds, I want the numbers to change to red color. $(function () { var $startTimer = ...

What could be causing the issue with my validation for alphabetical input?

I am currently working on a registration form that only accepts alphabetical input. However, I am facing an issue where my error message appears regardless of whether I input an alphabetical or special character. According to my understanding, the code sho ...

What is the best way to retrieve the second to last element in a list

When using Protractor, you have convenient methods like .first() and .last() on the ElementArrayFinder: var elements = element.all(by.css(".myclass")); elements.last(); elements.first(); But what about retrieving the element that comes right before the ...

Utilize Axios in Vue.js to fetch and process data from a REST API endpoint in C#

After successfully creating and deploying an API on Azure, I am trying to display the response in an alert box using javascript (Vue.js). The test method of my API returns the string "working". You can test the API here. This is the code snippet from my A ...

Adding Bootstrap to container-specific styling in SCSS

I am currently in the process of upgrading to the most recent version of reactstrap & Bootstrap. Previously, I had reactstrap in my package.json file and downloaded Bootstrap SCSS in my client/src/styles/bootstrap directory. Now, my updated package.json c ...

Tips for achieving JSON formatting with JavaScript or jQuery

To achieve the desired output format, I am required to transform the provided input json. input json: [ { "key a": "value alpha" "key b": "value beta" "key c": "value gamma& ...

I am currently struggling to make the userID route parameter function correctly with react-router-relay

I've been diving into the world of React Relay and GraphQL with react-relay-router, but I'm having trouble getting the params in my routes to function correctly. Specifically, I'm struggling with the "/Maps/:userID" route. Let me share my r ...

Obtaining static images from the public directory with getStaticProps in Next.js

Next.js provides a thorough method for accessing images from the /public/ folder, where static assets are stored. This involves using Node's fs module and fetching them within the getStaticProps function. Here is an example: export async function get ...

Dealing with functions that may not consistently return a promise

When dealing with a situation where a function does not always return a promise, how can it be best handled? The complexity of my current code prevents me from providing a detailed explanation, but essentially, the issue involves checking a condition and t ...

Experiencing Excessive Recursion While Dynamically Attaching Click Event Listener With Post Method to a Div Element

I'm encountering 'too much recursion' errors when trying to dynamically add a click handler to specific div tags with the class name 'reportLink'. Despite successfully logging the innerText of the divs, the code fails when attempti ...

How can one retrieve every element within nested associative arrays?

Situation : Upon receiving a JSON array from a jQuery <-> PHP Ajax request, the unparsed JSON array structure appears as follows: {"Focus":{"id":2,"brand":"Ford","name":"Focus"}} Upon using JSON.parse(json);, the structure transforms to: Foc ...

Does CSS in the current IE9 beta allow for text shadows to be implemented?

Text shadows look great in Firefox and Chrome. For example, I have a basic website for streaming videos. Unfortunately, there are no shadows in IE9 for me. This is my CSS code: p { color: #000; text-shadow: 0px 1px 1px #fff; padding-bottom: 1em; } ...

Ajax requests can form a pyramid of doom when multiple asynchronous calls are structured

My JavaScript application requires making an ajax call and potentially more calls based on the previous response. Currently, I have implemented this using a somewhat cumbersome pyramid of doom: function startParentArray(id) { getIssueDetail(id).succes ...

The (ReactJS + Redux) form fails to load when the /new segment is appended to the URL

I'm currently working on a project using React, Redux, and Rails. I've encountered an issue with loading the form. In my App.js file, I have set up a Router that defines the routes. import React, { Component } from 'react'; import { Br ...

Acquire dynamically loaded HTML content using Ajax within a WebView on an Android

I have been attempting to extract the content of a web page on an Android platform. Despite trying JSoup, I faced a limitation with ajax support. As an alternative, I am endeavoring to embed the URL within a hidden web view and retrieve the HTML in the on ...

Creating a Cross Fade Animation effect with the combination of CSS and JavaScript

I've been attempting to create a similar animation using html and css. Below gif shows the desired outcome I am aiming for: https://i.sstatic.net/YsNGy.gif Although I have tried the following code, I have not been able to achieve the desired result ...

Calculate the total of all values associated with a dynamically generated key within an array of objects

I'm trying to calculate the sum of similar keys in an array of objects. Each object in the array will have some common keys, but not all arrays will share the same set of keys. I'm considering storing these keys in a separate array and then loopi ...