Skip to content

Commit

Permalink
Merge pull request #983 from ewei2406/curb-radius-option
Browse files Browse the repository at this point in the history
Curb radius option
  • Loading branch information
kfarr authored Dec 23, 2024
2 parents 7632b22 + 1547d97 commit ed0d5db
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 57 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ The `intersection` component creates an intersection surface with options for ad
| --------- | --------- | --------- |
| dimensions | Specifies the width and depth of the intersection. First value represents width, second value represents depth. | '20 20' |
| sidewalk | Sets the width of the sidewalk at each side of the intersection. Values are set in the order of west, east, north, south. | '0 0 0 0' |
| northeastcurb | Sets the curb dimensions for the north east curb. Values are updated as width, then depth. | '4 4' |
| southwestcurb | Sets the curb dimensions for the south west curb. Values are updated as width, then depth. | '4 4' |
| southeastcurb | Sets the curb dimensions for the south east curb. Values are updated as width, then depth. | '4 4' |
| northwestcurb | Sets the curb dimensions for the north west curb. Values are updated as width, then depth. | '4 4' |
| northeastcurb | Sets the curb dimensions for the north east curb. Values are updated as width, depth, then radius. | '4 4 0' |
| southwestcurb | Sets the curb dimensions for the south west curb. Values are updated as width, depth, then radius. | '4 4 0' |
| southeastcurb | Sets the curb dimensions for the south east curb. Values are updated as width, depth, then radius. | '4 4 0' |
| northwestcurb | Sets the curb dimensions for the north west curb. Values are updated as width, depth, then radius. | '4 4 0' |
| stopsign | Sets if each side of the intersection has a stop sign. Values are set in the order of east, west, north, south. 0 is false, 1 is true. | '0 0 0 0' |
| trafficsignal | Sets if each side of the intersection has a traffic signal. Values are set in the order of east, west, north, south. 0 is false, 1 is true. | '1 1 1 1' |
| crosswalk | ​​Sets if each side of the intersection has a crosswalk. Values are set in the order of east, west, north, south. 0 is false, 1 is true. | '1 1 1 1' |
Expand Down
157 changes: 104 additions & 53 deletions src/components/intersection.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import * as THREE from 'three';

/* global AFRAME */
AFRAME.registerComponent('intersection', {
schema: {
dimensions: { type: 'string', default: '20 20' },
// cardinal direction order for sidewalk, stopsign, crosswalk, and trafficsignal: west, east, north, south
sidewalk: { type: 'string', default: '0 0 0 0' },
northeastcurb: { type: 'string', default: '4 4' },
southwestcurb: { type: 'string', default: '4 4' },
southeastcurb: { type: 'string', default: '4 4' },
northwestcurb: { type: 'string', default: '4 4' },
northeastcurb: { type: 'string', default: '4 4 1' },
southwestcurb: { type: 'string', default: '4 4 1' },
southeastcurb: { type: 'string', default: '4 4 1' },
northwestcurb: { type: 'string', default: '4 4 1' },
stopsign: { type: 'string', default: '0 0 0 0' },
trafficsignal: { type: 'string', default: '1 1 1 1' },
crosswalk: { type: 'string', default: '1 1 1 1' }
Expand Down Expand Up @@ -54,78 +56,118 @@ AFRAME.registerComponent('intersection', {
);
this.el.setAttribute('shadow', '');

function createSidewalkElem({
// Create the texture for sidewalk that will be re-used
const sidewalkTexture = new THREE.TextureLoader().load(
document.getElementById('seamless-sidewalk').src
);
sidewalkTexture.wrapS = THREE.RepeatWrapping;
sidewalkTexture.wrapT = THREE.RepeatWrapping;
sidewalkTexture.repeat.set(0.5, 0.5); // Scale the texture to repeat twice every meter

this.sidewalkMaterial = new THREE.MeshStandardMaterial({
map: sidewalkTexture,
roughness: 0.8, // Same roughness as sidewalk mixin in src/assets.js
color: 0xcccccc // Darkens the texture to match existing sidewalk
});
this.curbGeoms = [];

const createSidewalkElem = ({
length,
width,
radius,
positionVec,
scaleVec = { x: 1, y: 1, z: 1 },
rotationVec,
displayName
}) {
}) => {
const sd = document.createElement('a-entity');
// every 2 meters repeat sidewalk texture
const repeatCountInter = [width / 2, parseInt(length / 2)];
// Radius should not be greater than any side
const boundedRadius = Math.min(radius, length, width);

const points = [];
points.push(new THREE.Vector2(0, 0));
points.push(new THREE.Vector2(length, 0));
// Only if a radius is set, create the arc
if (radius > 0) {
const arc = new THREE.EllipseCurve(
length - boundedRadius,
width - boundedRadius,
boundedRadius,
boundedRadius,
0,
Math.PI / 2
);
points.push(...arc.getSpacedPoints());
} else {
points.push(new THREE.Vector2(length, width));
}
points.push(new THREE.Vector2(0, width));

// Create new shape out of the points:
const curbShape = new THREE.Shape(points);
const curbGeom = new THREE.ExtrudeGeometry(curbShape, {
depth: 0.4, // Match existing sidewalk thickness
bevelEnabled: false
});
this.curbGeoms.push(curbGeom); // Need to remove manually later since it is 3js component

const mesh = new THREE.Mesh(curbGeom, this.sidewalkMaterial);
mesh.scale.setX(scaleVec.x);
mesh.scale.setY(scaleVec.y);
mesh.scale.setZ(scaleVec.z);

sd.setAttribute(
'geometry',
`primitive:box; depth: ${length}; width: ${width}; height: 0.4`
);
sd.setAttribute('position', positionVec);
sd.setAttribute('scale', scaleVec);
sd.setAttribute('rotation', rotationVec);
sd.setAttribute('mixin', 'sidewalk');
sd.setAttribute('shadow', 'cast: false;');
sd.classList.add('autocreated');
sd.setAttribute(
'material',
`repeat: ${repeatCountInter[0]} ${repeatCountInter[1]}`
);
sd.setAttribute('data-layer-name', 'Sidewalk • ' + displayName);
sd.setAttribute('data-no-transform', '');
sd.setAttribute('data-ignore-raycaster', '');
sd.object3D.add(mesh);
el.appendChild(sd);
}
};

// describe sidewalk parameters
const sidewalkParams = {
west: {
positionVec: { x: -intersectWidth / 2 + sidewalkArray[0] / 2, z: 0.1 },
rotationVec: { x: 90, y: 0, z: 0 },
length: intersectDepth,
width: sidewalkArray[0],
positionVec: {
x: -intersectWidth / 2,
y: -intersectWidth / 2,
z: 0.1
},
width: intersectDepth,
length: sidewalkArray[0],
displayName: 'West'
},
east: {
positionVec: { x: intersectWidth / 2 - sidewalkArray[1] / 2, z: 0.1 },
rotationVec: { x: 90, y: 0, z: 0 },
length: intersectDepth,
width: sidewalkArray[1],
positionVec: {
x: intersectWidth / 2,
y: -intersectWidth / 2,
z: 0.1
},
scaleVec: { x: -1, y: 1, z: 1 },
width: intersectDepth,
length: sidewalkArray[1],
displayName: 'East'
},
north: {
positionVec: {
// add x offset to avoid sidewalk's element overlap
x: sidewalkArray[1] / 2 - sidewalkArray[0] / 2,
y: intersectDepth / 2 - sidewalkArray[2] / 2,
x: -intersectWidth / 2,
y: intersectWidth / 2,
z: 0.1
},
rotationVec: { x: 0, y: 90, z: -90 },
// minus the width of the crossing sidewalk
length: intersectWidth - sidewalkArray[1] - sidewalkArray[0],
scaleVec: { x: 1, y: -1, z: 1 },
width: sidewalkArray[2],
length: intersectDepth,
displayName: 'North'
},
south: {
positionVec: {
// add x offset to avoid sidewalk's element overlap
x: sidewalkArray[1] / 2 - sidewalkArray[0] / 2,
y: -intersectDepth / 2 + sidewalkArray[3] / 2,
x: -intersectWidth / 2,
y: -intersectWidth / 2,
z: 0.1
},
rotationVec: { x: 0, y: 90, z: -90 },
// minus the width of the crossing sidewalk
length: intersectWidth - sidewalkArray[1] - sidewalkArray[0],
width: sidewalkArray[3],
length: intersectDepth,
displayName: 'South'
}
};
Expand All @@ -144,53 +186,57 @@ AFRAME.registerComponent('intersection', {
const curbParams = {
northeast: {
positionVec: {
x: intersectWidth / 2 - northeastcurbArray[0] / 2,
y: intersectDepth / 2 - northeastcurbArray[1] / 2,
x: intersectWidth / 2,
y: intersectDepth / 2,
z: 0.1
},
rotationVec: { x: 0, y: 90, z: -90 },
scaleVec: { x: -1, y: -1, z: 1 },
length: northeastcurbArray[0],
width: northeastcurbArray[1],
radius: northeastcurbArray[2],
displayName: 'Northeast'
},
southwest: {
positionVec: {
x: -intersectWidth / 2 + southwestcurbArray[0] / 2,
y: -intersectDepth / 2 + southwestcurbArray[1] / 2,
x: -intersectWidth / 2,
y: -intersectDepth / 2,
z: 0.1
},
rotationVec: { x: 0, y: 90, z: -90 },
scaleVec: { x: 1, y: 1, z: 1 },
length: southwestcurbArray[0],
width: southwestcurbArray[1],
radius: southwestcurbArray[2],
displayName: 'Southwest'
},
southeast: {
positionVec: {
x: intersectWidth / 2 - southeastcurbArray[0] / 2,
y: -intersectDepth / 2 + southeastcurbArray[1] / 2,
x: intersectWidth / 2,
y: -intersectDepth / 2,
z: 0.1
},
rotationVec: { x: 0, y: 90, z: -90 },
scaleVec: { x: -1, y: 1, z: 1 },
length: southeastcurbArray[0],
width: southeastcurbArray[1],
radius: southeastcurbArray[2],
displayName: 'Southeast'
},
northwest: {
positionVec: {
x: -intersectWidth / 2 + northwestcurbArray[0] / 2,
y: intersectDepth / 2 - northwestcurbArray[1] / 2,
x: -intersectWidth / 2,
y: intersectDepth / 2,
z: 0.1
},
rotationVec: { x: 0, y: 90, z: -90 },
scaleVec: { x: 1, y: -1, z: 1 },
length: northwestcurbArray[0],
width: northwestcurbArray[1],
radius: northwestcurbArray[2],
displayName: 'Northwest'
}
};

// create curbs if they are given
for (const [curbName, params] of Object.entries(curbParams)) {
if (data[`${curbName}curb`] !== '0 0') {
if (data[`${curbName}curb`] !== '0 0 0') {
createSidewalkElem(params);
}
}
Expand Down Expand Up @@ -401,5 +447,10 @@ AFRAME.registerComponent('intersection', {
cw4.classList.add('autocreated');
el.appendChild(cw4);
}
},
remove() {
// Remove the 3js entities
this.curbGeoms.forEach((c) => c.dispose());
this.sidewalkMaterial.dispose();
}
});
14 changes: 14 additions & 0 deletions src/editor/components/components/IntersectionSidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ const IntersectionSidebar = ({ entity }) => {
updateCurbArray(newCurbArray);
};

const handleCurbRadiusChange = (_, value) => {
const newCurbArray = [...curbArrays[curb]];
newCurbArray[2] = value;
updateCurbArray(newCurbArray);
};

const updateCurbArray = (newCurbArray) => {
switch (curb) {
case 'northeast':
Expand Down Expand Up @@ -247,6 +253,14 @@ const IntersectionSidebar = ({ entity }) => {
onChange={handleCurbHeightChange}
/>
</div>
<div className="propertyRow">
<label className="text">Radius:</label>
<NumberWidget
name="curbRadius"
value={curbArrays[curb][2]}
onChange={handleCurbRadiusChange}
/>
</div>
</div>
</div>
</div>
Expand Down

0 comments on commit ed0d5db

Please sign in to comment.