Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Curb radius option #983

Merged
merged 9 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);

kfarr marked this conversation as resolved.
Show resolved Hide resolved
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
Loading