# ProfiledContourGeometry

**URL:** <https://discourse.threejs.org/t/profiledcontourgeometry/2330>\
**Category:** Resources\
**Tags:** buffergeometry\
**Created:** [April 4, 2018, 3:36pm UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330 "2018-04-04T15:36:15Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![prisoner849](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/prisoner849/32/535_2.png) [@prisoner849](https://discourse.threejs.org/u/prisoner849)\
**Post date:** [April 4, 2018, 3:36pm UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330/1 "2018-04-04T15:36:15Z")

</div>

Evolving of that [post](https://discourse.threejs.org/t/simple-rectangular-geometry-extrusion-anyone/743/14)

![ProfiledContourGeometry3](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/2X/2/26f0c20fca0a94cf5bffdc0158d798658ee7842e.png)

The function has four parameters:

1. `THREE.Shape()` for a profile;
2. array of `THREE.Vector2()` for a contour;
3. a boolean value for closing the contour (default is `true`);
4. a boolean value for open ends (default is `false`).

It calculates an angle between two edges of contour and then consiquently applies three matrices of shear/shift, rotation and translation to the points of the profile at each point of contour.

The solution is still rough and raw, and it needs lots of improvements and optimizations (a result geometry has no uvs, and I’m not sure how valid the trick with flipping of a shape).

[https://jsfiddle.net/prisoner849/597ecnh8/](https://jsfiddle.net/prisoner849/597ecnh8/)

```
  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);

      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;
  }
```

---

<div class="post-metadata">

**Author:** ![ssaras](https://avatars.discourse-cdn.com/v4/letter/s/9dc877/32.png) [@ssaras](https://discourse.threejs.org/u/ssaras)\
**Post date:** [August 9, 2018, 6:14pm UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330/2 "2018-08-09T18:14:25Z")

</div>

This is beautiful. I’ll be watching this topic.

---

<div class="post-metadata">

**Author:** ![ssaras](https://avatars.discourse-cdn.com/v4/letter/s/9dc877/32.png) [@ssaras](https://discourse.threejs.org/u/ssaras)\
**Post date:** [August 9, 2018, 6:33pm UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330/3 "2018-08-09T18:33:38Z")

</div>

This solution also allows for capping or cross-sectioning. Do you think it’ll be possible to apply a different color or texture to the cap?

---

<div class="post-metadata">

**Author:** ![prisoner849](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/prisoner849/32/535_2.png) [@prisoner849](https://discourse.threejs.org/u/prisoner849)\
**Post date:** [August 9, 2018, 6:36pm UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330/4 "2018-08-09T18:36:19Z")

</div>

@ssaras  
Yes, if one day (when I have time) I’ll add support for groups and calculation of uvs 🙂

---

<div class="post-metadata">

**Author:** ![zxo102](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/zxo102/32/8696_2.png) [@zxo102](https://discourse.threejs.org/u/zxo102)\
**Post date:** [January 12, 2020, 10:17am UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330/5 "2020-01-12T10:17:48Z")

</div>

@prisoner849, Great job. That is what I am looking for. Is that possible to change profile01 to profile02 from one point to next point in the middle of a same contour? I am also thinking how to make Y joints or X joints for three or four profiles meeting at one point. Any suggestions would be appreciated. Thanks a lot.

ouyang

---

<div class="post-metadata">

**Author:** ![vin-ni](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/vin-ni/32/5013_2.png) [@vin-ni](https://discourse.threejs.org/u/vin-ni)\
**Post date:** [May 21, 2020, 10:22pm UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330/6 "2020-05-21T22:22:16Z")

</div>

Thank you so much! This really helped me out.

Here’s a little update to

```auto
shiftMatrix.applyToBufferAttribute(cloneProfile);
rotationMatrix.applyToBufferAttribute(cloneProfile);
translationMatrix.applyToBufferAttribute(cloneProfile);

```

exchange to this:

```auto
cloneProfile.applyMatrix4(shiftMatrix);
cloneProfile.applyMatrix4(rotationMatrix);
cloneProfile.applyMatrix4(translationMatrix);
```

---

<div class="post-metadata">

**Author:** ![prisoner849](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/prisoner849/32/535_2.png) [@prisoner849](https://discourse.threejs.org/u/prisoner849)\
**Post date:** [May 22, 2020, 7:54am UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330/7 "2020-05-22T07:54:26Z")

</div>

@vin-ni  
That’s the power of the community 🤘  
Thanks for the reminding about that fix 🙂 I did it in this SO answer, but forgot to put these changes in this topic 🙂

> <https://stackoverflow.com/questions/60593982/threejs-adding-45-degree-cut-at-the-edges-of-a-shape/60598214#60598214>

---

<div class="post-metadata">

**Author:** ![vin-ni](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/vin-ni/32/5013_2.png) [@vin-ni](https://discourse.threejs.org/u/vin-ni)\
**Post date:** [June 17, 2020, 4:09pm UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330/8 "2020-06-17T16:09:59Z")

</div>

@prisoner849 How would I decrease the triangles size?  
On a shape like this I get 85k triangles, which is ways too much i think.

 ![Screenshot 2020-06-17 at 18.06.50](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/2X/c/c68a0e7b748dba6158dd8f48532d33482290e8c3.jpeg)

How could I best decrease the amount?

---

<div class="post-metadata">

**Author:** ![vin-ni](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/vin-ni/32/5013_2.png) [@vin-ni](https://discourse.threejs.org/u/vin-ni)\
**Post date:** [June 17, 2020, 5:34pm UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330/9 "2020-06-17T17:34:10Z")

</div>

I ended up doing

```auto
      if (i && i % 3 === 0) {
        // push only every nth vertice
        tempGeometry.push(pos);
      } else if (tempBuf.vertices.length - 1 === i) {
        // if its the last one, push it anyway
        tempGeometry.push(pos);
      }

```

and for the shape

```auto
const profileGeometry = new ShapeBufferGeometry(profileShape, 3); //12 is standard

```

 ![Screenshot 2020-06-17 at 19.34.38](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/2X/2/224a5517fc361ba19c1cf7d3bbfb7c7ab0e2712d.jpeg)

---

<div class="post-metadata">

**Author:** ![Nathan](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/nathan/32/19927_2.png) [@Nathan](https://discourse.threejs.org/u/Nathan)\
**Post date:** [August 4, 2021, 4:50pm UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330/10 "2021-08-04T16:50:45Z")

</div>

@prisoner849 Do you have any advice for texturing these kinds of shapes? I’m struggling to get anything but a solid color when applying a texture.

---

<div class="post-metadata">

**Author:** ![prisoner849](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/prisoner849/32/535_2.png) [@prisoner849](https://discourse.threejs.org/u/prisoner849)\
**Post date:** [August 4, 2021, 4:53pm UTC](https://discourse.threejs.org/t/profiledcontourgeometry/2330/11 "2021-08-04T16:53:52Z")

</div>

@Nathan Then you better have a look at this topic: [ProfiledContourGeometry MultiMaterial](https://discourse.threejs.org/t/profiledcontourgeometry-multimaterial/5801)  
as @hofk improved that idea 🙂
